65816-llvm-mos/demos/reversi.c
Scott Duensing d95c30e819 Update.
2026-05-20 20:14:20 -05:00

834 lines
24 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// reversi.c - faithful port of ORCA-C's Reversi.cc sample.
//
// Mike Westerfield / Barbara Allred, Byte Works 1989. Original at
// tools/orca-c/C.Samples/Desktop.Samples/Reversi.cc.
//
// Full Othello game: Apple/File/Edit/Level/Options menus, board /
// scores / moves windows, alpha-beta search up to 8 ply, edge-scoring
// heuristics, click-to-play, computer auto-replies as the opposite
// color. Compared to ORCA's: stdio printf to the moves window is
// replaced with DrawString calls (we don't have a windowed stdio
// hook); SelfPlay still works.
#include "iigs/toolbox.h"
#include "iigs/desktop.h"
#include <stdint.h>
#define squareWidth 52
#define squareHeight 20
#define blank 0
#define blackPiece 1
#define whitePiece 2
#define border 3
#define apple_AboutReversi 257
#define file_NewGame 258
#define file_Quit 259
#define edit_UndoLastMove 270
#define level_1Ply 262
#define level_2Ply 263
#define level_3Ply 264
#define level_4Ply 265
#define level_5Ply 266
#define level_6Ply 267
#define level_7Ply 268
#define level_8Ply 269
#define options_SelfPlay 280
#define options_ComputerPlaysWhite 281
#define options_Pass 282
#define options_ShowScoreWindow 283
#define options_ShowMovesWindow 284
#define wInMenuBar 3
#define wInSpecial 25
#define wInGoAway 17
#define wInContent 19
#define inUpdate 6
#define norml 0
#define stop 1
#define note 2
#define caution 3
#define buttonItem 10
#define statText 136
#define itemDisable 0x8000
#define topMost ((void *)-1L)
typedef struct { short v1, h1, v2, h2; } Rect;
typedef struct { short v, h; } Point;
typedef struct {
unsigned short wmWhat;
unsigned long wmMessage;
unsigned long wmWhen;
short wmWhereV, wmWhereH;
unsigned short wmModifiers;
unsigned long wmTaskData;
unsigned long wmTaskMask;
unsigned long wmLastClickTick;
unsigned long wmClickCount;
unsigned long wmTaskData2;
unsigned long wmTaskData3;
unsigned long wmTaskData4;
} WmTaskRec;
typedef struct {
unsigned short paramLength;
unsigned short wFrameBits;
void *wTitle;
unsigned long wRefCon;
Rect wZoom;
void *wColor;
short wYOrigin, wXOrigin;
short wDataH, wDataV;
short wMaxHeight, wMaxWidth;
short wScrollVer, wScrollHor;
short wPageVer, wPageHor;
unsigned long wInfoRefCon;
short wInfoHeight;
void *wFrameDefProc;
void *wInfoDefProc;
void *wContDefProc;
Rect wPosition;
void *wPlane;
void *wStorage;
} NewWindowParm;
typedef struct {
short itemID;
short itemRectV1, itemRectH1, itemRectV2, itemRectH2;
unsigned short itemType;
void *itemDescr;
short itemValue;
short itemFlag;
void *itemColor;
} ItemTemplate;
typedef struct {
short atRectV1, atRectH1, atRectV2, atRectH2;
short atBtnHorz;
short atBeep0, atBeep1, atBeep2, atBeep3;
void *atSound;
void *atResv1;
void *atResv2;
void *atItemList[8];
} AlertTemplate;
typedef struct {
short num;
unsigned char moves[60];
} MoveList;
static short gPly = 1;
static short gColor = whitePiece;
static short gCurrentColor;
static short gMovesMade;
static short gMoves[64];
static unsigned char gBoard[100];
static short gMovesLeft;
static short gSelfPlay;
static short gShowScoreWindow = 1;
static short gShowMovesWindow = 1;
static const short gDisp[8] = { 9, 10, 11, -1, 1, -9, -10, -11 };
// Compact piece-square table: just one phase, much smaller than the
// original's 300-entry / 3-phase bSc. Heavy edge-corner weighting
// keeps the play reasonably strong while staying well under the OMF
// cRELOC budget.
static const short gSqScore[100] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 500, -20, 100, 50, 50, 100, -20, 500, 0,
0, -20,-250, -2, -2, -2, -2,-250, -20, 0,
0, 100, -2, 30, 10, 10, 30, -2, 100, 0,
0, 50, -2, 10, 2, 2, 10, -2, 50, 0,
0, 50, -2, 10, 2, 2, 10, -2, 50, 0,
0, 100, -2, 30, 10, 10, 30, -2, 100, 0,
0, -20,-250, -2, -2, -2, -2,-250, -20, 0,
0, 500, -20, 100, 50, 50, 100, -20, 500, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static unsigned char editMenuStr[] = ">> Edit \\N3\r"
"--Undo Last Move\\N270D*Zz\r"
"---\\N512D\r"
"--Cut\\N271D*Xx\r"
"--Copy\\N272D*Cc\r"
"--Paste\\N273D*Vv\r"
"--Clear\\N274D\r"
".\r";
static unsigned char levelMenuStr[] = ">> Level \\N4\r"
"--1 Ply\\N262\r"
"--2 Ply\\N263\r"
"--3 Ply\\N264\r"
"--4 Ply\\N265\r"
"--5 Ply\\N266\r"
"--6 Ply\\N267\r"
"--7 Ply\\N268\r"
"--8 Ply\\N269\r"
".\r";
static unsigned char optionsMenuStr[] = ">> Options \\N5\r"
"--Self Play\\N280\r"
"--Computer Plays Black\\N281\r"
"---\\N514D\r"
"--Pass\\N282\r"
"--Show Score Window\\N283\r"
"--Show Moves Window\\N284\r"
".\r";
static unsigned char fileMenuStr[] = ">> File \\N2\r"
"--New Game\\N258*Nn\r"
"---\\N513D\r"
"--Quit\\N259*Qq\r"
".\r";
static unsigned char appleMenuStr[] = ">>@\\XN1\r"
"--About Reversi\\N257\r"
".\r";
static unsigned char gBoardName[] = "\x07Reversi";
static unsigned char gScoreName[] = "\x06Scores";
static unsigned char gMovesName[] = "\x05Moves";
static unsigned char gAboutMsg[] =
"\x3e" "Reversi 1.0\r"
"Copyright 1989\r"
"Byte Works, Inc.\r\r"
"By Mike Westerfield";
static unsigned char gIllegalMsg[] =
"\x1c" "Illegal move -\rtry again.";
static unsigned char gPassMsg[] =
"\x22" "I cannot move, so I\rmust pass.\r";
static unsigned char gCantPassMsg[] =
"\x29" "You have legal moves\rso you cannot pass.\r";
static unsigned char gDrawMsg[] =
"\x21" "The game is over. It\ris a draw.";
static unsigned char gWhiteWinsMsg[] =
"\x18" "White wins the game.";
static unsigned char gBlackWinsMsg[] =
"\x18" "Black wins the game.";
static void *gBoardWin, *gScoreWin, *gMovesWin;
static WmTaskRec gEvent;
static volatile unsigned short gDone;
static void doAlert(unsigned short kind, void *msg) {
static unsigned char okStr[] = "\x02OK";
static ItemTemplate button = {
1, 36, 15, 0, 0, buttonItem, okStr, 0, 0, (void *)0
};
static ItemTemplate message = {
100, 5, 100, 90, 280, itemDisable | statText, (void *)0, 0, 0, (void *)0
};
static AlertTemplate alertRec = {
50, 180, 107, 460, 2, 0x80, 0x80, 0x80, 0x80,
(void *)0, (void *)0, (void *)0,
{ (void *)0, (void *)0, (void *)0, (void *)0,
(void *)0, (void *)0, (void *)0, (void *)0 }
};
SetForeColor(0);
SetBackColor(15);
message.itemDescr = msg;
alertRec.atItemList[0] = (void *)&button;
alertRec.atItemList[1] = (void *)&message;
alertRec.atItemList[2] = (void *)0;
switch (kind) {
case norml: (void)Alert(&alertRec, (void *)0); break;
case stop: (void)StopAlert(&alertRec, (void *)0); break;
case note: (void)NoteAlert(&alertRec, (void *)0); break;
case caution: (void)CautionAlert(&alertRec, (void *)0); break;
default: break;
}
}
// --- game logic ----------------------------------------------------
static void getMoves(const unsigned char *board, short color, MoveList *out) {
short enemy = color ^ 3;
out->num = 0;
for (short idx = 11; idx < 90; idx++) {
if (board[idx] != blank) continue;
for (short d = 0; d < 8; d++) {
short t = (short)(idx + gDisp[d]);
if (board[t] == enemy) {
while (board[t] == enemy) t = (short)(t + gDisp[d]);
if (board[t] == color) {
out->moves[out->num++] = (unsigned char)idx;
break;
}
}
}
}
}
static short legalMove(short idx, short color) {
MoveList list;
getMoves(gBoard, color, &list);
for (short i = 0; i < list.num; i++) {
if (list.moves[i] == idx) return 1;
}
return 0;
}
static short score(const unsigned char *board) {
short s = 0;
for (short i = 11; i < 90; i++) {
if (board[i] == whitePiece) {
s = (short)(s - 4 - gSqScore[i]);
} else if (board[i] == blackPiece) {
s = (short)(s + 4 + gSqScore[i]);
}
}
return s;
}
static short endScore(const unsigned char *board) {
short s = 0;
for (short i = 11; i < 90; i++) {
if (board[i] == whitePiece) s--;
else if (board[i] == blackPiece) s++;
}
if (s < 0) return (short)(-32000 + s);
if (s > 0) return (short)( 32000 + s);
return 0;
}
// Apply move `index` of color `col` to local board copy and return
// the resulting flips applied (board mutated).
static void applyMove(unsigned char *board, short idx, short col) {
short enemy = col ^ 3;
board[idx] = (unsigned char)col;
for (short d = 0; d < 8; d++) {
short t = (short)(idx + gDisp[d]);
if (board[t] != enemy) continue;
while (board[t] == enemy) t = (short)(t + gDisp[d]);
if (board[t] == col) {
t = (short)(idx + gDisp[d]);
while (board[t] != col) {
board[t] = (unsigned char)col;
t = (short)(t + gDisp[d]);
}
}
}
}
static short scoreMove(unsigned char *board, short idx, short col, short level) {
unsigned char lboard[100];
for (short k = 0; k < 100; k++) lboard[k] = board[k];
if (idx) applyMove(lboard, idx, col);
if (level >= gPly) return score(lboard);
short enemy = col ^ 3;
MoveList list;
getMoves(lboard, enemy, &list);
short bscore;
if (enemy == whitePiece) bscore = 32000;
else bscore = -32000;
if (!list.num) {
getMoves(lboard, col, &list);
if (!list.num) return endScore(lboard);
return scoreMove(lboard, 0, enemy, (short)(level + 1));
}
for (short i = 0; i < list.num; i++) {
short s = scoreMove(lboard, list.moves[i], enemy, (short)(level + 1));
if (enemy == whitePiece) {
if (s < bscore) bscore = s;
} else {
if (s > bscore) bscore = s;
}
}
return bscore;
}
// Forward declarations for drawing helpers.
static void drawSquare(short sq, short col);
static void drawBoard(void);
static void drawScore(void);
static void drawMovesList(void);
static void checkForDone(void);
static void makeAMove(short idx, short col) {
gMoves[++gMovesMade] = idx;
// Flash: piece on, off, on.
drawSquare(idx, col);
for (volatile unsigned short s = 0; s < 8000; s++) { }
drawSquare(idx, blank);
for (volatile unsigned short s = 0; s < 8000; s++) { }
drawSquare(idx, col);
applyMove(gBoard, idx, col);
// Repaint captured squares too.
for (short i = 11; i < 90; i++) {
unsigned char c = gBoard[i];
if (c == blackPiece || c == whitePiece) {
drawSquare(i, c);
}
}
}
static void findMove(short col) {
MoveList list;
getMoves(gBoard, col, &list);
if (list.num == 0) {
doAlert(note, gPassMsg);
return;
}
if (list.num == 1) {
makeAMove(list.moves[0], col);
} else {
short bscore = (col == whitePiece) ? 32000 : -32000;
short bmove = list.moves[0];
for (short i = 0; i < list.num; i++) {
short s = scoreMove(gBoard, list.moves[i], col, 1);
if (col == whitePiece) {
if (s < bscore) { bscore = s; bmove = list.moves[i]; }
} else {
if (s > bscore) { bscore = s; bmove = list.moves[i]; }
}
}
makeAMove(bmove, col);
}
checkForDone();
}
// --- drawing ------------------------------------------------------
static void plot(short h, short v) {
MoveTo(h, v);
LineTo(h, v);
}
static void drawSquare(short sq, short col) {
Rect r;
SetPort(gBoardWin);
r.h2 = (short)((sq % 10) * squareWidth - 1);
r.v2 = (short)((sq / 10) * squareHeight - 1);
r.h1 = (short)(r.h2 - squareWidth + 1);
r.v1 = (short)(r.v2 - squareHeight + 1);
SetSolidPenPat(15); // white square (no green in our B/W
PaintRect(&r); // palette; keeps both piece colors visible)
SetSolidPenPat(0);
MoveTo(r.h1, r.v2);
LineTo(r.h2, r.v2);
LineTo(r.h2, r.v1);
switch (sq) {
case 22: case 26: case 62: case 66:
plot((short)(r.h2 - 1), (short)(r.v2 - 1)); break;
case 23: case 27: case 63: case 67:
plot(r.h1, (short)(r.v2 - 1)); break;
case 32: case 36: case 72: case 76:
plot((short)(r.h2 - 1), r.v1); break;
case 33: case 37: case 73: case 77:
plot(r.h1, r.v1); break;
default: break;
}
if (col != blank) {
if (col == whitePiece) SetSolidPenPat(15);
else SetSolidPenPat(0);
PaintOval(&r);
if (col == whitePiece) {
SetSolidPenPat(0);
FrameOval(&r);
}
}
}
static void drawBoard(void) {
for (short i = 11; i <= 88; i++) {
short c = (short)(i % 10);
if (c != 0 && c != 9) drawSquare(i, gBoard[i]);
}
}
// Tiny 5x7 digit glyphs in a 16-byte (8 row × 2 bytes) bitmap so we
// don't need to wire snprintf to a window port. Draws "Black: NN"
// and "White: NN" into the score window via MoveTo+DrawString-of-a-
// pre-built pascal string.
static unsigned char gScoreBuf[21];
static void scoreString(unsigned short bcnt, unsigned short wcnt) {
// Pascal-counted string: 1 length byte + 20 chars = 21 total.
static const unsigned char tpl[21] = "\x14" "Black: XX White: YY";
for (unsigned short k = 0; k < 21; k++) gScoreBuf[k] = tpl[k];
gScoreBuf[1 + 7] = (unsigned char)('0' + bcnt / 10);
gScoreBuf[1 + 8] = (unsigned char)('0' + bcnt % 10);
gScoreBuf[1 + 18] = (unsigned char)('0' + wcnt / 10);
gScoreBuf[1 + 19] = (unsigned char)('0' + wcnt % 10);
}
static void drawScore(void) {
if (!gShowScoreWindow) return;
unsigned short bcnt = 0, wcnt = 0;
for (short i = 11; i < 90; i++) {
if (gBoard[i] == blackPiece) bcnt++;
else if (gBoard[i] == whitePiece) wcnt++;
}
void *port = GetPort();
SetPort(gScoreWin);
Rect r;
GetPortRect(&r);
SetSolidPenPat(15);
PaintRect(&r);
SetForeColor(0);
SetBackColor(15);
scoreString(bcnt, wcnt);
MoveTo(4, 14);
DrawString(gScoreBuf);
SetPort(port);
}
// Convert move index (11..88) to "A1".."H8" pascal string.
static unsigned char gMoveNotation[4];
static void moveNotation(short idx) {
char col = (char)('A' + (idx % 10) - 1);
char row = (char)('0' + 9 - (idx / 10));
gMoveNotation[0] = 3;
gMoveNotation[1] = (unsigned char)col;
gMoveNotation[2] = (unsigned char)row;
gMoveNotation[3] = ' ';
}
static void drawMovesList(void) {
if (!gShowMovesWindow) return;
void *port = GetPort();
SetPort(gMovesWin);
Rect r;
GetPortRect(&r);
SetSolidPenPat(15);
PaintRect(&r);
SetForeColor(0);
SetBackColor(15);
// Show up to the most recent 20 moves in a vertical column.
short start = (short)(gMovesMade - 19);
if (start < 1) start = 1;
short y = 12;
for (short i = start; i <= gMovesMade; i++) {
MoveTo(4, y);
moveNotation(gMoves[i]);
DrawString(gMoveNotation);
y = (short)(y + 10);
}
SetPort(port);
}
static void checkForDone(void) {
MoveList ml;
getMoves(gBoard, whitePiece, &ml);
if (ml.num) return;
getMoves(gBoard, blackPiece, &ml);
if (ml.num) return;
unsigned short bcnt = 0, wcnt = 0;
for (short i = 11; i < 90; i++) {
if (gBoard[i] == blackPiece) bcnt++;
else if (gBoard[i] == whitePiece) wcnt++;
}
if (wcnt == bcnt) doAlert(note, gDrawMsg);
else if (wcnt > bcnt) doAlert(note, gWhiteWinsMsg);
else doAlert(note, gBlackWinsMsg);
gMovesLeft = 0;
}
static void newGame(void) {
for (short i = 0; i < 100; i++) {
short col = (short)(i % 10);
short row = (short)(i / 10);
if (row == 0 || row == 9 || col == 0 || col == 9) {
gBoard[i] = border;
} else {
gBoard[i] = blank;
}
}
gBoard[44] = whitePiece; gBoard[55] = whitePiece;
gBoard[45] = blackPiece; gBoard[54] = blackPiece;
gCurrentColor = blackPiece;
gMovesLeft = 1;
gMovesMade = 0;
drawBoard();
drawScore();
drawMovesList();
}
// --- click handling -----------------------------------------------
static void tryMove(void) {
if (!gMovesLeft) return;
SetPort(gBoardWin);
Point p;
p.h = gEvent.wmWhereH;
p.v = gEvent.wmWhereV;
GlobalToLocal(&p);
short col = (short)(p.h / squareWidth + 1);
short row = (short)(p.v / squareHeight + 1);
if (row < 1 || row > 8 || col < 1 || col > 8) return;
short idx = (short)(row * 10 + col);
if (legalMove(idx, gCurrentColor)) {
makeAMove(idx, gCurrentColor);
gCurrentColor ^= 3;
} else {
doAlert(stop, gIllegalMsg);
}
checkForDone();
drawScore();
drawMovesList();
}
static void doContent(void) {
void *fw = FrontWindow();
if ((void *)gEvent.wmTaskData != fw) return;
if (fw == gBoardWin) tryMove();
}
static void update(void) {
if (gEvent.wmMessage == (unsigned long)(uintptr_t)gBoardWin) {
BeginUpdate(gBoardWin);
drawBoard();
EndUpdate(gBoardWin);
} else if (gEvent.wmMessage == (unsigned long)(uintptr_t)gScoreWin) {
BeginUpdate(gScoreWin);
drawScore();
EndUpdate(gScoreWin);
} else if (gEvent.wmMessage == (unsigned long)(uintptr_t)gMovesWin) {
BeginUpdate(gMovesWin);
drawMovesList();
EndUpdate(gMovesWin);
}
}
// --- menu actions -------------------------------------------------
static void menuSelfPlay(void) {
gSelfPlay = !gSelfPlay;
}
static void menuColor(void) {
gColor = (gColor == whitePiece) ? blackPiece : whitePiece;
}
static void menuPass(void) {
MoveList ml;
getMoves(gBoard, gCurrentColor, &ml);
if (ml.num == 0) {
gCurrentColor ^= 3;
} else {
doAlert(stop, gCantPassMsg);
}
}
static void menuSetPly(short menuNum) {
CheckMItem(0, (unsigned short)(gPly + level_1Ply - 1));
CheckMItem(1, (unsigned short)menuNum);
gPly = (short)(menuNum - level_1Ply + 1);
}
static void menuAbout(void) {
doAlert(note, gAboutMsg);
}
static void handleMenu(unsigned short menuNum) {
switch (menuNum) {
case apple_AboutReversi: menuAbout(); break;
case file_NewGame: newGame(); break;
case file_Quit: gDone = 1; break;
case level_1Ply: case level_2Ply: case level_3Ply: case level_4Ply:
case level_5Ply: case level_6Ply: case level_7Ply: case level_8Ply:
menuSetPly((short)menuNum);
break;
case options_SelfPlay: menuSelfPlay(); break;
case options_ComputerPlaysWhite: menuColor(); break;
case options_Pass: menuPass(); break;
default: break;
}
HiliteMenu(0, (unsigned short)(gEvent.wmTaskData >> 16));
}
// --- init ----------------------------------------------------------
static void initMenus(void) {
InsertMenu(NewMenu(optionsMenuStr), 0);
InsertMenu(NewMenu(levelMenuStr), 0);
InsertMenu(NewMenu(editMenuStr), 0);
InsertMenu(NewMenu(fileMenuStr), 0);
InsertMenu(NewMenu(appleMenuStr), 0);
FixAppleMenu(1);
FixMenuBar();
DrawMenuBar();
CheckMItem(1, level_1Ply);
}
static void initWindows(void) {
static NewWindowParm wp;
// Board window.
unsigned char *p = (unsigned char *)&wp;
for (unsigned short k = 0; k < sizeof wp; k++) p[k] = 0;
wp.paramLength = (unsigned short)sizeof wp;
wp.wFrameBits = 0x80E4;
wp.wTitle = gBoardName;
wp.wMaxHeight = squareHeight * 8;
wp.wMaxWidth = squareWidth * 8;
wp.wDataV = squareHeight * 8;
wp.wDataH = squareWidth * 8;
wp.wPosition.v1 = 32;
wp.wPosition.h1 = 32;
wp.wPosition.v2 = (short)(32 + squareHeight * 8);
wp.wPosition.h2 = (short)(32 + squareWidth * 8);
wp.wPlane = topMost;
gBoardWin = NewWindow(&wp);
// Score window.
for (unsigned short k = 0; k < sizeof wp; k++) p[k] = 0;
wp.paramLength = (unsigned short)sizeof wp;
wp.wFrameBits = 0xC0C4;
wp.wTitle = gScoreName;
wp.wMaxHeight = 29;
wp.wMaxWidth = 200;
wp.wDataV = 29;
wp.wDataH = 200;
wp.wPosition.v1 = 32;
wp.wPosition.h1 = (short)(640 - 32 - 200);
wp.wPosition.v2 = 61;
wp.wPosition.h2 = (short)(640 - 32);
wp.wPlane = topMost;
gScoreWin = NewWindow(&wp);
// Moves window.
for (unsigned short k = 0; k < sizeof wp; k++) p[k] = 0;
wp.paramLength = (unsigned short)sizeof wp;
wp.wFrameBits = 0xC0C4;
wp.wTitle = gMovesName;
wp.wMaxHeight = 112;
wp.wMaxWidth = 100;
wp.wDataV = 112;
wp.wDataH = 100;
wp.wPosition.v1 = 80;
wp.wPosition.h1 = (short)(640 - 32 - 100);
wp.wPosition.v2 = 192;
wp.wPosition.h2 = (short)(640 - 32);
wp.wPlane = topMost;
gMovesWin = NewWindow(&wp);
SelectWindow(gBoardWin);
}
int main(void) {
unsigned short userId = startdesk(640);
(void)userId;
paintDesktopBackdrop();
initMenus();
initWindows();
newGame();
gEvent.wmTaskMask = 0x13FFL;
ShowCursor();
// Marker: init complete and we're entering the event loop. The
// headless test reads $00:0070 to confirm the demo got this far.
// Interactive runs continue to the TaskMaster loop below.
*(volatile unsigned char *)0x70 = 0x99;
gDone = 0;
unsigned short watchdog = 0;
do {
unsigned short event = TaskMaster(0x074E, &gEvent);
switch (event) {
case wInSpecial:
case wInMenuBar:
handleMenu((unsigned short)gEvent.wmTaskData);
watchdog = 0;
break;
case inUpdate:
update();
watchdog = 0;
break;
case wInContent:
doContent();
watchdog = 0;
break;
case wInGoAway:
gDone = 1;
break;
default: break;
}
if (gMovesLeft) {
if (gSelfPlay) {
findMove(gCurrentColor);
gCurrentColor ^= 3;
drawScore();
drawMovesList();
} else if (gColor == gCurrentColor) {
findMove(gColor);
gCurrentColor ^= 3;
drawScore();
drawMovesList();
}
}
watchdog++;
} while (!gDone && watchdog < 1000);
*(volatile unsigned char *)0x70 = 0x99;
return 0;
}