863 lines
22 KiB
C
863 lines
22 KiB
C
// 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.
|
|
//
|
|
// Phase 4.1 migration: menu mini-format strings, AlertTemplate,
|
|
// NewWindowParm boilerplate folded into iigs/uiBuilder.h.
|
|
|
|
#include "iigs/toolbox.h"
|
|
#include "iigs/desktop.h"
|
|
#include "iigs/uiBuilder.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
|
|
|
|
|
|
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 {
|
|
short num;
|
|
unsigned char moves[60];
|
|
} MoveList;
|
|
|
|
|
|
// --- alphabetised forward decls -----------------------------------
|
|
static void checkForDone(void);
|
|
static void doContent(void);
|
|
static short endScore(const unsigned char *board);
|
|
static void findMove(short col);
|
|
static void getMoves(const unsigned char *board, short color, MoveList *out);
|
|
static short legalMove(short idx, short color);
|
|
static void makeAMove(short idx, short col);
|
|
static void menuAbout(void);
|
|
static void menuColor(void);
|
|
static void menuPass(void);
|
|
static void menuSelfPlay(void);
|
|
static void menuSetPly(short menuNum);
|
|
static void newGame(void);
|
|
static void onAbout(uint16_t cmdId);
|
|
static void onMenuPick(uint16_t menuId, uint16_t itemId);
|
|
static void onNewGame(uint16_t cmdId);
|
|
static void onPass(uint16_t cmdId);
|
|
static void onPlyN(uint16_t cmdId);
|
|
static void onQuit(uint16_t cmdId);
|
|
static void onSelfPlay(uint16_t cmdId);
|
|
static void onTogglePlayer(uint16_t cmdId);
|
|
static void scoreString(unsigned short bcnt, unsigned short wcnt);
|
|
static short score(const unsigned char *board);
|
|
static short scoreMove(unsigned char *board, short idx, short col, short level);
|
|
static void drawBoard(void);
|
|
static void drawMovesList(void);
|
|
static void drawScore(void);
|
|
static void drawSquare(short sq, short col);
|
|
static void initWindows(void);
|
|
static void moveNotation(short idx);
|
|
static void plot(short h, short v);
|
|
static void tryMove(void);
|
|
static void update(void);
|
|
static void handleMenuLegacy(unsigned short menuNum);
|
|
|
|
|
|
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.
|
|
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
|
|
};
|
|
|
|
|
|
// --- menu spec via uiBuilder --------------------------------------
|
|
static const UiMenuItemT gEditItems[] = {
|
|
{ edit_UndoLastMove, "Undo Last Move", 'Z', MI_DISABLED },
|
|
{ 512, (const char *)0, 0, MI_DIVIDER | MI_DISABLED },
|
|
{ 271, "Cut", 'X', MI_DISABLED },
|
|
{ 272, "Copy", 'C', MI_DISABLED },
|
|
{ 273, "Paste", 'V', MI_DISABLED },
|
|
{ 274, "Clear", 0, MI_DISABLED },
|
|
};
|
|
|
|
static const UiMenuItemT gLevelItems[] = {
|
|
{ level_1Ply, "1 Ply", 0, 0 },
|
|
{ level_2Ply, "2 Ply", 0, 0 },
|
|
{ level_3Ply, "3 Ply", 0, 0 },
|
|
{ level_4Ply, "4 Ply", 0, 0 },
|
|
{ level_5Ply, "5 Ply", 0, 0 },
|
|
{ level_6Ply, "6 Ply", 0, 0 },
|
|
{ level_7Ply, "7 Ply", 0, 0 },
|
|
{ level_8Ply, "8 Ply", 0, 0 },
|
|
};
|
|
|
|
static const UiMenuItemT gOptionsItems[] = {
|
|
{ options_SelfPlay, "Self Play", 0, 0 },
|
|
{ options_ComputerPlaysWhite, "Computer Plays Black", 0, 0 },
|
|
{ 514, (const char *)0, 0, MI_DIVIDER | MI_DISABLED },
|
|
{ options_Pass, "Pass", 0, 0 },
|
|
{ options_ShowScoreWindow, "Show Score Window", 0, 0 },
|
|
{ options_ShowMovesWindow, "Show Moves Window", 0, 0 },
|
|
};
|
|
|
|
static const UiMenuItemT gFileItems[] = {
|
|
{ file_NewGame, "New Game", 'N', 0 },
|
|
{ 513, (const char *)0, 0, MI_DIVIDER | MI_DISABLED },
|
|
{ file_Quit, "Quit", 'Q', 0 },
|
|
};
|
|
|
|
static const UiMenuItemT gAppleItems[] = {
|
|
{ apple_AboutReversi, "About Reversi", 0, 0 },
|
|
};
|
|
|
|
static const UiMenuT gMenus[] = {
|
|
{ 1, "Apple", MN_APPLE, 1, gAppleItems },
|
|
{ 2, " File", 0, 3, gFileItems },
|
|
{ 3, " Edit", 0, 6, gEditItems },
|
|
{ 4, " Level", 0, 8, gLevelItems },
|
|
{ 5, " Options", 0, 6, gOptionsItems },
|
|
};
|
|
|
|
|
|
static const UiCmdHandlerT gCmdTable[] = {
|
|
{ apple_AboutReversi, onAbout },
|
|
{ file_NewGame, onNewGame },
|
|
{ file_Quit, onQuit },
|
|
{ level_1Ply, onPlyN },
|
|
{ level_2Ply, onPlyN },
|
|
{ level_3Ply, onPlyN },
|
|
{ level_4Ply, onPlyN },
|
|
{ level_5Ply, onPlyN },
|
|
{ level_6Ply, onPlyN },
|
|
{ level_7Ply, onPlyN },
|
|
{ level_8Ply, onPlyN },
|
|
{ options_SelfPlay, onSelfPlay },
|
|
{ options_ComputerPlaysWhite, onTogglePlayer },
|
|
{ options_Pass, onPass },
|
|
};
|
|
|
|
|
|
static void *gBoardWin, *gScoreWin, *gMovesWin;
|
|
static WmTaskRec gEvent;
|
|
static volatile unsigned short gDone;
|
|
|
|
|
|
// --- 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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static void makeAMove(short idx, short col) {
|
|
gMoves[++gMovesMade] = idx;
|
|
|
|
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);
|
|
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) {
|
|
uiBuilderAlert(UA_NOTE, "I cannot move, so I\rmust pass.\r");
|
|
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);
|
|
PaintRect(&r);
|
|
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]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static unsigned char gScoreBuf[21];
|
|
|
|
|
|
static void scoreString(unsigned short bcnt, unsigned short wcnt) {
|
|
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;
|
|
unsigned short 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);
|
|
}
|
|
|
|
|
|
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);
|
|
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;
|
|
unsigned short wcnt = 0;
|
|
for (short i = 11; i < 90; i++) {
|
|
if (gBoard[i] == blackPiece) {
|
|
bcnt++;
|
|
} else if (gBoard[i] == whitePiece) {
|
|
wcnt++;
|
|
}
|
|
}
|
|
if (wcnt == bcnt) {
|
|
uiBuilderAlert(UA_NOTE, "The game is over. It\ris a draw.");
|
|
} else if (wcnt > bcnt) {
|
|
uiBuilderAlert(UA_NOTE, "White wins the game.");
|
|
} else {
|
|
uiBuilderAlert(UA_NOTE, "Black wins the game.");
|
|
}
|
|
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 {
|
|
uiBuilderAlert(UA_STOP, "Illegal move -\rtry again.");
|
|
}
|
|
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 {
|
|
uiBuilderAlert(UA_STOP, "You have legal moves\rso you cannot pass.\r");
|
|
}
|
|
}
|
|
|
|
|
|
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) {
|
|
uiBuilderAlert(UA_NOTE,
|
|
"Reversi 1.0\r"
|
|
"Copyright 1989\r"
|
|
"Byte Works, Inc.\r\r"
|
|
"By Mike Westerfield");
|
|
}
|
|
|
|
|
|
static void onAbout(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
menuAbout();
|
|
}
|
|
|
|
|
|
static void onNewGame(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
newGame();
|
|
}
|
|
|
|
|
|
static void onQuit(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
gDone = 1;
|
|
}
|
|
|
|
|
|
static void onPlyN(uint16_t cmdId) {
|
|
menuSetPly((short)cmdId);
|
|
}
|
|
|
|
|
|
static void onSelfPlay(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
menuSelfPlay();
|
|
}
|
|
|
|
|
|
static void onTogglePlayer(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
menuColor();
|
|
}
|
|
|
|
|
|
static void onPass(uint16_t cmdId) {
|
|
(void)cmdId;
|
|
menuPass();
|
|
}
|
|
|
|
|
|
static void onMenuPick(uint16_t menuId, uint16_t itemId) {
|
|
(void)menuId;
|
|
uiBuilderDispatch(itemId, gCmdTable, (uint16_t)(sizeof gCmdTable / sizeof gCmdTable[0]));
|
|
HiliteMenu(0, (unsigned short)(gEvent.wmTaskData >> 16));
|
|
}
|
|
|
|
|
|
static void handleMenuLegacy(unsigned short menuNum) {
|
|
onMenuPick(0, (uint16_t)menuNum);
|
|
}
|
|
|
|
|
|
// --- init ----------------------------------------------------------
|
|
|
|
static void initWindows(void) {
|
|
UiWindowT spec;
|
|
|
|
// Board window: 0x80E4 = fTitle | fVis | fMove | fInfo + fPage
|
|
spec.title = "Reversi";
|
|
spec.frameBits = 0x80E4;
|
|
spec.position.v1 = 32;
|
|
spec.position.h1 = 32;
|
|
spec.position.v2 = (int16_t)(32 + squareHeight * 8);
|
|
spec.position.h2 = (int16_t)(32 + squareWidth * 8);
|
|
spec.maxHeight = (int16_t)(squareHeight * 8);
|
|
spec.maxWidth = (int16_t)(squareWidth * 8);
|
|
spec.refCon = 0;
|
|
spec.contentDefProc = (void *)0;
|
|
gBoardWin = uiBuilderOpenWindow(&spec);
|
|
|
|
// Score window: 0xC0C4 = fTitle | fClose | fVis | fMove | fInfo
|
|
spec.title = "Scores";
|
|
spec.frameBits = 0xC0C4;
|
|
spec.position.v1 = 32;
|
|
spec.position.h1 = (int16_t)(640 - 32 - 200);
|
|
spec.position.v2 = 61;
|
|
spec.position.h2 = (int16_t)(640 - 32);
|
|
spec.maxHeight = 29;
|
|
spec.maxWidth = 200;
|
|
gScoreWin = uiBuilderOpenWindow(&spec);
|
|
|
|
// Moves window.
|
|
spec.title = "Moves";
|
|
spec.frameBits = 0xC0C4;
|
|
spec.position.v1 = 80;
|
|
spec.position.h1 = (int16_t)(640 - 32 - 100);
|
|
spec.position.v2 = 192;
|
|
spec.position.h2 = (int16_t)(640 - 32);
|
|
spec.maxHeight = 112;
|
|
spec.maxWidth = 100;
|
|
gMovesWin = uiBuilderOpenWindow(&spec);
|
|
|
|
SelectWindow(gBoardWin);
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
unsigned short userId = startdesk(640);
|
|
(void)userId;
|
|
|
|
paintDesktopBackdrop();
|
|
uiBuilderInstallMenuBar(gMenus, (uint16_t)(sizeof gMenus / sizeof gMenus[0]));
|
|
CheckMItem(1, level_1Ply);
|
|
initWindows();
|
|
newGame();
|
|
gEvent.wmTaskMask = 0x13FFL;
|
|
ShowCursor();
|
|
|
|
*(volatile unsigned char *)0x70 = 0x99;
|
|
|
|
gDone = 0;
|
|
unsigned short watchdog = 0;
|
|
do {
|
|
unsigned short event = TaskMaster(0x074E, (void *)&gEvent);
|
|
switch (event) {
|
|
case wInSpecial:
|
|
case wInMenuBar:
|
|
handleMenuLegacy((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;
|
|
}
|