544 lines
8.2 KiB
C
544 lines
8.2 KiB
C
|
/************/
|
||
|
/* ROUTINES */
|
||
|
/************/
|
||
|
|
||
|
#define _WINDOWS
|
||
|
#include <windows.h>
|
||
|
#include <port1632.h>
|
||
|
|
||
|
#include "main.h"
|
||
|
#include "res.h"
|
||
|
#include "rtns.h"
|
||
|
#include "grafix.h"
|
||
|
#include "pref.h"
|
||
|
#include "sound.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
|
||
|
/*** Global/Local Variables ***/
|
||
|
|
||
|
INT iPlayer;
|
||
|
|
||
|
BOOL fFlash = fFalse; /* Integerate these ! */
|
||
|
BOOL fFlashOnOff;
|
||
|
|
||
|
BOOL f4; /* TRUE if need 4 in a row */
|
||
|
|
||
|
INT cPlane; /* Number of planes */
|
||
|
INT cBlkRow; /* Number of balls in a row */
|
||
|
INT cBlkPlane; /* Number of positions in a single plane */
|
||
|
INT cBlkMac; /* Total number of available positions */
|
||
|
|
||
|
|
||
|
/* Computer stuff */
|
||
|
|
||
|
INT iBlkLose; /* Place to put if about to lose */
|
||
|
INT iMoveCurr;
|
||
|
|
||
|
BLK rgBlk[cBlkMax]; /* The main grid data */
|
||
|
BLK rgFlash[cDimMax]; /* list of balls to flash */
|
||
|
BLK rgMove[cBlkMax]; /* list of moves (for UNDO) */
|
||
|
BLK rgBest[cBlkMax]; /* count of times "best" */
|
||
|
|
||
|
#include "tbls.inc"
|
||
|
|
||
|
|
||
|
#define cChkMax k4x4x4
|
||
|
INT cChkMac;
|
||
|
|
||
|
INT rgChk[cChkMax][cDimMax];
|
||
|
|
||
|
|
||
|
/*** Global/External Variables ***/
|
||
|
|
||
|
extern STATUS status;
|
||
|
extern PREF Preferences;
|
||
|
|
||
|
extern blkCurr;
|
||
|
|
||
|
BLK ComputerMove(VOID);
|
||
|
|
||
|
|
||
|
|
||
|
/****** F C H E C K W I N ******/
|
||
|
|
||
|
/* Return TRUE if anyone won */
|
||
|
/* rgFlash contains winning set */
|
||
|
|
||
|
BOOL FCheckWin(VOID)
|
||
|
{
|
||
|
REGISTER INT x;
|
||
|
REGISTER INT y;
|
||
|
INT c;
|
||
|
|
||
|
if (f4)
|
||
|
{
|
||
|
for (y = 0; y < cChkMac; y++)
|
||
|
{
|
||
|
if (c = rgBlk[rgChk[y][0]])
|
||
|
if (c == rgBlk[rgChk[y][1]])
|
||
|
if (c == rgBlk[rgChk[y][2]])
|
||
|
if (c == rgBlk[rgChk[y][3]])
|
||
|
{
|
||
|
for (x = 0; x < cBlkRow; x++)
|
||
|
rgFlash[x] = rgChk[y][x];
|
||
|
|
||
|
return (fFlash = fTrue);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (y = 0; y < cChkMac; y++)
|
||
|
{
|
||
|
if (c = rgBlk[rgChk[y][0]])
|
||
|
if (c == rgBlk[rgChk[y][1]])
|
||
|
if (c == rgBlk[rgChk[y][2]])
|
||
|
{
|
||
|
for (x = 0; x < cBlkRow; x++)
|
||
|
rgFlash[x] = rgChk[y][x];
|
||
|
|
||
|
return (fFlash = fTrue);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return fFalse;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/****** D O M O V E ******/
|
||
|
|
||
|
VOID DoMove(BLK blk)
|
||
|
{
|
||
|
#ifdef DEBUG3
|
||
|
CHAR sz[80];
|
||
|
LMove:
|
||
|
wsprintf(sz,"Move=%d Player=%d\r\n",blk,iPlayer);
|
||
|
OutputDebugString(sz);
|
||
|
|
||
|
if (blk < 0 || blk >= cBlkMac)
|
||
|
{
|
||
|
Oops("Invalid blk");
|
||
|
return;
|
||
|
}
|
||
|
else if (rgBlk[blk])
|
||
|
{
|
||
|
Oops("Invalid Move!");
|
||
|
return;
|
||
|
}
|
||
|
#else
|
||
|
LMove:
|
||
|
#endif
|
||
|
|
||
|
rgMove[iMoveCurr++] = blk; /* Remember move */
|
||
|
PlaceBall(blk, iPlayer);
|
||
|
|
||
|
if (FCheckWin())
|
||
|
{
|
||
|
ClrStatusPlay();
|
||
|
fFlashOnOff = fTrue;
|
||
|
fFlash = fTrue;
|
||
|
PlayTune(iPlayer == iComputer ? TUNE_LOSEGAME : TUNE_WINGAME);
|
||
|
}
|
||
|
else if (iMoveCurr == cBlkMac)
|
||
|
ClrStatusPlay();
|
||
|
|
||
|
else
|
||
|
{
|
||
|
blkCurr = blk;
|
||
|
if ((iPlayer ^= 3) == iComputer)
|
||
|
{
|
||
|
blk = ComputerMove();
|
||
|
goto LMove;
|
||
|
}
|
||
|
else
|
||
|
PlayTune(TUNE_DROP); /* Computer makes noise too */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/****** U N D O M O V E ******/
|
||
|
|
||
|
VOID UnDoMove(VOID)
|
||
|
{
|
||
|
if (iMoveCurr)
|
||
|
{
|
||
|
rgBlk[rgMove[--iMoveCurr]] = iBallBlank; /* Undo Computer move */
|
||
|
if (iMoveCurr)
|
||
|
{
|
||
|
rgBlk[rgMove[--iMoveCurr]] = iBallBlank; /* Undo Human move */
|
||
|
ReDoDisplay();
|
||
|
DisplayGrid();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iPlayer = iComputer;
|
||
|
DoMove(ComputerMove());
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
MessageBeep(0); /* Should be a sound !!! */
|
||
|
}
|
||
|
|
||
|
|
||
|
/****** G E T B E S T B L K ******/
|
||
|
|
||
|
BLK GetBestBlk(VOID)
|
||
|
{
|
||
|
INT cMac;
|
||
|
BLK blkBest;
|
||
|
BLK iBlk;
|
||
|
INT cBlk;
|
||
|
|
||
|
/* Future: If f4 & we have a set of corners, try recursion */
|
||
|
|
||
|
|
||
|
/* Find most promising position */
|
||
|
|
||
|
cMac = 0;
|
||
|
cBlk = 0;
|
||
|
blkBest = 0;
|
||
|
|
||
|
for (iBlk = 0; iBlk < cBlkMac; iBlk++)
|
||
|
if (rgBest[iBlk] != 0)
|
||
|
{
|
||
|
if (rgBest[iBlk] > cMac)
|
||
|
{
|
||
|
cBlk = 1;
|
||
|
blkBest = iBlk;
|
||
|
cMac = rgBest[iBlk];
|
||
|
}
|
||
|
else if (rgBest[iBlk] == cMac)
|
||
|
{
|
||
|
if (Rnd(++cBlk) == 0)
|
||
|
blkBest = iBlk;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (blkBest != 0)
|
||
|
return blkBest;
|
||
|
|
||
|
|
||
|
switch (Preferences.iGameType)
|
||
|
{
|
||
|
case iGame3x3:
|
||
|
if (rgBlk[4] == iBallBlank)
|
||
|
return 4;
|
||
|
break;
|
||
|
|
||
|
case iGame3x3x3:
|
||
|
if (rgBlk[13] == iBallBlank)
|
||
|
return 13;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Find an empty corner */
|
||
|
|
||
|
iBlk = Rnd(8);
|
||
|
for (cMac = 0; cMac++ < 8;)
|
||
|
{
|
||
|
if (rgBlk[blkBest = rgCorner[Preferences.iGameType][iBlk]] == iBallBlank)
|
||
|
return blkBest;
|
||
|
iBlk = (iBlk+1) % 8;
|
||
|
}
|
||
|
|
||
|
return iBlkNil;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****** B L K C H K 3 ******/
|
||
|
|
||
|
BLK BlkChk3(INT b1, INT b2, INT b3)
|
||
|
{
|
||
|
#ifdef DEBUG
|
||
|
CHAR sz[80];
|
||
|
wsprintf(sz,"BlkChk3=%d %d %d v=%d\r\n",b1,b2,b3,rgBlk[b1]+rgBlk[b2]*3+rgBlk[b3]*9);
|
||
|
OutputDebugString(sz);
|
||
|
#endif
|
||
|
|
||
|
switch (rgBlk[b1] + rgBlk[b2]*3 + rgBlk[b3]*9)
|
||
|
{
|
||
|
case 4:
|
||
|
return b3;
|
||
|
case 10:
|
||
|
return b2;
|
||
|
case 12:
|
||
|
return b1;
|
||
|
|
||
|
case 8:
|
||
|
iBlkLose = b3;
|
||
|
break;
|
||
|
case 20:
|
||
|
iBlkLose = b2;
|
||
|
break;
|
||
|
case 24:
|
||
|
iBlkLose = b1;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
rgBest[b3]++;
|
||
|
break;
|
||
|
case 9:
|
||
|
rgBest[b1]++;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return iBlkNil;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****** B L K C H K 4 ******/
|
||
|
|
||
|
BLK BlkChk4(INT b1, INT b2, INT b3, INT b4)
|
||
|
{
|
||
|
switch (rgBlk[b1] + rgBlk[b2]*3 + rgBlk[b3]*9 + rgBlk[b4]*27)
|
||
|
{
|
||
|
case 13:
|
||
|
return b4;
|
||
|
case 31:
|
||
|
return b3;
|
||
|
case 37:
|
||
|
return b2;
|
||
|
case 39:
|
||
|
return b1;
|
||
|
|
||
|
case 26:
|
||
|
iBlkLose = b4;
|
||
|
break;
|
||
|
case 62:
|
||
|
iBlkLose = b3;
|
||
|
break;
|
||
|
case 74:
|
||
|
iBlkLose = b2;
|
||
|
break;
|
||
|
case 78:
|
||
|
iBlkLose = b1;
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
rgBest[b4]++;
|
||
|
break;
|
||
|
case 27:
|
||
|
rgBest[b1]++;
|
||
|
break;
|
||
|
|
||
|
case 28:
|
||
|
rgBest[b2] += 4;
|
||
|
rgBest[b3] += 4;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return iBlkNil;
|
||
|
}
|
||
|
|
||
|
|
||
|
/****** S K I L L ******/
|
||
|
|
||
|
BOOL Skill(INT intel, INT chance)
|
||
|
{
|
||
|
if (Preferences.skill > intel)
|
||
|
return fTrue;
|
||
|
|
||
|
return (Rnd(100) < chance);
|
||
|
}
|
||
|
|
||
|
|
||
|
/****** C O M P U T E R M O V E ******/
|
||
|
|
||
|
BLK ComputerMove(VOID)
|
||
|
{
|
||
|
REGISTER INT iBlk;
|
||
|
REGISTER INT y;
|
||
|
|
||
|
iBlkLose = iBlkNil;
|
||
|
|
||
|
for (iBlk = 0; iBlk < cBlkMac; iBlk++) /** USE A BLT !!! **/
|
||
|
rgBest[iBlk] = 0;
|
||
|
|
||
|
if (Skill(10, 10))
|
||
|
{
|
||
|
if (Skill(10, 50))
|
||
|
/* Search for winning location */
|
||
|
{
|
||
|
if (f4)
|
||
|
{
|
||
|
for (y = 0; y < cChkMac; y++)
|
||
|
if ((iBlk = BlkChk4(rgChk[y][0], rgChk[y][1], rgChk[y][2], rgChk[y][3])) != iBlkNil)
|
||
|
return iBlk;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (y = 0; y < cChkMac; y++)
|
||
|
if ((iBlk = BlkChk3(rgChk[y][0], rgChk[y][1], rgChk[y][2])) != iBlkNil)
|
||
|
return iBlk;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Is there a losing location ? */
|
||
|
|
||
|
if ((iBlkLose != iBlkNil) && Skill(50, 50))
|
||
|
return iBlkLose;
|
||
|
|
||
|
|
||
|
/* Is there a best spot ? */
|
||
|
|
||
|
if (Skill(70, 80) && ((iBlk = GetBestBlk()) != iBlkNil))
|
||
|
return iBlk;
|
||
|
|
||
|
}
|
||
|
|
||
|
/* Random search for a valid spot */
|
||
|
|
||
|
while (rgBlk[iBlk = Rnd(cBlkMac)] != iBallBlank)
|
||
|
;
|
||
|
|
||
|
return iBlk;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/****** D O T I M E R ******/
|
||
|
|
||
|
VOID DoTimer(VOID)
|
||
|
{
|
||
|
if (fFlash)
|
||
|
DoFlash(fFlashOnOff ^= 1);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/****** S E T U P D A T A */
|
||
|
|
||
|
VOID SetupData(VOID)
|
||
|
{
|
||
|
#ifdef DEBUG2
|
||
|
CHAR sz[80];
|
||
|
#endif
|
||
|
|
||
|
INT c,c2,c3;
|
||
|
POS pos;
|
||
|
INT iTbl = 0;
|
||
|
INT cTblMac = 0;
|
||
|
|
||
|
switch (Preferences.iGameType)
|
||
|
{
|
||
|
case iGame3x3:
|
||
|
f4 = fFalse;
|
||
|
cBlkRow = 3;
|
||
|
cPlane = 1;
|
||
|
iTbl = i3x3;
|
||
|
cTblMac = c3x3;
|
||
|
break;
|
||
|
|
||
|
case iGame3x3x3:
|
||
|
f4 = fFalse;
|
||
|
cBlkRow = 3;
|
||
|
cPlane = 3;
|
||
|
iTbl = i3x3x3;
|
||
|
cTblMac = c3x3x3;
|
||
|
break;
|
||
|
|
||
|
case iGame4x4x4:
|
||
|
f4 = fTrue;
|
||
|
cBlkRow = 4;
|
||
|
cPlane = 4;
|
||
|
iTbl = i4x4x4;
|
||
|
cTblMac = c4x4x4;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
cBlkPlane = cBlkRow * cBlkRow;
|
||
|
cBlkMac = cBlkPlane * cPlane;
|
||
|
|
||
|
#ifdef DEBUG3
|
||
|
wsprintf(sz,"Game=%d cBlkRow=%d cPlane=%d\r\n",Preferences.iGameType, cBlkRow, cPlane);
|
||
|
OutputDebugString(sz);
|
||
|
wsprintf(sz,"cBlkPlane=%d cBlkMac=%d cTblMac=%d\r\n",cBlkPlane, cBlkMac, cTblMac);
|
||
|
OutputDebugString(sz);
|
||
|
#endif
|
||
|
|
||
|
cChkMac = 0;
|
||
|
|
||
|
c3 = 0;
|
||
|
while (c3++ < cTblMac)
|
||
|
{
|
||
|
pos = rgtbl[iTbl][iposStart];
|
||
|
c2 = 0;
|
||
|
while (c2 < cBlkRow)
|
||
|
{
|
||
|
for (c=0; c < cBlkRow; c++)
|
||
|
{
|
||
|
rgChk[cChkMac][c] = pos;
|
||
|
#ifdef DEBUG2
|
||
|
wsprintf(sz," %d",pos);
|
||
|
OutputDebugString(sz);
|
||
|
#endif
|
||
|
pos = (pos + rgtbl[iTbl][iposOff]) % cBlkMac;
|
||
|
}
|
||
|
cChkMac++;
|
||
|
c2++;
|
||
|
if (rgtbl[iTbl][iposLine] == (BYTE) -1)
|
||
|
c2 = cBlkRow;
|
||
|
else
|
||
|
pos = (pos + rgtbl[iTbl][iposLine] - rgtbl[iTbl][iposOff]) % cBlkMac;
|
||
|
#ifdef DEBUG2
|
||
|
OutputDebugString("\r\n");
|
||
|
#endif
|
||
|
}
|
||
|
iTbl++;
|
||
|
#ifdef DEBUG2
|
||
|
OutputDebugString("------\r\n");
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DEBUG2
|
||
|
wsprintf(sz,"cChkMac=%d",cChkMac);
|
||
|
OutputDebugString(sz);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
/****** S T A R T G A M E *******/
|
||
|
|
||
|
VOID StartGame(VOID)
|
||
|
{
|
||
|
REGISTER i;
|
||
|
|
||
|
fFlash = fFalse;
|
||
|
|
||
|
ClrStatusIcon();
|
||
|
SetStatusPlay();
|
||
|
iMoveCurr = 0;
|
||
|
|
||
|
#ifdef DEBUG3
|
||
|
OutputDebugString("*** New Game ***\r\n");
|
||
|
#endif
|
||
|
|
||
|
for (i = 0; i < cBlkMac; i++) /* USE A BLT ! */
|
||
|
rgBlk[i] = iBallBlank;
|
||
|
|
||
|
ReDoDisplay();
|
||
|
DisplayGrid();
|
||
|
|
||
|
if (Preferences.iStart == iStartRnd)
|
||
|
iPlayer = 1 + Rnd(2);
|
||
|
else
|
||
|
iPlayer = Preferences.iStart;
|
||
|
|
||
|
if (iPlayer == iComputer)
|
||
|
{
|
||
|
iPlayer = iComputer;
|
||
|
DoMove(ComputerMove());
|
||
|
}
|
||
|
}
|
||
|
|