ORCA-C/C.Samples/Desktop.Samples/Reversi.cc

1627 lines
49 KiB
C++

/******************************************************************
*
* Reversi
*
* A desktop program for the Apple IIGS.
*
* Reversi is a board game played between two players. This
* program will play against an opponent (probably you, but you
* could play it against another program), or it will play
* itself (useful when you are learning).
*
* To play the game, the black piece goes first; the human
* plays black by default (the game allows this to be changed).
* The object of the game is to try to trap enemy pieces
* between one of your existing pieces and the new piece to
* be played.
*
* The game is so popular and well known that there are several
* commercial versions available, and all come with rules and
* basic strategy hints. Many fine books are also available
* from your local book store. When looking, you should note
* that the game is also sold under the name Othello.
*
* The program itself is provided as a real-world example of
* using the desktop. Unlike the other samples on this disk,
* this program was designed as a working game, not as a
* sample. For that reason, some problems that can be avoided
* by careful choice of a sample are handled here - like
* scrolling without the help of TaskMaster (to avoid scrolling
* a small part of the Moves window).
*
* To learn how the program works, start with the main event
* loop at the end of the program, and examine how it handles
* each event. The move selection procedure, FindMove, is the
* only place where an event loop is not used. That function
* uses a technique called an alpha-beta search to find the best
* move. To understand that search, you may refer to text books
* on artificial intelligence, or to any one of several fine
* articles, mostly dealing with chess, that appeared in Byte
* Magazine in the early 1980's.
*
* Note that the program is contained in two files: the first
* part of the source program is in the file named
* REVERSI1.CC, while the second part of the source is in the
* file named REVERSI2.CC. The ORCA/C append command is used
* at the end of the first file to automatically begin
* compilation of the second source file.
*
* Original Pascal version by Mike Westerfield
*
* C translation by Barbara Allred
*
* Copyright 1987-1989
* Byte Works, Inc.
*
*******************************************************************/
#pragma keep "Reversi"
#pragma lint -1
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <orca.h>
#include <types.h>
#include <quickdraw.h>
#include <qdaux.h>
#include <misctool.h>
#include <event.h>
#include <control.h>
#include <window.h>
#include <menu.h>
#include <desk.h>
#include <lineedit.h>
#include <dialog.h>
#define squareWidth 52 /* width of one square */
#define squareHeight 20 /* height of one square */
#define blank 0 /* square colors */
#define blackPiece 1
#define whitePiece 2
#define border 3
#define apple_AboutReversi 257 /* menu names/numbers */
#define file_NewGame 258
#define file_Quit 259
#define edit_UndoLastMove 270
#define edit_Cut 271
#define edit_Copy 272
#define edit_Paste 273
#define edit_Clear 274
#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
typedef int BOOL; /* simulate boolean types */
enum alertKind { norml, stop, note, caution }; /* kinds of alerts */
/* move list: */
struct moveListType { int num; /* #legal moves */
char moves [60]; }; /* list of moves */
/* Global variables */
static int ply = 1; /* set initial playing level to 1 */
static int color = whitePiece; /* color the computer plays */
static int currentColor; /* color to move next */
static int movesMade; /* # moves, by playing level, made */
static int topMove; /* 1st visible move in moves list */
static int event; /* event #; returned by TaskMaster */
static int moveHeight; /* current height of move window */
static int charHeight; /* size of a character */
static int moves [61]; /* list of moves made */
static int disp [8] = /* move displacements */
{ 9, 10, 11, -1, 1, -9, -10, -11 };
static int bSc [300] = /* square scores for 3 portions of game */
{ 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, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 500, -20, 200, 50, 50, 200,
-20, 500, 0, 0, -20, -250, 15, 10, 10, 15, -250, -20, 0,
0, 200, 15, 35, 20, 20, 35, 15, 200, 0, 0, 50, 10,
20, 15, 15, 20, 10, 50, 0, 0, 50, 10, 20, 15, 15,
20, 10, 50, 0, 0, 200, 15, 35, 20, 20, 35, 15, 200,
0, 0, -20, -250, 15, 10, 10, 15, -250, -20, 0, 0, 500,
-20, 200, 50, 50, 200, -20, 500, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 400, -20, 100, 75, 75, 100, -20, 400, 0, 0,
-20, -300, 60, 10, 10, 60, -300, -20, 0, 0, 100, 60, 50,
5, 5, 50, 60, 100, 0, 0, 75, 10, 5, 30, 30, 5,
10, 75, 0, 0, 75, 10, 5, 30, 30, 5, 10, 75, 0,
0, 100, 60, 50, 5, 5, 50, 60, 100, 0, 0, -20, -300,
60, 10, 10, 60, -300, -20, 0, 0, 400, -20, 100, 75, 75,
100, -20, 400 };
static BOOL done; /* are we done yet? */
static BOOL selfPlay; /* computer-human or computer-computer */
static BOOL movesLeft; /* are there legal moves left? */
static BOOL updateMoves; /* does entire list need updating? */
static BOOL showScoreWindow; /* is scoreWindow visible? */
static BOOL showMovesWindow; /* is movesWindow visible? */
static BOOL movesNotFront; /* is Moves window the front window? */
static char board [100]; /* main game board */
static char msg [256]; /* for builing alert strings */
/* strings for SetItem calls */
static char selfPlayStr [] = "--Self Play";
static char computerPlayStr [] = "--Play Computer";
static char computerPlaysBlack [] = "--Computer Plays Black";
static char computerPlaysWhite [] = "--Computer Plays White";
static WmTaskRec lastEvent; /* last event returned in event loop */
static ParamList wParms = /* parameters for NewWindow */
{ 78, 0x80E4, NULL, 0, 0, 0, 0, 0, NULL, 0, 0, squareHeight * 8,
squareWidth * 8, squareHeight * 8, squareWidth * 8, 0, 0, 0, 0, 0, 0,
NULL, NULL, NULL, 32, 32, 32 + squareHeight * 8, 32 + squareWidth * 8,
(GrafPortPtr) topMost, NULL };
static ParamList boardParms, scoreParms, movesParms; /* window parameter */
/* records */
static GrafPortPtr boardWindow; /* pointer to the game board window */
static GrafPortPtr scoreWindow; /* pointer to the score window */
static GrafPortPtr movesWindow; /* pointer to the Moves window */
static CtlRecHndl growHandle; /* Moves window's grow box */
static CtlRecHndl vScrollHandle; /* Moves window's vertical scroll bar */
/* Utility routines */
/****************************************************************
*
* DoAlert - Create an alert box
*
* Input:
* kind - kind of alert
* msg - alert message
*
****************************************************************/
void DoAlert (enum alertKind kind, char *msg)
{
static ItemTemplate button = /* button item */
{ 1, 36, 15, 0, 0, buttonItem, "\pOK", 0, 0, NULL };
static ItemTemplate message = /* message item */
{ 100, 5, 100, 90, 280, itemDisable+statText, NULL, 0, 0, NULL };
static AlertTemplate alertRec = /* alert box */
{ 50, 180, 107, 460, 2, 0x80, 0x80, 0x80, 0x80, NULL, NULL, NULL };
SetForeColor (0); /* set text colors */
SetBackColor (15);
message.itemDescr = msg; /* init. non-constant */
alertRec.atItemList [0] = (ItemTempPtr) &button; /* template fields */
alertRec.atItemList [1] = (ItemTempPtr) &message;
switch (kind) {
case norml: Alert (&alertRec, NULL);
break;
case stop: StopAlert (&alertRec, NULL);
break;
case note: NoteAlert (&alertRec, NULL);
break;
case caution: CautionAlert (&alertRec, NULL);
break;
default: printf ("Error in DoAlert\n");
exit (-1);
break;
}
} /* DoAlert */
/****************************************************************
*
* Even - Returns an even number by incrementing odd parameters;
* for move list calculations
*
* Input:
* i - integer tested for being even
*
* Output:
* i if i is even; i+1 if i is odd
*
****************************************************************/
int Even (int i)
{
if (i & 0x0001)
return i+1;
else
return i;
} /* Even */
/* Routines involved in playing the game */
/****************************************************************
*
* Plot - Plot a point
*
* Input:
* h - horizontal pixel of point
* v - vertical pixel of point
*
****************************************************************/
void Plot (int h, int v)
{
MoveTo (h, v);
LineTo (h, v);
} /* Plot */
/****************************************************************
*
* DrawSquare - Draw a square on the game board
*
* Input:
* square - number of square to draw
* col - color of square to draw
*
****************************************************************/
void DrawSquare (int square, int col)
{
#define penBlack 0 /* pen colors */
#define penGreen 2
#define penWhite 3
Rect r; /* square's rectangle */
StartDrawing (boardWindow); /* draw to our window */
r.h2 = (square % 10) * squareWidth - 1; /* set up square's rectangle */
r.v2 = (square / 10) * squareHeight - 1;
r.h1 = r.h2 - squareWidth + 1;
r.v1 = r.v2 - squareHeight + 1;
SetSolidPenPat (penGreen); /* draw background of square */
PaintRect (&r);
SetSolidPenPat (penBlack); /* draw edge of square */
MoveTo (r.h1, r.v2);
LineTo (r.h2, r.v2);
LineTo (r.h2, r.v1);
switch (square) { /* draw "corner" dots, if required */
case 22: case 26: case 62: case 66:
Plot (r.h2-1, r.v2-1);
break;
case 23: case 27: case 63: case 67:
Plot (r.h1, r.v2-1);
break;
case 32: case 36: case 72: case 76:
Plot (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) { /* draw the piece, if any */
if (col == whitePiece)
SetSolidPenPat (penWhite);
PaintOval (&r);
}
} /* DrawSquare */
/****************************************************************
*
* DrawBoard - Draw (or redraw) the entire game board
*
****************************************************************/
void DrawBoard (void)
{
int i; /* loop variable */
int col; /* column # */
for (i = 11; i <= 88; i++) {
col = i % 10;
if ((col != 0) && (col != 9))
DrawSquare (i, board[i]);
}
} /* DrawBoard */
/****************************************************************
*
* GetMoves - Create a list of legal moves
*
* Input:
* board - game board to search for moves
* color - color of piece for which conducting search
* moveList - record of moves to make
*
****************************************************************/
void GetMoves (char board[], int color, struct moveListType *moveList)
{
int index; /* square being checked */
int tindex; /* work index */
int enemyColor = color ^ 3; /* temp variable for enemy color */
int dir; /* direction being checked */
struct moveListType lMoveList; /* local move list - for efficiency */
lMoveList.num = 0; /* no moves so far */
for (index = 11; index < 90; index++) { /* loop over all squares */
if (board[index] == blank) /* check only empty squares */
for (dir = 0; dir < 8; dir++) { /* loop in all 8 directions */
tindex = index + disp[dir]; /* see if there's a capture */
if (board[tindex] == enemyColor) { /* in this direction */
while (board[tindex] == enemyColor) /* skip enemy pieces */
tindex += disp[dir];
if (board[tindex] == color) { /* if last piece is ours, */
/* move is legal */
lMoveList.moves [lMoveList.num] = index;
lMoveList.num++;
goto Out;
}
}
}
Out: ;
}
*moveList = lMoveList; /* return move list */
} /* GetMoves */
/****************************************************************
*
* CheckForDone - Check if game is over
*
****************************************************************/
void CheckForDone (void)
{
struct moveListType moveList; /* for checking # of moves */
char string [10]; /* for converting scores to strings*/
int wcnt, bcnt; /* # pieces for each side */
int i; /* loop variable */
GetMoves (board, whitePiece, &moveList);
if (! moveList.num) {
GetMoves (board, blackPiece, &moveList);
if (! moveList.num) {
for (i = 11, wcnt = 0, bcnt = 0; i < 90; i++) {
if (board[i] == whitePiece)
wcnt++;
else if (board[i] == blackPiece)
bcnt++;
}
if (wcnt == bcnt)
strcpy (msg, "The game is overs. It\ris a draw.");
else {
if (wcnt > bcnt)
strcpy (msg, "White");
else
strcpy (msg, "Black");
strcat (msg, " wins by a score\rof ");
sprintf (string, "%d", bcnt); /* convert scores to strings */
strcat (msg, string);
strcat (msg, " to ");
sprintf (string, "%d", wcnt);
strcat (msg, string);
}
DoAlert (note, c2pstr (msg));
movesLeft = false;
}
}
} /* CheckForDone */
/****************************************************************
*
* ScoreEdge - Score an edge of the game board by these rules:
*
* 1. An edge must have at least one empty square to be scored.
* 2. If there is a single space between friendly pieces, score -100.
* 3. If there are two spaces between friendly pieces, score 30.
* 4. If there are three spaces between friendly pieces, score -50.
* 5. If there is a solid line of enemy pieces between friendly
* pieces, score -150;
*
* Input:
* edge - array of edge squares to score
*
* Output:
* score of edges
*
****************************************************************/
int ScoreEdge (int edge[])
{
BOOL atLeastOneBlank; /* for checking rule #1 */
int s = 0, rs; /* for computing scores */
int enemyColor; /* temp variable for enemy color */
int i, j; /* loop variables */
atLeastOneBlank = false; /* check rule 1 */
for (i = 1; i < 9; i++)
atLeastOneBlank = atLeastOneBlank || (edge[i] == blank);
if (atLeastOneBlank) /* check all edge */
for (i = 1; i < 8; i++) /* positions */
if ((edge[i] == blackPiece) || (edge[i] == whitePiece)) {
enemyColor = edge[i] ^ 3;
j = i + 1;
if (edge[j] == enemyColor) { /* check rule 5 */
while (edge[j] == enemyColor)
j++;
if (edge[j] == edge[i])
if (enemyColor == whitePiece)
s -= 150;
else
s += 150;
}
else if (edge[j] == blank) { /* check rules 2..4 */
while (edge[j] == blank)
j++;
if (edge[j] == edge[i]) {
switch (j - i) {
case 2: rs = -100; /* score rule 2 */
break;
case 3: rs = 30; /* score rule 3 */
break;
case 4: rs = -50; /* score rule 4 */
break;
default: break;
}
if (edge[i] == whitePiece)
s -= rs;
else
s += rs;
} /* if */
} /* else if */
} /* if */
return s;
} /* ScoreEdge */
/****************************************************************
*
* Score - Score the game board passed
*
* Input:
* board - game board to score
*
* Output:
* score of game board
*
****************************************************************/
int Score (char board[])
{
int s = 0, rs; /* temp variables for scoring */
int pi; /* game portion index */
int numPieces = 0; /* # pieces on board */
int edge [10]; /* for scoring edges */
int i; /* loop variable */
for (i = 11; i < 90; i++) /* loop over all squares */
if (board[i] == whitePiece) { /* add 4 for black, decrement */
s -= 4; /* 4 for white */
numPieces++;
}
else if (board[i] == blackPiece) {
s += 4;
numPieces++;
}
if (numPieces < 24) /* set index into board scores */
pi = 0; /* by part of game this is */
else if (numPieces < 44)
pi = 100;
else
pi = 200;
for (i = 11; i < 90; i++) /* loop over all squares, */
/* summing square values */
if ((board[i] == blackPiece) || (board[i] == whitePiece)) {
if ((i == 12) || (i == 21) || (i == 22)) /* squares adjacent to corners */
{ /* get special treatment */
if ((board[11] == blackPiece) || (board[11] == whitePiece))
rs = 10;
else
rs = bSc[pi+i];
}
else if ((i == 17) || (i == 27) || (i == 28)) {
if ((board[18] == blackPiece) || (board[18] == whitePiece))
rs = 10;
else
rs = bSc[pi+i];
}
else if ((i == 71) || (i == 72) || (i == 82)) {
if ((board[81] == blackPiece) || (board[81] == whitePiece))
rs = 10;
else
rs = bSc[pi+i];
}
else if ((i == 77) || (i == 78) || (i == 87)) {
if ((board[88] == blackPiece) || (board[88] == whitePiece))
rs = 10;
else
rs = bSc[pi+i];
}
else
rs = bSc[pi+i];
if (board[i] == whitePiece)
s -= rs;
else
s += rs;
}
for (i = 0; i < 10; i++) /* score top edge */
edge[i] = board[10+i];
s += ScoreEdge (edge);
for (i = 0; i < 10; i++) /* score bottom edge */
edge[i] = board[i+80];
s += ScoreEdge (edge);
for (i = 0; i < 10; i++) /* score left edge */
edge[i] = board[1+i*10];
s += ScoreEdge (edge);
for (i = 0; i < 10; i++) /* score right edge */
edge[i] = board[8+i*10];
s += ScoreEdge (edge);
return s; /* return the score */
} /* Score */
/****************************************************************
*
* MakeAMove - Make a move on the main playing board
*
* Input:
* index - board index of move to make
* col - color of player making move
*
****************************************************************/
void MakeAMove (int index, int col)
{
#define pause 100 /* index for pause */
int dir; /* loop variable for directions */
int tindex; /* temp index; for captures */
int enemyColor; /* temp variable for enemy color */
int i; /* loop variable */
moves[++movesMade] = index; /* record the move */
DrawSquare (index, col); /* flash the piece played */
for (i = 0; i < pause; i++)
;
DrawSquare (index, blank);
for (i = 0; i < pause; i++)
;
DrawSquare (index, col);
board[index] = col; /* make the move on the board */
enemyColor = col ^ 3; /* set enemy color */
for (dir = 0; dir < 8; dir++) { /* loop in all 8 directions */
tindex = index + disp[dir]; /* see if there's a capture */
if (board[tindex] == enemyColor) { /* in this direction */
while (board[tindex] == enemyColor) /* skip enemy pieces */
tindex += disp[dir];
if (board[tindex] == col) { /* if last piece is ours, capture */
tindex = index + disp[dir];
while (board[tindex] != col) {
DrawSquare (tindex, col);
board[tindex] = col;
tindex += disp[dir];
} /* while */
} /* if */
} /* if */
} /* for */
} /* MakeAMove */
/****************************************************************
*
* EndScore - Compute an end-game score (no more moves) for passed board
*
* Input:
* board - game board to score
*
* Output:
* score of game board passed
*
****************************************************************/
int EndScore (char board[])
{
int s = 0; /* work copy of score */
int i; /* loop variable */
for (i = 11; i < 90; i++) /* count difference in pieces */
if (board[i] == whitePiece)
s--;
else if (board[i] == blackPiece)
s++;
if (s < 0) /* set the return value */
return INT_MIN + 65 + s;
else if (s > 0)
return INT_MAX - 65 + s;
else
return 0;
} /* EndScore */
static char boardName [] = "\pReversi"; /* names of Reversi's windows */
static char scoreName [] = "\pScores";
static char movesName [] = "\pMoves";
/****************************************************************
*
* ScoreMove - Find the score for a particular move
*
* Input:
* board - game board to make move on
* index - move to make
* s - best score at previous level
* col - color computer is playing
* level - current playing level
*
* Output:
* score for chosen move
*
****************************************************************/
int ScoreMove (char board[], int index, int s, int col, int level)
{
int bscore; /* best score from this level */
int bmove; /* best move from this level */
int enemyColor; /* color of the enemy peices */
int dir; /* direction loop variable */
int tindex; /* temp board index; for captures */
int i; /* loop variable */
struct moveListType moveList; /* list of legal moves */
char lBoard [100]; /* local copy of game board */
/* Make the move passed */
memcpy (lBoard, board, 100); /* make local copy of passed board */
enemyColor = col ^ 3; /* set enemy color */
if (index) { /* if there was a move, make it */
lBoard[index] = col; /* make the move on the board */
for (dir = 0; dir < 8; dir++) { /* loop in all 8 directions */
tindex = index + disp[dir]; /* see if there's a capture in */
if (lBoard[tindex] == enemyColor) { /* this direction */
while (lBoard[tindex] == enemyColor) /* skip enemy pieces */
tindex += disp[dir];
if (lBoard[tindex] == col) { /* if last piece is ours, capture */
tindex = index + disp[dir];
while (lBoard[tindex] != col) {
lBoard[tindex] = col;
tindex += disp[dir];
}
} /* if */
} /* if */
} /* for */
} /* if */
/* Part 2: Score the board */
if (level == ply) /* if at max depth, score is static */
return Score (lBoard);
else { /* else pick from available moves */
GetMoves (lBoard, enemyColor, &moveList); /* get a list of legal moves */
if (enemyColor == whitePiece) /* init. score is worst possible, so */
bscore = INT_MAX; /* that any alternative is selected */
else
bscore = INT_MIN;
if (! moveList.num) { /* if no moves, check for end of game */
GetMoves (lBoard, col, &moveList);
if (! moveList.num)
bscore = EndScore (lBoard);
else
bscore = ScoreMove (lBoard, 0, bscore, enemyColor, level+1);
}
else {
for (i = 0; i < moveList.num; i++) { /* scan/score available moves */
s = ScoreMove (lBoard, moveList.moves[i], bscore, enemyColor,
level+1);
if (enemyColor == whitePiece) { /* if this is the best so far, */
/* remember the move */
if (s < bscore) {
bscore = s;
bmove = moveList.moves[i];
}
}
else {
if (s > bscore) {
bscore = s;
bmove = moveList.moves[i];
}
} /* else */
} /* for */
} /* else */
return bscore;
} /* else */
} /* ScoreMove */
/****************************************************************
*
* FindMove - Make a computer-generated move
*
* Input:
* col - color computer is playing
*
****************************************************************/
void FindMove (int col)
{
struct moveListType moveList; /* list of legal moves */
int bmove; /* best move from this level */
int bscore; /* best score from this level */
int s; /* work copy of score */
int i; /* loop variable */
WaitCursor (); /* change to the watch cursor */
GetMoves (board, col, &moveList); /* get a list of legal moves */
if (moveList.num == 1) /* if there's only 1 move, make it */
MakeAMove (moveList.moves[0], col);
else if (moveList.num > 1) { /* if there's more than 1, initial */
if (col == whitePiece) /* score is worst possible, so */
bscore = INT_MAX; /* any alternative is selected */
else
bscore = INT_MIN;
for (i = 0; i < moveList.num; i++) { /* scan/score available moves */
s = ScoreMove (board, moveList.moves[i], bscore, col, 1);
if (col == whitePiece) {
if (s < bscore) { /* if this is best so far, */
bscore = s; /* remember the move */
bmove = moveList.moves[i];
}
}
else { /* if color is black then */
if (s > bscore) {
bscore = s;
bmove = moveList.moves[i];
}
}
}
MakeAMove (bmove, col); /* make the best move found */
}
else { /* no legal moves */
strcpy (msg, "\pI cannot move, so I\rmust pass.\r");
InitCursor ();
DoAlert (note, msg);
WaitCursor ();
}
InitCursor (); /* back to the arrow cursor */
CheckForDone ();
} /* FindMove */
/****************************************************************
*
* NewGame - Set up the board for a new game
*
****************************************************************/
void NewGame (void)
{
Rect r; /* rectangle for clearing Moves window */
GrafPortPtr port; /* graph port pointer */
int i; /* loop variable */
int col, row; /* row, column numbers */
static void DrawMoves (void); /* subroutines called by NewGame */
static void DrawScore (void);
/* Write the contents of the Scores window */
for (i = 0; i < 100; i++) { /* initialize the game board */
col = i % 10;
row = i / 10;
if ((!row) || (!col) || (row == 9) || (col == 9))
board[i] = border;
else
board[i] = blank;
}
board[44] = whitePiece; board[55] = whitePiece;
board[45] = blackPiece; board[54] = blackPiece;
currentColor = blackPiece; /* black moves first */
movesLeft = true; /* the game is not over yet... */
movesMade = 0; /* empty the moves list */
topMove = 1; /* first visible move in moves list */
updateMoves = true; /* draw entire list */
if (showMovesWindow) { /* if move list is visible, clear it */
port = GetPort (); /* save the graph port */
StartDrawing (movesWindow); /* draw to the Moves window */
GetPortRect (&r); /* get the rectangle */
EraseRect (&r); /* erase the window's contents */
DrawControls (movesWindow); /* draw the controls */
SetPort (port); /* restore the old graph port */
DrawMoves (); /* draw the pieces */
}
if (showScoreWindow)
DrawScore (); /* redraw the Scores window */
} /* NewGame */
/* Initialization routines */
/****************************************************************
*
* InitMenus - Create and draw the initial menu bar
*
****************************************************************/
void InitMenus (void)
{
InsertMenu (NewMenu (">> Options \\N5\r" /* create the Options menu */
"--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"), 0);
InsertMenu (NewMenu (">> Level \\N4\r" /* create the Level menu */
"--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"), 0);
InsertMenu (NewMenu (">> Edit \\N3\r" /* create the Edit menu */
"--Undo Last Move\\N270D*Zz\r"
"---\\N512D\r"
"--Cut\\N271D*Xx\r"
"--Copy\\N272D*Cc\r"
"--Paste\\N273D*Vv\r"
"--Clear\\N274D\r"
".\r"), 0);
InsertMenu (NewMenu (">> File \\N2\r" /* create the File menu */
"--New Game\\N258*Nn\r"
"---\\N513D\r"
"--Quit\\N259*Qq\r"
".\r"), 0);
InsertMenu (NewMenu (">>@\\XN1\r" /* create the Apple menu */
"--About Reversi\\N257\r"
"---\\N513D\r"
".\r"), 0);
FixAppleMenu (1); /* add desk accessories */
FixMenuBar (); /* draw the completed menu bar */
DrawMenuBar ();
CheckMItem (true, level_1Ply); /* check ply 1 */
} /* InitMenus */
/****************************************************************
*
* InitVariables - Initialize global variables
*
****************************************************************/
void InitVariables (void)
{
Rect r; /* rectangle for finding height of chars */
CharBounds ('A', &r); /* find the height of a character */
charHeight = r.h2 - r.h1;
NewGame (); /* set up the board for a new game */
}
/****************************************************************
*
* InitWindow - Draw the game board's window
*
****************************************************************/
void InitWindow (void)
{
#define scoreWidth 96 /* width of Score, Moves windows */
Rect r; /* for setting sizes of controls */
boardParms = wParms; /* create the board window */
boardParms.wTitle = boardName;
boardWindow = NewWindow (&boardParms);
scoreParms = wParms; /* create the Scores window */
scoreParms.wTitle = scoreName;
scoreParms.wFrameBits = 0xC0C4;
scoreParms.wDataH = 29;
scoreParms.wDataW = scoreWidth;
scoreParms.wMaxH = 29;
scoreParms.wMaxW = scoreWidth;
scoreParms.wPosition.v1 = 32;
scoreParms.wPosition.h1 = 640 - 32 - scoreWidth;
scoreParms.wPosition.v2 = 61;
scoreParms.wPosition.h2 = 640 - 32;
scoreWindow = NewWindow (&scoreParms);
movesParms = scoreParms; /* create the Moves window */
movesParms.wTitle = movesName;
movesParms.wDataH = 112;
movesParms.wPosition.v1 = 80;
movesParms.wPosition.v2 = 192;
movesWindow = NewWindow (&movesParms);
r.h1 = scoreWidth - 23; /* create a grow box */
r.h2 = scoreWidth + 1;
r.v1 = 100; r.v2 = 113;
growHandle = NewControl (movesWindow, &r, NULL, 0, 0, 0, 0, (void *) 0x08000000,
0L, NULL);
r.v1 = 0; r.v2 = 101; /* create scroll bar */
vScrollHandle = NewControl (movesWindow, &r, NULL, 3, 0, 112, 25,
(void *) 0x06000000, 0L, NULL);
moveHeight = 112; /* set height of window */
SelectWindow (boardWindow); /* make game board the front window */
} /* InitWindow */
/* Action Routines */
/****************************************************************
*
* WriteMove - Write a move on the screen
*
* Input:
* move - move to write
*
****************************************************************/
void WriteMove (int move)
{
printf ("%c%d ", (char) (move % 10 - 1 + 'A'), 9 - move / 10);
} /* WriteMove */
/****************************************************************
*
* DrawMoves - Write the contents of the Moves window
*
****************************************************************/
void DrawMoves (void)
{
GrafPortPtr port; /* graphics port */
Rect r; /* rectangle for drawing colors */
int i, n; /* index variables */
if (showMovesWindow) {
port = GetPort (); /* save the current graph port */
StartDrawing (movesWindow); /* draw to the Score window */
SetForeColor (0); /* black pen on white background */
SetBackColor (15);
if (updateMoves) {
SetSolidPenPat (0); /* draw black column header */
r.v1 = 5; r.h1 = 26;
r.v2 = 13; r.h2 = 40;
PaintOval (&r);
r.v1 = 5; r.h1 = 47; /* draw white column header */
r.v2 = 13; r.h2 = 61;
PaintOval (&r);
SetSolidPenPat (15);
r.v1 = 6; r.h1 = 48;
r.v2 = 12; r.h2 = 60;
PaintOval (&r);
}
if (movesMade > 0) { /* draw the moves */
MoveTo (2, 25);
i = topMove;
n = (i + 1) / 2;
while (i <= movesMade) {
if ((updateMoves) || (i > movesMade - 2)) {
if (n < 10)
printf (" ");
printf ("%2d: ", n);
WriteMove (moves[i]);
if (i + 1 <= movesMade)
WriteMove (moves[i+1]);
else
printf (" ");
}
printf ("\r");
n++;
i += 2;
}
printf (" \r"); /* blank the last line (for scrolls) */
SetCtlParams (Even (movesMade) / 2 * charHeight, moveHeight - 25,
vScrollHandle); /* update the thumb size */
SetPort (port); /* restore the old graph port */
}
updateMoves = false; /* complete update is no longer needed */
}
} /* DrawMoves */
/****************************************************************
*
* DrawScore - Write the contents of the Score window
*
****************************************************************/
void DrawScore (void)
{
int i; /* loop variable */
int wcnt, bcnt; /* for counting pieces */
GrafPortPtr port; /* current graphics port */
if (showScoreWindow) {
port = GetPort (); /* save the graph port */
StartDrawing (scoreWindow); /* draw to the score window */
SetForeColor (0); /* black pen on white background */
SetBackColor (15);
MoveTo (2, 10); /* start at upper left corner */
for (i = 11, wcnt = 0, bcnt = 0; i < 90; i++) /* count the pieces on board */
if (board[i] == whitePiece)
wcnt++;
else if (board[i] == blackPiece)
bcnt++;
printf ("White: %d \rBlack: %d \r", wcnt, bcnt); /* write the scores */
printf ("Score: %d \r", Score (board));
SetPort (port); /* restore the old graph port */
}
} /* DrawScore */
/****************************************************************
*
* MenuShowMovesWindow - Hide or show the Moves window
*
****************************************************************/
void MenuShowMovesWindow (void)
{
GrafPortPtr port; /* current graphics port */
showMovesWindow = ! showMovesWindow; /* reverse window's status */
CheckMItem (showMovesWindow, options_ShowMovesWindow);
ShowHide (showMovesWindow, movesWindow);
if (showMovesWindow) { /* if visible, draw it... */
updateMoves = true; /* draw the moves list */
DrawMoves ();
port = GetPort (); /* save the graph port */
StartDrawing (movesWindow); /* redraw the controls */
SetPort (port);
}
SelectWindow (boardWindow);
} /* MenuShowMovesWindow */
/****************************************************************
*
* MenuShowScoreWindow - Hide or show the Score window
*
****************************************************************/
void MenuShowScoreWindow (void)
{
showScoreWindow = ! showScoreWindow;
CheckMItem (showScoreWindow, options_ShowScoreWindow);
ShowHide (showScoreWindow, scoreWindow);
SelectWindow (boardWindow);
DrawScore ();
} /* MenuShowScoreWindow */
/****************************************************************
*
* MenuAbout - Show the About dialog
*
****************************************************************/
void MenuAbout (void)
{
strcpy (msg, "\pReversi 1.0\rCopyright 1989\rByte Works, Inc.\r"
"\rBy Mike Westerfield");
DoAlert (note, msg);
} /* MenuAbout */
/****************************************************************
*
* MenuColor - Change the color the computer plays
*
****************************************************************/
void MenuColor (void)
{
if (color == whitePiece) {
SetMItem (computerPlaysWhite, options_ComputerPlaysWhite);
color = blackPiece;
}
else {
SetMItem (computerPlaysBlack, options_ComputerPlaysWhite);
color = whitePiece;
}
} /* MenuColor */
/****************************************************************
*
* MenuPass - Player wants to pass
*
****************************************************************/
void MenuPass (void)
{
struct moveListType moveList; /* for seeing if there are legal moves */
GetMoves (board, currentColor, &moveList); /* get a list of legal moves */
if (! moveList.num) /* OK to pass if there are no moves */
currentColor ^= 3;
else { /* error to pass if there are moves */
strcpy (msg, "\pYou have legal moves\rso you cannot pass.\r");
DoAlert (stop, msg);
}
} /* MenuPass */
/****************************************************************
*
* MenuSelfPlay - Change the current playing mode
*
****************************************************************/
void MenuSelfPlay (void)
{
selfPlay = ! selfPlay;
if (selfPlay)
SetMItem (computerPlayStr, options_SelfPlay);
else
SetMItem (selfPlayStr, options_SelfPlay);
} /* MenuSelfPlay */
/****************************************************************
*
* MenuSetPly - Change the current playing level
*
* Input:
* newPly - menu number of playing level selected
*
****************************************************************/
void MenuSetPly (int newPly)
{
CheckMItem (false, ply + level_1Ply - 1); /* uncheck old ply */
CheckMItem (true, newPly); /* check new ply */
ply = newPly - level_1Ply + 1; /* set the ply level */
} /* MenuSetPly */
/****************************************************************
*
* HandleMenu - Handle a menu event
*
* Input:
* menuNum - menu number of menu to handle
*
****************************************************************/
void HandleMenu (int menuNum)
{
switch (menuNum) { /* handle the menu */
case apple_AboutReversi:
MenuAbout ();
break;
case file_NewGame:
NewGame ();
DrawBoard ();
break;
case file_Quit:
done = true;
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 (menuNum);
break;
case options_SelfPlay:
MenuSelfPlay ();
break;
case options_ComputerPlaysWhite:
MenuColor ();
break;
case options_Pass:
MenuPass ();
break;
case options_ShowScoreWindow:
MenuShowScoreWindow ();
break;
case options_ShowMovesWindow:
MenuShowMovesWindow ();
break;
default:
break;
} /* switch */
HiliteMenu (false, (int) (lastEvent.wmTaskData >> 16));
} /* HandleMenu */
/****************************************************************
*
* HideAWindow - Hide the front window
*
****************************************************************/
void HideAWindow (void)
{
if (FrontWindow () == scoreWindow)
MenuShowScoreWindow ();
else /* if FrontWindow == movesWindow */
MenuShowMovesWindow ();
} /* HideAWindow */
/****************************************************************
*
* LegalMove - Check if a move is legal
*
* Input:
* index - move to make
* color - color of player making the move
*
****************************************************************/
BOOL LegalMove (int index, int color)
{
struct moveListType moveList; /* for list of legal moves */
int i; /* loop variable */
GetMoves (board, color, &moveList);
for (i = 0; i < moveList.num; i++)
if (index == moveList.moves[i])
return true;
return false;
} /* LegalMove */
/****************************************************************
*
* GrowMoves - Grow the Moves window (the only window that can grow)
*
****************************************************************/
void GrowMoves (void)
{
Rect rt; /* for creating scroll bar */
int movesInWindow; /* # moves the Moves window can display */
union longShort { long isLong; /* for converting between int and long */
int lsw, msw; } r, s;
s.isLong = GetMaxGrow (movesWindow); /* get max size */
/* track the growing of the window */
r.isLong = GrowWindow (s.msw, 64, lastEvent.where.h, lastEvent.where.v,
movesWindow);
if (r.isLong) { /* if the size changed then... */
SizeWindow (s.msw, r.lsw, movesWindow); /* change the window's size */
MoveControl (73, r.lsw - 12, growHandle); /* move the grow box */
DisposeControl (vScrollHandle); /* resize the scroll bar */
rt.h1 = 73; rt.h2 = 89;
rt.v1 = 0; rt.v2 = r.lsw - 11;
vScrollHandle = /* update the thumb size */
NewControl (movesWindow, &rt, NULL, 3, 0, 112, 25, (void *) 0x06000000,
0L, NULL);
/* position thumb of the scroll bar */
SetCtlParams (Even (movesMade) / 2 * charHeight, r.lsw - 25, vScrollHandle);
SetCtlValue (topMove / 2 * charHeight, vScrollHandle);
if (r.lsw > moveHeight) { /* if the window grew then... */
/* dispose of trailing blanks */
movesInWindow = (r.lsw - 25) / charHeight * 2 + 2;
if ((Even(topMove) + movesInWindow + 1) / 2 > (Even (movesMade) + 1) / 2)
{
topMove = Even (movesMade) - movesInWindow;
if (!(topMove & 0x0001))
topMove++;
if (topMove < 1)
topMove = 1;
}
updateMoves = true; /* redraw moves list */
moveHeight = r.lsw;
DrawMoves ();
}
moveHeight = r.lsw; /* update the window size */
}
} /* GrowMoves */
/****************************************************************
*
* Scroll - Handle vertical scrolls in the Moves window
*
****************************************************************/
void Scroll (void)
{
int part; /* part # from TrackControl */
int movesInWindow; /* # moves the Moves window can display */
part = TrackControl (lastEvent.where.h, lastEvent.where.v, (void *) -1,
vScrollHandle);
movesInWindow = (moveHeight - 25) / charHeight * 2 + 2;
if ((part > 4) && (part < 9)) { /* if the part is not the slide switch... */
switch (part) {
case 5: /* handle up arrow */
if (topMove > 1)
topMove -= 2;
break;
case 6: /* handle down arrow */
if (Even (topMove) + movesInWindow < Even (movesMade) + 1)
topMove += 2;
break;
case 7: /* handle up page */
if (topMove > 1) {
topMove -= movesInWindow;
if (topMove < 1)
topMove = 1;
}
break;
case 8: /* handle down page */
if (Even(topMove) + movesInWindow < Even(movesMade) + 1) {
topMove += movesInWindow;
if ( ((Even(topMove) + movesInWindow + 1) / 2) >
(Even(movesMade) + 1) / 2 ) {
topMove = Even (movesMade) - movesInWindow;
if (! (topMove >> 15) & 0x0001)
topMove++;
}
}
} /* switch */
updateMoves = true; /* redraw the window */
DrawMoves ();
/* position thumb of the scroll bar */
SetCtlValue (topMove / 2 * charHeight, vScrollHandle);
}
else if (part == 129) { /* reposition based on new thumb loc. */
topMove = GetCtlValue (vScrollHandle) * 2 / charHeight +1;
if (! (topMove >> 15) & 0x0001)
topMove++;
updateMoves = true;
DrawMoves ();
}
} /* Scroll */
/****************************************************************
*
* TryMove - If there is a legal move at the indicated coordinates,
* make it
*
****************************************************************/
void TryMove (void)
{
Point p; /* location of mouse */
int row, col; /* position on board */
int index; /* index into board array */
if (movesLeft) { /* make sure game is not over */
StartDrawing (boardWindow); /* easy way to set port */
p.h = lastEvent.where.h; /* find out where the mouse is */
p.v = lastEvent.where.v;
GlobalToLocal (&p);
col = p.h / squareWidth + 1; /* convert to board index */
row = p.v / squareHeight + 1;
index = row * 10 + col;
if (LegalMove( index, currentColor)) { /* if move is legal, make it */
MakeAMove (index, currentColor);
currentColor ^= 3; /* switch color to move */
}
else { /* flag a bad move */
strcpy (msg, "\pIllegal move -\rtry again.");
DoAlert (stop, msg);
}
CheckForDone ();
DrawScore (); /* update the score */
DrawMoves (); /* update the move list */
}
} /* TryMove */
/****************************************************************
*
* DoContent - Handle a mouse-down event in the content region
*
****************************************************************/
void DoContent (void)
{
int part; /* part # returned by FindControl */
CtlRecHndl ctl; /* control handle */
if (FrontWindow () == (GrafPortPtr) lastEvent.wmTaskData) {
if ((GrafPortPtr) lastEvent.wmTaskData == boardWindow)
TryMove (); /* try making a move */
else if ((GrafPortPtr) lastEvent.wmTaskData == movesWindow) {
part = FindControl (&ctl, lastEvent.where.h, lastEvent.where.v,
movesWindow);
if (part == 10)
GrowMoves (); /* handle grow box */
else if (part)
Scroll (); /* handle scroll bar */
}
}
} /* DoContent */
/****************************************************************
*
* Update - Handle an update event
*
****************************************************************/
void Update (void)
{
if (lastEvent.message == (long) boardWindow) {
BeginUpdate (boardWindow); /* update the board window */
DrawBoard (); /* redraw the board */
EndUpdate (boardWindow); /* complete the update */
}
else if (lastEvent.message == (long) scoreWindow) {
BeginUpdate (scoreWindow); /* update the score window */
DrawScore (); /* redraw the score window */
EndUpdate (scoreWindow); /* complete the update */
}
else if (lastEvent.message == (long) movesWindow) {
BeginUpdate (movesWindow); /* update the Moves window */
updateMoves = true; /* redraw the Moves window */
DrawMoves ();
EndUpdate (movesWindow); /* complete the update */
DrawControls (movesWindow); /* redraw the controls */
}
} /* Update */
/****************************************************************
*
* Main program starts here
*
****************************************************************/
int main (void)
{
startdesk (640); /* initialize the dekstop environment */
QDAuxStartUp ();
SetPenMode (0); /* set pen mode to copy */
InitMenus (); /* set up the menu bar */
InitWindow (); /* draw the board's window */
InitVariables (); /* initialize global variables */
lastEvent.wmTaskMask = 0x13FFL; /* let Task Master do most stuff */
ShowCursor (); /* show the cursor */
done = false; /* main event loop */
do {
event = TaskMaster (0x074E, &lastEvent);
switch (event) { /* handle the events we need to */
case wInMenuBar: HandleMenu ((int) lastEvent.wmTaskData);
break;
case inUpdate: Update ();
break;
case wInContent: DoContent ();
break;
case wInGoAway : HideAWindow ();
break;
default: break;
}
/* If the Moves window has been brought to front, draw its controls. */
if (FrontWindow () == movesWindow) {
if (movesNotFront) {
movesNotFront = false;
HiliteControl (0, vScrollHandle);
HiliteControl (0, growHandle);
DrawControls (movesWindow);
}
}
else if (! movesNotFront) {
movesNotFront = true;
HiliteControl (255, vScrollHandle);
HiliteControl (255, growHandle);
}
if (movesLeft) { /* let the computer move */
if (selfPlay) {
FindMove (currentColor);
currentColor ^= 3;
DrawScore ();
DrawMoves ();
}
else if (color == currentColor) {
FindMove (color);
currentColor ^= 3;
DrawScore ();
DrawMoves ();
}
} /* if */
}
while (!done);
QDAuxShutDown (); /* shut down the desktop environment */
enddesk ();
} /* Reversi */