1627 lines
49 KiB
C++
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 */
|