// A Simple Checkers Program. // by Jonathan Kreuzer // jkreuzer@3dkingdoms.com // I compiled it with MSVC++ 6.0, haven't tested it with other compilers #include #include #include #include #include #include #include #define WHITE 2 #define BLACK 1 // Black == Red #define EMPTY 0 #define BPIECE 1 #define BKING 2 #define WPIECE 3 #define WKING 4 #define NUMBLACK 64 #define NUMWHITE 65 #define NUMMOVES 0 #define NUMJUMPMOVES 1 #define EVAL 66 #define ALL 255 #define NONE 255 #define KING 18 #define TIMEOUT 31000 #define HASHTABLESIZE 250000 // 2.5 megabytes, 250,000 entries #define MAXSECONDS 3 // The number of seconds the computer is allowed to search char g_CBoard [68], KBoard[68], UndoBoard[68]; unsigned char Movelist[36][66]; char Boardlist[36][68]; clock_t starttime, endtime; int HashFunction [85][6]; int HashFunction2 [85][6]; int hashing = 1, BoardFlip = 1; int nodes, nodes2, SearchDepth; void inline FindMovesBlack ( char board[], int src, unsigned char CMovelist[]); void inline FindMovesWhite ( char board[], int src, unsigned char CMovelist[]); int GetPlayerMove(char Board[], int djump, char cPlayerColor ); // // ---------- TRANSPOSITION TABLE FUNCTIONS ------------- // struct TEntry { // FUNCTIONS public: void inline Read ( unsigned long CheckSum, short alpha, short beta, int &bestmove, int &value, int depth) { if (m_checksum == CheckSum ) //To almost totally sure these are really the same position. { // Get the Value if the search was deep enough, and bounds usable if (m_depth >= depth) { if (m_failtype == 0) value = m_eval; // True Value else if (m_failtype == 1 && m_eval <= alpha) value = m_eval; // Alpha Bound else if (m_failtype == 2 && m_eval >= beta) value = m_eval; // Beta Bound } // Otherwise take the best move from Transposition Table bestmove = m_bestmove; } } void inline Write ( unsigned long CheckSum, short alpha, short beta, int &bestmove, int &value, int depth) { m_checksum = CheckSum; m_eval = value; if (value <= alpha) m_failtype = 1; else if (value >= beta) m_failtype = 2; else m_failtype = 0; m_depth = depth; m_bestmove = bestmove; } // DATA private: unsigned long m_checksum; char m_depth, m_failtype; short m_eval; short m_bestmove; }; TEntry *TTable = (TEntry *) malloc( sizeof (TEntry) * HASHTABLESIZE + 2); // ------------------ // Hash Board // // The Hash Value really should be updated incrementally with moves (much faster.) Instead Hash_Board is called each time // to get the hash value of the board. // ------------------ void Create_HashFunction ( void ) { for (int i = 0; i <65; i++) for (int x = 0; x < 5; x++) {HashFunction [i][x] = rand() + (rand()*256) + (rand()*65536); HashFunction2 [i][x] = rand() + (rand()*65536) + (rand()*256); } } unsigned long Hash_Board (char board[], char color, unsigned long &CheckSum) {unsigned long sum = 0; CheckSum = 0; int x, y, index; for (y = 0; y < 8; y++) {if ( (y&1) == 0) x = 1; else x = 0; for (; x < 8; x+=2) { index = x + (y<<3); sum += HashFunction [ index ][ board [ index ] ]; CheckSum += HashFunction2 [ index ][ board [ index ] ]; } } if (color == BLACK) {sum += HashFunction [64][ 3 ]; CheckSum += HashFunction2 [64][ 3 ]; } return sum; } // ==================================================== // Setup the Board in the starting position // void Setup_Board (char board[]) {int i, y, x; for (i = 0; i< 63; i++) board [i] = EMPTY; for (i = 1; i< 8; i+=2) {board [i] = BPIECE; board [i+16] = BPIECE; } for (i = 8; i < 16; i+=2) {board [i] = BPIECE; } for (i = 49; i < 56; i+=2) {board [i] = WPIECE; } for (i = 56; i < 64; i+=2) {board [i] = WPIECE; board [i - 16] = WPIECE; } board[NUMBLACK] = 12; board[NUMWHITE] = 12; board[EVAL] = 0; // King Evaluation of the Board // (I don't know if king centralization is actually good, but this might make it actually do something in endgames.) for (i = 0; i< 64; i++) { x = i & 7; y = (i >> 3); if ( x > 2 && x < 5 && y > 2 && y < 5) KBoard[i] = 2; else if ( x > 1 && x < 6 && y > 1 && y < 6) KBoard[i] = 1; else KBoard[i] = 0; } } // ----------------- // Copy a Board onto another // ----------------- void Copy_Board (char DBoard[], char CBoard[]) { memcpy (DBoard, CBoard, 68); } // ------------------ // Evaluate Board // // I don't know much about checkers, this is the best I could do for an evaluation function // ------------------ int Evaluate_Board (char board[], int ahead, int extra) { register int eval = 0; // Game is over? if (board[NUMWHITE] == 0) return -2001 + ahead; if (board[NUMBLACK] == 0) return 2001 - ahead; // Number of pieces present. A ratio is used to encourage trading for the side that is up. eval = (board[NUMWHITE]-board[NUMBLACK]) * (55 + ( 20 - board[NUMWHITE] - board[NUMBLACK] ) ); eval += (int)board[EVAL]; if ((int)board[EVAL]>128) eval-=256; eval +=extra; // Bonus for leaving pieces guarding the back row. if (board[1] == BPIECE) eval-=5; if (board[3] == BPIECE) eval-=5; if (board[5] == BPIECE) eval-=5; if (board[7] == BPIECE) eval-=3; if (board[56] == WPIECE) eval+=3; if (board[58] == WPIECE) eval+=5; if (board[60] == WPIECE) eval+=5; if (board[62] == WPIECE) eval+=5; // For near the end of the game, There are two corners where the losing side // can make it tough to for the opponent to win the game by moving a King there // where it can move back and forth on the 2 corner squares. if (eval > 8) {if (board[1] == BKING || board[8] == BKING) eval-=12; if (board[62] == BKING || board[55] == BKING) eval-=12; } if (eval < -8) {if (board[1] == WKING || board[8] == WKING) eval+=12; if (board[62] == WKING || board[55] == WKING) eval+=12; } return eval; } // ------------------ // Draw Board // ------------------ void Draw_Board (char board[]) { int i, start = 63, add = -1, add2 = 0; COORD textp; HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); textp.X = 0;textp.Y = 0; SetConsoleCursorPosition(hConsole, textp); printf ("\n\n\n "); if (BoardFlip == 0) {start = 0; add = 1; add2 = 1;} // Print pieces and squares in ASCII for (i = start; i>=0 && i<=63; i+=add) { // piece if (board [i] == WPIECE) printf ("%cC%c ", char(219), char (219)); else if (board [i] == WKING) printf ("%cK%c ", char(219), char (219)); else if (board [i] == BPIECE) printf ("%cC%c ", char(177), char (177)); else if (board [i] == BKING) printf ("%cK%c ", char(177), char (177)); // empty square else if ( ((i%2 == 1) && (i/8)%2 == 1 ) || ((i%2 == 0) && (i/8)%2 == 0) ) printf (" . "); else printf (" * "); if ((i+add2)%8 == 0 && i!=0 && i!=63) printf ("\n\n "); } for (i = 0; i < 3; i++) printf (" \n"); textp.X = 0;textp.Y = 19; SetConsoleCursorPosition(hConsole, textp); } // Swap Moves at byte location a, b in CMovelist[] void inline SwapMoves (unsigned char CMovelist[], int a, int b) { unsigned char temp, temp2; temp = CMovelist[ a ]; temp2 = CMovelist[ a + 1 ]; CMovelist[ a ] = CMovelist[ b ]; CMovelist[ a + 1 ] = CMovelist[ b + 1]; CMovelist[ b ] = temp; CMovelist[ b + 1 ] = temp2; } // --------------------- // Execute a Move // --------------------- int DoMove (int Movenum, char board[], unsigned char CMovelist[], int human) {register int src, dst; int jumped, eval = 0; dst = CMovelist [ (Movenum<<1) + 1]; src = CMovelist [ Movenum << 1] & 63; // Move The Piece board [ dst ] = board [ src ]; board [ src ] = EMPTY; // Update King Evaluation incrementally if (board[ dst ] == BKING) board[EVAL] -= KBoard[ dst ] - KBoard [ src ]; else if (board[ dst ] == WKING) board[EVAL] += KBoard[ dst ] - KBoard [ src ]; // Bonus for advancing a checker if (board[ dst ] == WPIECE || board[ dst ] == BPIECE) eval = 3; // Jump opponent's piece? if (CMovelist [ Movenum << 1 ] > 100) { jumped = (dst + src) >> 1; if (board [ jumped ] == WPIECE ) {board[NUMWHITE]--;} else if (board [jumped ] == BPIECE) {board[NUMBLACK]--;} else if (board [jumped ] == BKING) {board[NUMBLACK]--; board[EVAL]+=KING;} else if (board [jumped ] == WKING) {board[NUMWHITE]--; board[EVAL]-=KING;} board [ jumped ] = EMPTY; // Check for Double Jump if (board [ CMovelist [ Movenum* 2 + 1] ] < 3) // BLACK { FindMovesBlack ( board, dst, &CMovelist[66]); if (CMovelist [1 + 66] != 0) if (human == 1) GetPlayerMove (board , 1, BLACK); else DoMove ( 1 , board, &CMovelist[66], human); } else { FindMovesWhite ( board, dst , &CMovelist[66] ); if (CMovelist [1 + 66] != 0) if (human == 1) GetPlayerMove (board , 1, WHITE); else DoMove ( 1 , board, &CMovelist[66], human); } } // King Me? if (dst > 55 && board [ dst ] == BPIECE) {board [ dst ] = BKING; board[EVAL]-=KING; } if (dst < 8 && board [ dst ] == WPIECE) {board [ dst ] = WKING; board[EVAL]+=KING; } return eval; } // --------------------------------------------- // Check Possiblity/Execute Move from one selected square to another // returns 0 if the move is not possible, otherwise 1 // --------------------------------------------- int SquareMove( char Board[], int x , int y, int xloc, int yloc, char PlayerCOLOR) {int i, src, dst; static unsigned char CMovelist[220]; if (PlayerCOLOR == BLACK) FindMovesBlack (Board, ALL , CMovelist); else FindMovesWhite (Board, ALL , CMovelist); for (i = 1; i <= CMovelist[0]; i++) {if (BoardFlip == 1) {src = (7-xloc + (7-yloc) * 8); dst = ((7-x) + (7-y) * 8); } else {src = xloc + yloc*8; dst = x + y*8; } if ( CMovelist[i * 2 + 1] == src && (CMovelist[i * 2 ]&63) == dst ) { Copy_Board ( UndoBoard, g_CBoard); DoMove ( i, Board, CMovelist, 1); return 1; } } return 0; } // ========================================= // Add a Move to Movelist // ========================================= void inline AddMove (int src, int dst, int jump, unsigned char CMovelist[]) {register int i; // Jump moves take precedence (ie. if a jump is present, write over non-jumps) if (jump == 1) {i = (CMovelist[ NUMJUMPMOVES ] << 1) + 2; CMovelist [ i ] = (char)src + 128; CMovelist [ i + 1] = (char)dst; CMovelist [ NUMJUMPMOVES ]++; CMovelist [ NUMMOVES ] = CMovelist[ NUMJUMPMOVES ]; // Number of Moves return; } // If this is not a Jump move if (CMovelist[ NUMJUMPMOVES ] != 0) return; // Ignore non-jumps if a jump move is present i = (CMovelist[ NUMMOVES ] << 1) + 2; CMovelist [ i ] = (char)(src); CMovelist [ i + 1] = (char)dst; CMovelist [ NUMMOVES ]++; // Number of Moves return; } // ====================================================== // Find the Moves available on board, and store them in Movelist // If src != 255, FindMoves will only check the src square // ====================================================== // for BLACK void inline FindMovesBlack ( char board[], int src, unsigned char CMovelist[]) {int x,y; register int square; // Clear the Move list CMovelist [ NUMJUMPMOVES ] = 0; CMovelist [ NUMMOVES ] = 0; // Now look for moves for (y = 0; y < 8; y++) {if ( (y&1) == 0) x = 1; else x = 0; for (; x < 8; x+=2) { if (src != 255) { x = src & 7; y = (src>>3); }// Set X,Y to one square to check square = x + (y << 3); if (board[ square ] == BKING || board[ square ] == BPIECE) { if ( x > 0 && y < 7 && board[square + 7 ] == 0 ) AddMove (square , square +7, 0, CMovelist); // Move else if ( x > 1 && y <6 && board[square + 14 ] == 0 && board[square + 7] >= 3) AddMove (square , square + 14, 1, CMovelist); // Jump if ( x < 7 && y < 7 && board[square + 9 ] == 0 ) AddMove (square , square + 9, 0, CMovelist); // Move else if ( x < 6 && y <6 && board[square + 18 ] == 0 && board[square + 9 ] >= 3) AddMove (square, square + 18, 1, CMovelist); // Jump } if (board[ square ] == BKING ) { if ( x<7 && y>0 && board[square - 7 ] == 0 ) AddMove ( square , square - 7, 0, CMovelist); // Move else if ( x < 6 && y > 1 && board[square - 14 ] == 0 && board[square - 7 ] >= 3) AddMove ( square , square - 14, 1, CMovelist) ; // Jump if ( x>0 && y>0 && board[square - 9 ] == 0 ) AddMove (square , square - 9, 0, CMovelist); // Move else if ( x > 1 && y >1 && board[square - 18 ] == 0 && board[square - 9 ] >= 3) AddMove (square , square - 18, 1, CMovelist); // Jump } if (src != 255) { x = 8; y = 8; } // Don't Loop } } } // for WHITE void inline FindMovesWhite ( char board[], int src, unsigned char CMovelist[]) {int x,y; register int square; // Clear the Move list CMovelist [ NUMJUMPMOVES ] = 0; CMovelist [ NUMMOVES ] = 0; // Now look for moves for (y = 0; y < 8; y++) {if ( (y&1) == 0) x = 1; else x = 0; for (; x < 8; x+=2) {if (src != 255) { x = src & 7; y = (src>>3); }// Set X,Y to one square to check square = x + (y << 3); if (board[ square ] == WKING || board[ square ] == WPIECE ) { if ( x < 7 ) if ( y>0 && board[square - 7 ] == 0 ) AddMove ( square , square - 7, 0, CMovelist); // Move else if ( x < 6 && y > 1 && board[square - 14 ] == 0 && board[square - 7 ] < 3) AddMove ( square , square - 14, 1, CMovelist) ; // Jump if ( x > 0 ) if ( y>0 && board[square - 9 ] == 0 ) AddMove (square , square - 9, 0, CMovelist); // Move else if ( x > 1 && y >1 && board[square - 18 ] == 0 && board[square - 9 ] < 3) AddMove (square , square - 18, 1, CMovelist); // Jump } if (board[ square ] == WKING) {if ( x > 0 ) if ( y < 7 && board[square + 7 ] == 0 ) AddMove (square , square +7, 0, CMovelist); // Move else if ( x > 1 && y <6 && board[square + 14 ] == 0 && board[square + 7] < 3) AddMove (square , square + 14, 1, CMovelist); // Jump if ( x < 7 ) if ( y < 7 && board[square + 9 ] == 0 ) AddMove (square , square + 9, 0, CMovelist); // Move else if ( x < 6 && y <6 && board[square + 18 ] == 0 && board[square + 9 ] < 3) AddMove (square, square + 18, 1, CMovelist); // Jump } if (src != 255) { x = 8; y = 8; } // Don't Loop } } } // =============================================== // Computer Search // Alpha Beta Search with Transposition(Hash) Table // =============================================== short ABSearch(char color, int ahead, short alpha, short beta, int temp2, int &bestmove) { char ocolor; short Max, i, temp; short Move; // number of best move int value, smax, oldbest; unsigned long indexTT, checksumTT; nodes++; // Check to see if move time has run out every 100,000 nodes if (nodes > nodes2 + 100000 ) { endtime = clock (); nodes2 = nodes; if (endtime - starttime > CLOCKS_PER_SEC * MAXSECONDS && SearchDepth > 12) return TIMEOUT; } // Find possible moves (and set a couple variables) if (color == WHITE) { ocolor = BLACK; smax = 1; FindMovesWhite(Boardlist[ahead], ALL, Movelist[ahead+1] ); } else { ocolor = WHITE; smax = -1; FindMovesBlack(Boardlist[ahead], ALL, Movelist[ahead+1] ); } // if bestmove is set, try Best Move first by swapping it with move #1, if (hashing == 1 && bestmove != NONE && bestmove <= Movelist[ahead+1][NUMMOVES] && bestmove > 0) { SwapMoves(Movelist[ahead+1], 2, bestmove*2); } oldbest = bestmove; ahead++; Max = short (-9999*smax); Move = 0; // If you can't move, you lose if (Movelist[ahead][NUMMOVES] == 0) {if (color == WHITE) return -2001+ahead; else return 2001 - ahead; } // Run through the movelist, doing each move for (i = 1; i <= Movelist[ahead][NUMMOVES]; i++) { // Reset the move board to the one it was called with, then do the move # i in the Movelist Copy_Board(Boardlist[ahead] , Boardlist[ahead-1]); temp = DoMove(i, Boardlist[ahead], Movelist[ahead], 0 ); // If the game is over, return value if (Boardlist[ahead][NUMWHITE] == 0 || Boardlist[ahead][NUMBLACK] == 0) {if (ahead == 1) DoMove(i, Boardlist[0], Movelist[ahead], 0 ); return Evaluate_Board (Boardlist[ahead], ahead, 0); } // If this is the max depth, evaluate the board position if (ahead >= SearchDepth) { // Quiescence search after jumps. ( Up to 8 ply further... I'm guessing it won't reach 8 jumps in a row anyway) if (ahead < SearchDepth + 8 && Movelist[ahead][NUMMOVES + 1] != 0) {bestmove = NONE; value = (ABSearch( ocolor , ahead , alpha, beta, temp2, bestmove) ); if (value == TIMEOUT) return value; } // Otherwise evaluate and return the value else {value = Evaluate_Board (Boardlist[ahead], ahead, temp2) ; // Evaluate the position nodes++; } } //If this isn't the max depth, continue to look ahead else {value = -9999; bestmove = NONE; if (ahead == 1) temp2 = temp; // Look up the this Position in the Transposition Table if (ahead < (SearchDepth - 3) && hashing == 1 ) { indexTT = Hash_Board ( Boardlist[ahead], color, checksumTT ) % HASHTABLESIZE; TTable [ indexTT ].Read ( checksumTT, alpha, beta, bestmove, value, SearchDepth - ahead); } // If value wasn't set from the Transposition Table, look ahead further with a recursive call if (value == -9999) { value = ABSearch( ocolor , ahead , alpha, beta, temp2, bestmove); if (value == TIMEOUT) return value; // Store for the search info for this position in the Transposition Table if (ahead < (SearchDepth - 3) && hashing == 1) { TTable [ indexTT ].Write ( checksumTT, alpha, beta, bestmove, value, SearchDepth - ahead); } } } // Keep Track of Best Move and Alpha-Beta Prune if (smax==1 && value > Max ) { Max = value; Move = i; if (Max > alpha) alpha = Max; if (ahead!= 1 && alpha >= beta ) return beta; } if (smax==-1 && value < Max ) { Max = value; Move = i; if (Max < beta) beta = Max; if (ahead!=1 && alpha >= beta ) return alpha; } } // end for // Store the Best Move if (oldbest == NONE) bestmove = Move; else if (Move == 0) bestmove = NONE; else if (Move == 1) bestmove = oldbest; else if (Move == oldbest) bestmove = 1; // If the search is done (ahead == 1) , do the move if (ahead == 1) {DoMove(Move, Boardlist[0], Movelist[ahead], 0 ); return Max;} return Max; } //-===========================- // Retrieve Move from Keyboard Input //-===========================- int GetPlayerMove(char Board[66], int djump, char cColor ) { int in, in2, moved = 0; static short xloc = 0, yloc = 0; int x, y; COORD textp; HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); textp.X = xloc*4+1; textp.Y = yloc*2+3; SetConsoleCursorPosition(hConsole, textp); printf (">\n"); if (djump == 1) { moved = 1; x = xloc; y = yloc; textp.X = xloc*4+5;textp.Y = yloc*2+3; SetConsoleCursorPosition(hConsole, textp); printf ("<"); } while (moved < 2) { in = getch(); if (kbhit()) in2 = getch(); textp.X = xloc*4+1;textp.Y = yloc*2+3; SetConsoleCursorPosition(hConsole, textp); if (y!=yloc || x+1!=xloc || moved == 0) printf (" \n"); else printf ("<\n"); switch (in) { case 'f': if (BoardFlip == 0) BoardFlip = 1; else BoardFlip = 0; moved = 0; Draw_Board (g_CBoard); break; case 'q': return 14; case 'h': if (hashing == 0) hashing = 1; else hashing = 0; break; case 'u': Copy_Board (g_CBoard , UndoBoard); Copy_Board (Board , UndoBoard); Draw_Board (g_CBoard); break; case 75 : xloc--; if (xloc < 0) xloc = 0; break; case 80 : yloc++; if (yloc > 7) yloc = 7; break; case 77 : xloc++; if (xloc > 7) xloc = 7; break; case 72 : yloc--; if (yloc < 0) yloc = 0; break; case 0 : switch (in2) {case 75 : xloc--; if (xloc < 0) xloc = 0; break; case 80 : yloc++; if (yloc > 7) yloc = 7; break; case 77 : xloc++; if (xloc > 7) xloc = 7; break; case 72 : yloc--; if (yloc < 0) yloc = 0; break; } break; case 13 : if (moved == 0 && ((Board[(7-xloc) + (7-yloc)*8] !=0 && BoardFlip == 1 ) || (Board[xloc + yloc*8] !=0 && BoardFlip == 0 ))) {moved = 1; x = xloc; y = yloc; textp.X = xloc*4+5; textp.Y = yloc*2+3; SetConsoleCursorPosition(hConsole, textp); printf ("<\n"); } // Select Piece else if (moved == 1 && x == xloc && y == yloc && djump!=1) {moved = 0; textp.X = xloc*4+5; textp.Y = yloc*2+3; SetConsoleCursorPosition(hConsole, textp); printf (" \n"); } // Deselect Piece else if (moved ==1 && SquareMove( Board, x , y , xloc, yloc, cColor) == 1) {moved = 2; return 1; }// Move the Piece break; }// end switch textp.X = xloc*4+1;textp.Y = yloc*2+3; SetConsoleCursorPosition(hConsole, textp); printf (">\n"); } // end while return 0; } // =============================================== // Print some info on the search just completed for the user // =============================================== void DisplaySearchInfo ( int SearchDepth, int Eval, int nodes, int nMoves ) { int nps = 0; // Display Search/Game Info printf("\nMove: %d Red: %d White: %d \n", nMoves, (int)g_CBoard[NUMBLACK], (int)g_CBoard[NUMWHITE]); printf("Eval: %d KNodes: %d \n",(Eval - 2 ), (nodes/1000) ); nps = int (float(nodes) / float (endtime - starttime) * CLOCKS_PER_SEC / 1000); if (endtime == starttime) nps = 0; printf("Depth: %d+q KN/Sec: %d \n",(SearchDepth-2 ), nps ); } // =============================================== // The computer calculates a move then updates g_CBoard. // =============================================== void ComputerMove (int move, char cColor) { int LastEval = 0, Eval; int bestmove = NONE; char TempBoard[68]; // Do the first move randomly to add some variation. if (move == 1) {Copy_Board (TempBoard, g_CBoard); FindMovesBlack(TempBoard, ALL, Movelist[0] ); DoMove(rand() % 7 + 1, TempBoard, Movelist[0], 0 ); SearchDepth = 2; } // Otherwise minimax search else {nodes = 0; nodes2 = 0; starttime = clock (); endtime = starttime; SearchDepth = 6; Eval = 0; // Iteratively deepen until until the computer runs out of time, or reaches 22 ply while ( SearchDepth < 22 && Eval!=TIMEOUT) { SearchDepth +=2; printf ("%i, ", SearchDepth); printf ("", SearchDepth); Copy_Board (Boardlist[0], g_CBoard); Eval = ABSearch (cColor, 0, -3000, 3000, 99, bestmove); if (Eval!=TIMEOUT) {LastEval = Eval; Copy_Board (TempBoard, Boardlist[0]); } // Check if there is only one legal move, if so don't keep searching if (cColor == BLACK) FindMovesBlack(g_CBoard, ALL, Movelist[0] ); if (cColor == WHITE) FindMovesWhite(g_CBoard, ALL, Movelist[0] ); if (Movelist[0][NUMMOVES] == 1) Eval = TIMEOUT; } } Copy_Board ( g_CBoard, TempBoard); Draw_Board ( g_CBoard ); if (g_CBoard[NUMBLACK] != 0 && g_CBoard[NUMWHITE] != 0) DisplaySearchInfo ( SearchDepth, LastEval, nodes, move); } // =============================================== // MAIN // =============================================== void main ( void ) { int move; char compColor, mColor, cKey; Create_HashFunction ( ); do { move = 0; mColor = BLACK; compColor = BLACK; Setup_Board ( g_CBoard ); Draw_Board ( g_CBoard ); printf ("Do you want to go first? (y/n) "); cKey = getch(); if (cKey == 'y' || cKey == 'Y') compColor = WHITE; starttime = clock (); srand(starttime); for (int x = 0; x < 2000; x++) { move++; // Get the move ( from the computer or player) if (compColor == mColor) ComputerMove ( move, compColor ); else { if (GetPlayerMove( g_CBoard , 0, mColor) == 14) x = 2000; Draw_Board ( g_CBoard ); } if (mColor == WHITE) mColor = BLACK; else mColor = WHITE; // Check for game over if (g_CBoard[NUMBLACK] == 0) {printf ("(WHITE WINS) "); x = 2000;} if (g_CBoard[NUMWHITE] == 0) {printf ("(RED WINS) "); x = 2000;} } printf ("\nDo you want to play again? (y/n) "); cKey = getch(); } while (cKey == 'y' || cKey == 'Y'); }