int index, trys, ttWin;
int OppWin= -3 * MySide;
int MyWin= 3 * MySide;
for(index=0; index<9;index++) for(trys=0; trys<2; trys++){
if(level>0) Copy_ttt(level);
if(ttt[level][index] == 0){ // empty space if(trys ==0)ttt[level][index]= MySide;
else ttt[level][index]=-MySide; // opponent
ttWin= ttt[level][wp[index][0] + ttt[level][wp[index][1] + ttt[level][wp[index][2];
if(ttWin == MyWin)return(100 + index); // I win plus space to move to else
if(ttWin == OppWin)return(-(100 + index)); // Forced plus space to block }
return 0; // No forced move }
void Tic_Tac_Toe() {
int x=0, MySide= 1, spaceon;
while(SpaceEmpty(0) && x < 100){ // Game still on x= Forced_Move(1, MySide);
spaceon= abs(x)%100;
if(x != 0)ttt[0][spaceon]= x/100; // either a '1' or a '-1' else{ // find a space that's not forced
BestMove(1); // find the best move }
x= 0; // reset 'x' }
}
(This code can be found on the book’s companion CD.)
Visual C++ Language: Code for the AI Logic of Tic-Tac-Toe
#include "stdafx.h"
#define IDB_SMALLX 144
#define IDB_BOARD 145
// extern(al) variables or variables that are defined in another file extern CString m_msg1,m_msg2,m_moves;
extern HINSTANCE m_hInstance;
extern int Setbuttons;
extern HDC my_hDC;
// Set the Global variables
char Path[100];
int ttt[10][9], wp[8][3], BigBoxXY[9][3], SmBoxXY[9][3];
int playerMark, playerDONE, PmouseX, PmouseY;
char squote[2]={(char)39,(char)0}; // ' single quote
HBITMAP markX, markO, smarkx, smarko;
HBITMAP BigBoard, SmBoard;
// Initialize the Tic-Tac-Toe boards (all levels) to Zeor for(i = 0; i <= 9; i++)
for(j = 0; j <= 8; j++) ttt[ i ][ j ] = 0;
// We start the arrays at Zero to match our C-code // Rows
// Define our two Tic-Tac-Toe boards
Chapter 11
int xoff1= 260,xoff2=40,yoff1=6,yoff2=210;
setRandom(); // initialize the Random number function }
/////////////////////////////////////////////////////////////////////
// Copy a new Tic-Tac-Toe board starting with the previous board
void Copy_ttt(int level) {
int index;
if( level < 1)return;
if( level > 8)return;
Clevel = level; // for small tic-tac-toe board
for( index = 0; index <= 8;index++)
ttt[ level ][ index ] = ttt[ level – 1 ][ index ];
}
/////////////////////////////////////////////////////////////////////
// Let's verify if there's an empty space to fill
int SpaceEmpty(int level) {
int index;
for(index = 0; index <= 8; index++) if( ttt[level][index] == 0)
return( index + 1 );
return(0); //no empty space was found }
/////////////////////////////////////////////////////////////////////
// A function to check if the current board position is a winning // one for either player
int IsThereaWin(int level, int MySide) {
int ttWin, OppWin, MyWin, wpindex, i;
OppWin = -3 * MySide;
MyWin = 3 * MySide;
for(wpindex = 0; wpindex <= 7; wpindex++){ // for each win (3 rows, 3 columns and 2 diagonals) ttWin = 0;
for(i = 0; i<= 2; i++) // add the 3 spaces as per direction to check
ttWin = ttWin + ttt[level][wp[wpindex][i]];
if( ttWin == MyWin) // I have 3 connecting marks return(100); // I win plus space to move to
if( ttWin == OppWin) // Opponent can get 3 connecting marks return(-100); // forced plus space to block
}
return(0);
}
/////////////////////////////////////////////////////////////////////
// Let's check to see if there's a forced move to make
// A forced move is a winning space or a blocking space to stop // a loss
int forced_Move(int level, int MySide) {
int index, trys, OppWin, MyWin, FMove, sgn1;
OppWin = -3 * MySide;
MyWin = 3 * MySide;
for( trys = 0; trys<= 1; trys++) for( index = 0; index <= 8; index++){
if( level > 0 )Copy_ttt (level);
if( ttt[level][index] == 0 ){ // empty space
if( trys == 0 ) // Can we win on this turn ttt[level][index] = MySide;
else // Can our opponent win if we don’t block ttt[level][index] = -MySide; // opponent
FMove = IsThereaWin(level, MySide);
if( FMove != 0 ){
sgn1 = 1;
if( FMove < 0 ) sgn1 = -1;
if( level > 0 ) Copy_ttt (level);
return (sgn1 * (abs(FMove) + index + 1));
} } }
if( level > 0 ) Copy_ttt (level);
return(0); //No forced move
}
/////////////////////////////////////////////////////////////////////
Chapter 11
void Tic_Tac_Toe() {
int X, MySide, spaceon, FMove, i;
X = 0;
MySide = -playerMark;
if( SpaceEmpty(0) > 0 ){ // Game still on
// A forced move to win or to block a win is the best mark move
X = forced_Move(1, MySide);
spaceon = abs(X) % 100; // -100 or +100 plus space to mark
if( X != 0 ){ // A forced move has been calculated ttt[0][spaceon - 1] = MySide; // either a '1' or a '-1' m_moves=_T(" I marked space ");
m_moves+=REPitoa(spaceon,2);
} // X != 0
//find a space that’s not forced if( X == 0 ){
// Initialize the level 1 available mark spaces to an extremely // low value
for( i = 0; i <= 9; i++) Level1Data[i] = -999;
// Call the Min-Max Function where Alpha is very low and Beta is // very high
X = MinMaxValue(1, MySide, -999, 999); // find the best move
// find the best space to mark from the level 1 list of valid spaces // to mark
spaceon = SpaceEmpty(0) - 1;
X = -999;
for( i = 1; i <= 9; i++) if( Level1Data[i] > X ){
spaceon = i;
X = Level1Data[i];
}
ttt[ 0 ][ spaceon – 1 ] = MySide; // Mark the best space
m_moves+=_T(" I marked space ");
m_moves+=REPitoa(spaceon,2);
} // X == 0
m_msg2=_T("Marked ");
m_moves+=REPitoa(spaceon,2);
show_boards(); // display current path scenario of board } // SpaceEmpty(0) > 0
show_boards();
playerDONE = 0;
FMove = IsThereaWin(0, playerMark);
if( FMove <= -100 ){ // Be prepared to see this message often m_moves+=_T(" HURRAY! I WON!");
m_moves+=_T(" Play me again?");
m_moves+=_T("Would you like to play me again?");
playerDONE = 100;
Setbuttons= -2; // Reset_Buttons return;
} // FMove <= -100
if( FMove >= 100 ){ // You may never see this message m_moves+=_T(" WOW! YOU");
m_moves+=squote; // Place a ' within " "
m_moves+=_T("VE WON!");
m_moves+=_T(" Play me again?");
m_moves+=_T("Would you like to play me again?");
playerDONE = 100;
m_msg1=_T("Your turn, please mark a space");
if( SpaceEmpty(0) == 0 ){ // no more empty spaces to mark m_moves=_T(" DRAW!");
m_moves=_T(" Play me again?");
m_moves=_T("Would you like to play me again?");
Setbuttons= -2; // Reset_Buttons
// Just like a chess opening database, here are standard // Tic-Tac-Toe opening marks
void opening() {
int i, j, k;
m_moves=_T("");
// Check to see if we go first and second j = 0;
Tic_Tac_Toe(); // find a space to mark return;
Chapter 11
}
if( j == 0 ){ // we go first
i = (int)(Random(5)); // valid first marks are spaces 1,3,5,7 // and 9
m_msg1=_T("Your turn, please mark a space"); // Player goes return;
} // j == 0
if( j == 1 ){ // we go second
// valid moves are center and the opposite corner if the opponent // marked a corner space
i = -1; // a flag
if( k == 4 && i == -1 ){ // center space marked
i=4 ; // can't mark the center space
while (i ==4)
i = (int)(Random(4)); // valid first marks are spaces 1,3,7 // and 9
m_moves=_T(" I marked space ");
m_moves+=REPitoa(i + 1,2);
playerDONE = 0;
show_boards();
m_msg1=_T("Your turn, please mark a space"); // Player goes return;
} }
/////////////////////////////////////////////////////////////////////
int MinMaxValue(int level, int MySide, int alpha, int beta) {
int WIN, index, nextspace, X, succval;
long MTlist;
// reset current level board Copy_ttt (level);
if( SpaceEmpty(level) == 0 ) return( 0 );
// find a space that’s not forced // list all open spaces
MTlist = 0; // List all the open (unmarked) spaces on this level
X = forced_Move(level, MySide); // Is there a forced move (a win // or a block of a win)
if( X != 0 ) // a forced move has been flagged MTlist = abs(X % 100); // space is 1 to 9
else{
for( index = 0; index <= 8; index++)
if( ttt[level][8 - index] == 0 ) // subtract from 8 for // ascending order MTlist = MTlist * 10 + (9 - index);
} // else
while (MTlist > 0){
nextspace = MTlist % 10; // next space to mark MTlist = (MTlist - nextspace) / 10; // remainder of possible
// spaces to mark
Copy_ttt (level);
ttt[level][nextspace - 1] = MySide;
m_msg2=_T("Marked ");
m_moves+=REPitoa(nextspace,2);
show_boards();
WIN = IsThereaWin(level, MySide);
if( WIN >= 100 ) succval = WIN;
else
succval = -MinMaxValue(level + 1, -MySide, -beta, -alpha);
if( level == 1 ){ // Save this space’s value Level1Data[ nextspace ] = succval;
if( succval == 100) return succval; // Winning line found
} // level == 1
// The Alpha-Beta pruning code
if( succval >= beta ) return( beta );
if( succval > alpha ) alpha = succval;
} // While
return( alpha ); // return a draw
}
/////////////////////////////////////////////////////////////////////
// Convert as number to a character array with a length of lval1 // (may have preceding zeroes)
char *REPitoa(long val1, int lval1) {
long x;
int sgn=1, i, j, k, zeroflag=0;
Chapter 11
static char strx[10];
x= val1;
if((val1 == 0) && (lval1 == 99)) return("");
if(lval1 == 99) lval1= 0;
if(val1 < 0){sgn= -1; x= -val1;} // flag and set to positive if(lval1 < 0){
zeroflag= 1; // preceding zero
lval1= -lval1;
if((val1 == 0) && (lval1 == 0)) lval1=1;
if(lval1 > 9)lval1= 9; // max length for(i=0; i<10; i++) strx[i]= (char)0; // end string if(x == 0){
strx[lval1 - (1 + i)]= (char)32; // blank left zeroes x=x / 10; // must be after the "if"
playerDONE = 1; // Computer goes first
if( buttonid < 2){ // Player’s turn m_msg1=_T("Your turn, please mark a space");
playerDONE = 0;
if (playerDONE == 1)return;
flag = -1;
// check the area the player has selected to mark for (i = 0; i < 9; i++)
if ((PmouseX >= (BigBoxXY[i][0])) && (PmouseX <= (BigBoxXY[i][0]
+ 120)))
if ((PmouseY >= (BigBoxXY[i][1])) && (PmouseY <=
(BigBoxXY[i][1] + 95)))
// Seed the random-number generator with current time so that // the numbers will be different every time we run.
if(randseed == 0){
::StretchBlt(my_hDC, 260, 6, 367, 293, hMemDC, 0, 0, 367, 293, SRCCOPY);
for (i = 0; i<9; i++){
x= BigBoxXY[i][0]; y= BigBoxXY[i][1];
if (ttt[ 0 ][ I ] == 1) // markX
::StretchBlt(my_hDC, x, y, 113, 90, hMemDCX, 0, 0, 114, 90, SRCCOPY);
else
if (ttt[ 0 ][ I ] == -1) // markO
::StretchBlt(my_hDC, x, y, 114, 90, hMemDCO, 0, 0, 114, 90, SRCCOPY);
}
Clevel = 0;
DeleteDC(hMemDCO);
DeleteDC(hMemDCX);
DeleteDC(hMemDC);
}
/////////////////////////////////////////////////////////////////////
void showSmallBoard() {
int i,x,y;
HDC hMemDCo= ::CreateCompatibleDC(NULL);
HDC hMemDCx= ::CreateCompatibleDC(NULL);
HDC hMemDC = ::CreateCompatibleDC(NULL);
SelectObject(hMemDCo, smarko);
SelectObject(hMemDCx, smarkx);
SelectObject(hMemDC, SmBoard);
::StretchBlt(my_hDC, 40, 210, 64, 64, hMemDC, 0, 0, 64, 64, SRCCOPY);
for (i = 0; i<9; i++){
x= SmBoxXY[ i ][ 0 ]; y= SmBoxXY[ i ][ 1 ];
if (ttt[ Clevel ][ i ] == 1) // smarkX
::StretchBlt(my_hDC, x, y, 114, 90, hMemDCx, 0, 0, 114, 90, SRCCOPY);
else
if (ttt[ Clevel ][ i ] == -1) // smarkO
::StretchBlt(my_hDC, x, y, 18, 20, hMemDCo, 0, 0, 18, 20, SRCCOPY);
}
DeleteDC(hMemDCo);
DeleteDC(hMemDCx);
DeleteDC(hMemDC);
}