9 tic tac toe program to illustrate simple minimax searching
13 Scott Gasch (SGasch) 18 Mar 2004
19 ver2 : alpha beta search
29 SQUARE g_sComputerPlays = O_MARK; // what side comp plays
30 unsigned int g_uPly = 0;
31 MOVE g_mvBest = { 0 };
32 unsigned int g_uNodes = 0;
34 //+----------------------------------------------------------------------------
36 // Function: SquareContentsToChar
38 // Synopsis: Helper function for DrawBoard
40 // Arguments: IN SQUARE s - a square to return a char to represent
42 // Returns: char - character representing square
44 //+----------------------------------------------------------------------------
45 char SquareContentsToChar(IN SQUARE s)
67 //+----------------------------------------------------------------------------
69 // Function: DrawBoard
71 // Synopsis: Draw the board
73 // Arguments: IN POSITION *p - pointer to a position whose board to draw
77 //+----------------------------------------------------------------------------
78 void DrawBoard(IN POSITION *p)
82 for (y = 0; y < BOARD_SIZE; y++)
84 for (x = 0; x < BOARD_SIZE; x++)
86 printf("%c ", SquareContentsToChar(p->sBoard[y][x]));
90 ASSERT(X_OR_O(p->sWhoseTurn));
91 printf("\n%c to move.\n", SquareContentsToChar(p->sWhoseTurn));
94 //+----------------------------------------------------------------------------
96 // Function: ClearBoard
98 // Synopsis: Clear the board
100 // Arguments: IN OUT POSITION *p - pointer to position whose board to clear
104 //+----------------------------------------------------------------------------
105 void ClearBoard(IN OUT POSITION *p)
107 memset(p->sBoard, 0, sizeof(p->sBoard));
108 p->sWhoseTurn = X_MARK; // x's go first
109 p->uNumEmpty = (BOARD_SIZE * BOARD_SIZE);
112 //+----------------------------------------------------------------------------
114 // Function: IsLegalMove
116 // Synopsis: Determine if a given move is legal on a given board
118 // Arguments: IN POSITION *p - the board to play the move on
119 // IN MOVE *m - the move in question
121 // Returns: BOOL - TRUE if it's legal, FALSE otherwise
123 //+----------------------------------------------------------------------------
124 BOOL IsLegalMove(IN POSITION *p, IN MOVE *m)
126 if ((m->cVpos < BOARD_SIZE) && (m->cHpos < BOARD_SIZE))
128 if (IS_SQUARE_EMPTY(p->sBoard[m->cVpos][m->cHpos]))
136 //+----------------------------------------------------------------------------
138 // Function: GetHumanMove
140 // Synopsis: Ask the human for a move
142 // Arguments: IN POSITION *p - the current board
143 // OUT MOVE *m - the move the human made; this struct is populated
144 // as a side-effect of this function.
146 // Returns: void* (populates the move struct)
148 //+----------------------------------------------------------------------------
149 void GetHumanMove(IN POSITION *p, OUT MOVE *m)
155 printf("Enter your move number: ");
158 m->cHpos = NUM_TO_HPOS(x);
159 m->cVpos = NUM_TO_VPOS(x);
160 m->sMark = OPPOSITE_MARK(g_sComputerPlays);
162 while(FALSE == IsLegalMove(p, m));
165 //+----------------------------------------------------------------------------
167 // Function: GameOver
169 // Synopsis: Is the game over?
171 // Arguments: IN POSITION *p - the board
172 // OUT SQUARE *psWhoWon - who won the game (if it's over)
174 // Returns: TRUE if the game is over. Also sets psWhoWon telling
175 // which side one if the game is over. This also serves
176 // as a very simple evaluation routine for the search.
178 // FALSE if the game is not over.
180 //+----------------------------------------------------------------------------
181 BOOL GameOver(IN POSITION *p, OUT SQUARE *psWhoWon)
186 for (x = 0; x < BOARD_SIZE; x++)
190 for (y = 0; y < BOARD_SIZE; y++)
192 iSum += p->sBoard[x][y];
194 if (abs(iSum) == BOARD_SIZE) goto winner;
197 for (y = 0; y < BOARD_SIZE; y++)
201 for (x = 0; x < BOARD_SIZE; x++)
203 iSum += p->sBoard[x][y];
205 if (abs(iSum) == BOARD_SIZE) goto winner;
209 for (x = 0; x < BOARD_SIZE; x++)
211 iSum += p->sBoard[x][x];
213 if (abs(iSum) == BOARD_SIZE) goto winner;
216 for (x = 0; x < BOARD_SIZE; x++)
218 iSum += p->sBoard[x][(BOARD_SIZE - 1 - x)];
220 if (abs(iSum) == BOARD_SIZE) goto winner;
223 if (p->uNumEmpty == 0)
233 *psWhoWon = (iSum / BOARD_SIZE);
234 ASSERT(X_OR_O(*psWhoWon));
239 //+----------------------------------------------------------------------------
241 // Function: MakeMove
243 // Synopsis: Make a move on a board
245 // Arguments: IN OUT POSITION *p - the board
246 // IN MOVE *m - the move
250 //+----------------------------------------------------------------------------
251 void MakeMove(IN OUT POSITION *p, IN MOVE *m)
253 if (TRUE == IsLegalMove(p, m))
255 ASSERT(p->sBoard[m->cVpos][m->cHpos] == EMPTY);
256 p->sBoard[m->cVpos][m->cHpos] = m->sMark;
258 ASSERT(p->uNumEmpty < (BOARD_SIZE * BOARD_SIZE));
259 p->sWhoseTurn = OPPOSITE_MARK(p->sWhoseTurn);
265 //+----------------------------------------------------------------------------
267 // Function: UnmakeMove
269 // Synopsis: The opposite of MakeMove
271 // Arguments: IN OUT POSITION *p - the board
272 // IN MOVE *m - the move to undo
276 //+----------------------------------------------------------------------------
277 void UnmakeMove(IN OUT POSITION *p, IN MOVE *m)
279 if (p->sBoard[m->cVpos][m->cHpos] == m->sMark)
281 p->sBoard[m->cVpos][m->cHpos] = EMPTY;
283 ASSERT(p->uNumEmpty > 0);
284 ASSERT(p->uNumEmpty <= (BOARD_SIZE * BOARD_SIZE));
285 p->sWhoseTurn = OPPOSITE_MARK(p->sWhoseTurn);
293 AlphaBeta(IN POSITION *p, IN int iAlpha, IN int iBeta)
299 int iBestScore = -INFINITY;
304 // Evaluate this position
306 if (TRUE == GameOver(p, &sWhoWon))
308 if (sWhoWon == p->sWhoseTurn)
310 return(+INFINITY - g_uPly);
312 else if (sWhoWon == (p->sWhoseTurn * -1))
314 return(-INFINITY + g_uPly);
320 // No one won, game is still going. Evaluate every
321 // possible move from here.
323 ASSERT(p->uNumEmpty > 0);
324 for (s = 0; s < (BOARD_SIZE * BOARD_SIZE); s++)
326 mv.cHpos = NUM_TO_HPOS(s);
327 mv.cVpos = NUM_TO_VPOS(s);
328 mv.sMark = p->sWhoseTurn;
330 if (IsLegalMove(p, &mv))
334 iScore = -1 * AlphaBeta(p, -iBeta, -iAlpha);
343 if (iScore > iBestScore)
363 SimpleSearch(IN POSITION *p)
369 int iBestScore = -INFINITY;
374 // Evaluate this position
376 if (TRUE == GameOver(p, &sWhoWon))
378 if (sWhoWon == p->sWhoseTurn)
380 return(+INFINITY - g_uPly);
382 else if (sWhoWon == (p->sWhoseTurn * -1))
384 return(-INFINITY + g_uPly);
390 // No one won, game is still going. Evaluate every
391 // possible move from here.
393 ASSERT(p->uNumEmpty > 0);
394 for (s = 0; s < (BOARD_SIZE * BOARD_SIZE); s++)
396 mv.cHpos = NUM_TO_HPOS(s);
397 mv.cVpos = NUM_TO_VPOS(s);
398 mv.sMark = p->sWhoseTurn;
400 if (IsLegalMove(p, &mv))
404 iScore = -1 * SimpleSearch(p);
405 if (iScore > iBestScore)
420 //+----------------------------------------------------------------------------
422 // Function: SearchForComputerMove
424 // Synopsis: Use our sophisticated search algorithm to find a computer
427 // Arguments: IN POSITION *p - the current board
428 // OUT MOVE *m - the move the computer chooses; this move struct
429 // is populated as a side-effect of this function.
431 // Returns: void* (populates move struct)
433 //+----------------------------------------------------------------------------
434 void SearchForComputerMove(IN POSITION *p, OUT MOVE *m)
438 #if defined(PLAY_RANDOMLY)
441 x = rand() % (BOARD_SIZE * BOARD_SIZE);
442 m->cHpos = NUM_TO_HPOS(x);
443 m->cVpos = NUM_TO_VPOS(x);
444 m->sMark = g_sComputerPlays;
446 while(FALSE == IsLegalMove(p, m));
447 #elif defined(SIMPLE_SEARCH)
448 g_uPly = g_uNodes = 0;
451 printf("Searched %u node(s).\n", g_uNodes);
452 #elif defined(ALPHA_BETA_SEARCH)
453 g_uPly = g_uNodes = 0;
454 AlphaBeta(p, -INFINITY-1, +INFINITY+1);
456 printf("Searched %u node(s).\n", g_uNodes);
458 #error "No Search Strategy Defined"
462 //+----------------------------------------------------------------------------
466 // Synopsis: The program entry point and main game loop.
472 //+----------------------------------------------------------------------------
480 // Randomize: the random numbers returned by rand() will be based on
481 // the system clock when the program starts up.
486 // Setup the board and draw it once.
497 // See whose turn it is -- the human's or the computers -- and
498 // get a move from whoever's turn it is.
500 if (p.sWhoseTurn == g_sComputerPlays)
502 SearchForComputerMove(&p, &mv);
506 GetHumanMove(&p, &mv);
510 // Make the move on the board and draw the board again.
515 while(FALSE == GameOver(&p, &sResult));
518 // If we get here the game is over... see what happened.
523 printf("\nX's win.\n");
526 printf("\nO's win.\n");
529 printf("Tie (what a surprise)\n");