//-------------------------------------------------------------------------------------- // ChessScoreKeeper.cpp // // This file contains the source to keep the position of a chess game, // to enforce legal moves, and to return the game status (mate, stalemate, etc). // // Author - Michael Keating //-------------------------------------------------------------------------------------- #include //#include #include #include #include #include #include #include #include //#define MY_DEBUG #define TRUE 1 #define FALSE 0 //***************************************************************** // // ChessScoreKeeper::ChessScoreKeeper() // // Parameters: // // none // // Description: // // Just set the bGameIsInProgress flag to FALSE since a game // is only started with newGame(); // // //***************************************************************** ChessScoreKeeper::ChessScoreKeeper() { bGameIsInProgress = FALSE; intMoveNumber = 0; } //***************************************************************** // // ChessScoreKeeper::~ChessScoreKeeper() // // Parameters: // // none // // Description: // // Free any allocated memory // //***************************************************************** ChessScoreKeeper::~ChessScoreKeeper() { FreeMoves(); vPositions.clear(); } //***************************************************************** // // void ChessScoreKeeper::newGame() // // Parameters: // // none // // Description: // // This method starts a new game for this object. // //***************************************************************** void ChessScoreKeeper::newGame() { // set up the starting position char newGamePosition[] = {'R','N','B','K','Q','B','N','R', 'P','P','P','P','P','P','P','P', '-','-','-','-','-','-','-','-', '-','-','-','-','-','-','-','-', '-','-','-','-','-','-','-','-', '-','-','-','-','-','-','-','-', 'p','p','p','p','p','p','p','p', 'r','n','b','k','q','b','n','r'}; memcpy(&position[0], &newGamePosition[0], 64); // initialize this object's variables bGameIsInProgress = TRUE; bIsWhitesMove = TRUE; bWhiteCantKCastle = FALSE; bWhiteCantQCastle = FALSE; bBlackCantKCastle = FALSE; bBlackCantQCastle = FALSE; bEnPassent = FALSE; intLastSource = 0; intLastDest = 0; cLastPiece = '-'; if (intMoveNumber) { FreeMoves(); vPositions.clear(); } intMoveNumber = 0; } //***************************************************************** // // int ChessScoreKeeper::isGameInProgress() // // Parameters: // // none // // Returns: // // TRUE if a game is in progress for this object, FALSE otherwise // // Description: // // Let's the caller know if a game is in progress for this object // //***************************************************************** int ChessScoreKeeper::isGameInProgress() { return bGameIsInProgress; } //***************************************************************** // // int ChessScoreKeeper::isWhitesMove() // // Parameters: // // none // // Returns: // // TRUE if white is on move, FALSE otherwise // // Description: // // Let's us know whose move it is // //***************************************************************** int ChessScoreKeeper::isWhitesMove() { return bIsWhitesMove; } //***************************************************************** // // void ChessScoreKeeper::gameOver(int Result) // // Parameters: // // intResult - the game result according to the following: // 0 - draw, 1 - black won, 2 - white won // // Returns: // // nothing // // Description: // // Let's the caller know if a game is in progress for this object // //***************************************************************** void ChessScoreKeeper::gameOver(int Result) { bGameIsInProgress = false; } //***************************************************************** // // int ChessScoreKeeper::move(char *lpszMove) // // Parameters: // // char *lpstMove - the chess move to make; such as "e2e4" // // Description: // // This method first checks to see if the given move is legal in the // the objects current position. If legal, the move is made and // the position is adjusted. // // Returns: // // 0 - lpszMove is illegal in the current position // 1 - lpszMove is legal // 2 - lpszMove is legal and the position is a stalemate // 3 - lpszMove is legal and white is mated // 4 - lpszMove is legal and black is mated // 5 - lpszMove is legal and we have a draw by repetition // // The board below shows the square numbers of our chess board // For Example: the square "e2" would be "11" // // The Board // // 1 0 1 2 3 4 5 6 7 // 2 8 9 10 11 12 13 14 15 // 3 16 17 18 19 20 21 22 23 // 4 24 25 26 27 28 29 30 31 // 5 32 33 34 35 36 37 38 39 // 6 40 41 42 43 44 45 46 47 // 7 48 49 50 51 52 53 54 55 // 8 56 57 58 59 60 61 62 63 // // H G F E D C B A // //***************************************************************** int ChessScoreKeeper::move(char *lpszMove) { // make sure the game is in progress if (!bGameIsInProgress) { return 0; } // make a local copy of lpszMove char lpMove[7]; strncpy(lpMove, lpszMove, 6); lpMove[5] = '\0'; // and convert it to lower case if (isupper(lpMove[0])) { lpMove[0] = tolower(lpMove[0]); } if (isupper(lpMove[2])) { lpMove[2] = tolower(lpMove[2]); } // find the source square int intFile = (lpMove[0] - 'h') * -1; int intRank = lpMove[1] - '1'; int intSource = (intRank * 8) + intFile; // now the destination square intFile = (lpMove[2] - 'h') * -1; intRank = lpMove[3] - '1'; int intDest = (intRank * 8) + intFile; // get the promotion piece - Q, R, N, B, or '\0'. char cPromotionPiece = toupper(lpMove[4]); int bIsCapture = position[intDest] != '-'; int intStatus = move(intSource, intDest, cPromotionPiece); int bIsMate = intStatus == 3 || intStatus == 4; if (intStatus == 1) { StoreMove(intSource, intDest, cPromotionPiece, bIsCapture, bIsMate); // check for three-fold repetition POSITION_T stPosition; memcpy(&stPosition.position[0], &position[0], 64); stPosition.position[64] = isWhitesMove() ? 'W' : 'B'; vPositions.push_back(stPosition); int intPositionCount = 0; int intSize = vPositions.size(); for (int i = 0; i < intSize; ++i) { if (!memcmp(&vPositions[i].position[0], &position, 64)) { if (++intPositionCount == 3) { // we have repeated this position three times - draw intStatus = 5; break; } } } } #ifdef MY_DEBUG printf("\n"); if (!intStatus) { printf("The move %s is illegal\n", lpMove); } else { if (bIsWhitesMove) { printf("Black played %s\n", lpMove); } else { printf("White played %s\n", lpMove); } // printf the board for (int i = 0; i < 64; ++i) { if (!(i % 8)) { printf("\n "); } else { putchar(' '); } putchar(position[i]); } if (intStatus == 2) { printf("\nStalemate!\n"); } else if(intStatus == 3) { printf("\nWhite is mated!\n"); } else if(intStatus == 4) { printf("\nBlack is mated!\n"); } } #endif return intStatus; } //***************************************************************** // // int ChessScoreKeeper::move(int intSource, int intDest) // // Parameters: // // intSource - the source square // // Description: // // This method first checks to see if the given move is legal in the // the objects current position. If legal, the move is made and // the position is adjusted. // // Returns: // // 0 - lpszMove is illegal in the current position // 1 - lpszMove is legal // 2 - lpszMove is legal and the position is a stalemate // 3 - lpszMove is legal and black is mated // 4 - lpszMove is legal and white is mated // //***************************************************************** int ChessScoreKeeper::move(int intSource, int intDest, char cPromotionPiece) { if (!bGameIsInProgress) { return 0; } // return illegal if source or dest are off the board if (intSource < 0 || intSource > 63 || intDest < 0 || intDest > 63) { return 0; } // now find the piece char piece = position[intSource]; // make sure the right side is moving if ((bIsWhitesMove && islower(piece)) || (!bIsWhitesMove && isupper(piece))) { return 0; } // we can't capture one of our own pieces if ((bIsWhitesMove && isupper(position[intDest])) || (!bIsWhitesMove && islower(position[intDest]))) { return 0; } // now determine if we have a legal move int bIsLegal = FALSE; switch (piece) { case 'P': case 'p': bIsLegal = IsPawnMove(intSource, intDest, cPromotionPiece); break; case 'N': case 'n': bIsLegal = IsKnightMove(intSource, intDest); break; case 'B': case 'b': bIsLegal = IsBishopMove(intSource, intDest); break; case 'R': case 'r': bIsLegal = IsRookMove(intSource, intDest); break; case 'Q': case 'q': bIsLegal = IsQueenMove(intSource, intDest); break; case 'K': case 'k': bIsLegal = IsKingMove(intSource, intDest); break; default: bIsLegal = FALSE; break; } if (bIsLegal) { // save the current position and it's state char savedPosition[64]; memcpy(&savedPosition[0], &position[0], 64); int savedLastSource = intLastSource; int savedLastDest = intLastDest; int savedLastPiece = cLastPiece; int savedEnPassent = bEnPassent; int savedWhiteCantKCastle = bWhiteCantKCastle; int savedWhiteCantQCastle = bWhiteCantQCastle; int savedBlackCantKCastle = bBlackCantKCastle; int savedBlackCantQCastle = bBlackCantQCastle; // make the move // check for promotion if (piece == 'P' && IsBoardBottom(intDest)) { position[intDest] = cPromotionPiece; } else if (piece == 'p' && IsBoardTop(intDest)) { position[intDest] = tolower(cPromotionPiece); } else { position[intDest] = piece; } position[intSource] = '-'; // if we just took en passent, remove the enemy piece if (bEnPassent) { if (bIsWhitesMove) { position[intDest - 8] = '-'; } else { position[intDest + 8] = '-'; } bEnPassent = FALSE; } // make castling moves and set castling flags if needed switch (piece) { case 'K': // white king side castle if (intSource == 3 && intDest == 1) { // make the rook move position[0] = '-'; position[2] = 'R'; } // white queen side castle else if (intSource == 3 && intDest == 5) { position[7] = '-'; position[4] = 'R'; } bWhiteCantKCastle = TRUE; bWhiteCantQCastle = TRUE; break; case 'k': // black king side castle if (intSource == 59 && intDest == 57) { // make the rook move position[56] = '-'; position[58] = 'r'; } // black queen side castle else if (intSource == 59 && intDest == 61) { // make the root move position[63] = '-'; position[60] = 'r'; } bBlackCantKCastle = TRUE; bBlackCantQCastle = TRUE; break; case 'R': if (intSource == 0) { bWhiteCantKCastle = TRUE; } else if (intSource == 7) { bWhiteCantQCastle = TRUE; } break; case 'r': if (intSource == 56) { bBlackCantKCastle = TRUE; } else if (intSource == 63) { bBlackCantQCastle = TRUE; } break; default: break; } // set the last move for the benifit of en passent checking // on the next move intLastSource = intSource; intLastDest = intDest; cLastPiece = piece; if (IsInCheck()) { // woops - we're in check! reset the position and it's state variables memcpy(&position[0], &savedPosition[0], 64); intLastSource = savedLastSource; intLastDest = savedLastDest; cLastPiece = savedLastPiece; bEnPassent = savedEnPassent; bWhiteCantKCastle = savedWhiteCantKCastle; bWhiteCantQCastle = savedWhiteCantQCastle; bBlackCantKCastle = savedBlackCantKCastle; bBlackCantQCastle = savedBlackCantQCastle; bIsLegal = FALSE; } else { // it's now the other guy's moves bIsWhitesMove = bIsWhitesMove ? FALSE : TRUE; } } // CheckForMate() will return the game's status (mate, no mate, or statlemate) return bIsLegal ? CheckForMate() : 0; } //***************************************************************** // // int ChessScoreKeeper::IsPawnMove(int intSource, int intDest, char cPromotionPiece) // // Parameters: // // intSource - the source square // intDest - the dest square // cPromotionPiece - the PromotionPiece - must be Q, R, B, N or '\0'; // // Description: // // This method determines whether the move from intSource to // intDest is a legal pawn move in the current position. // // Returns: // // TRUE if this is a legal pawn move - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsPawnMove(int intSource, int intDest, char cPromotionPiece) { int intDistance = intDest - intSource; if (bIsWhitesMove) { // if we are promoting - make sure we have a valid promotion piece if (IsBoardBottom(intDest) && cPromotionPiece != 'Q' && cPromotionPiece != 'R' && cPromotionPiece != 'B' && cPromotionPiece != 'N') { return FALSE; } // moving two squares? if (intDistance == 16) { // not on the second rank if (intSource > 15) { return FALSE; } // is there an enemy piece blocking us? else if (position[intSource + 8] != '-' || position[intDest] != '-') { return FALSE; } else { return TRUE; } } // are we moving one square? else if (intDistance == 8) { // we can't capture a piece in front of us if (position[intDest] != '-') { return FALSE; } else { return TRUE; } } // is this pawn capturing a piece? else if (intDistance == 7 || intDistance == 9) { // don't jump over the edge of the board if ((IsAFile(intSource) && IsHFile(intDest)) || (IsHFile(intSource) && IsAFile(intDest))) { return FALSE; } // check for En Passent here // first, we must be on the sixth rank to be an e.p. move if (intDest / 8 == 5) { // we must examine the last move to determine if this is // en passent if (cLastPiece == 'p' && (intLastSource == intDest + 8) && (intLastDest == intDest - 8)) { bEnPassent = TRUE; return TRUE; } } // make sure we're capturing an enemy piece if (islower(position[intDest])) { return TRUE; } } } // black pawns moves below else { // if we are promoting - make sure we have a valid promotion piece if (IsBoardTop(intDest) && cPromotionPiece != 'Q' && cPromotionPiece != 'R' && cPromotionPiece != 'B' && cPromotionPiece != 'N') { return FALSE; } // moving two squares? if (intDistance == -16) { // not on the second rank if (intSource < 48) { return FALSE; } // is there an enemy piece blocking us? else if (position[intSource - 8] != '-' || position[intDest] != '-') { return FALSE; } else { return TRUE; } } // are we moving one square? else if (intDistance == -8) { // we can't capture a piece in front of us if (position[intDest] != '-') { return FALSE; } else { return TRUE; } } // is this pawn capturing a piece? else if (intDistance == -7 || intDistance == -9) { // don't jump over the edge of the board if ((IsAFile(intSource) && IsHFile(intDest)) || (IsHFile(intSource) && IsAFile(intDest))) { return FALSE; } // check for En Passent here // first, we must be on the sixth rank to be an e.p. move if (intDest / 8 == 2) { // we must examine the last move to determine if this is // en passent if (cLastPiece == 'P' && (intLastSource == intDest - 8) && (intLastDest == intDest + 8)) { bEnPassent = TRUE; return TRUE; } } // make sure we're capturing an enemy piece if (isupper(position[intDest])) { return TRUE; } } } // if we made it here we're illegal return FALSE; } //***************************************************************** // // int ChessScoreKeeper::IsKnightMove(int intSource, int intDest) // // Parameters: // // intSource - the source square // intDest - the dest square // // Description: // // This method determines whether the move from intSource to // intDest is a legal knight move in the current position. // // Returns: // // TRUE if this is a legal knight move - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsKnightMove(int intSource, int intDest) { // Do not let the knight jump over the side of board if ((IsAFile(intSource) || IsBFile(intSource)) && (IsGFile(intDest) || IsHFile(intDest))) { return FALSE; } if ((IsGFile(intSource) || IsHFile(intSource)) && (IsAFile(intDest) || IsBFile(intDest))) { return FALSE; } // okay, now look for a valid knight move int intDistance = abs(intDest - intSource); if (intDistance == 17 || intDistance == 10 || intDistance == 15 || intDistance == 6) { return TRUE; } return FALSE; } //***************************************************************** // // int ChessScoreKeeper::IsBishopMove(int intSource, int intDest) // // Parameters: // // intSource - the source square // intDest - the dest square // // Description: // // This method determines whether the move from intSource to // intDest is a legal bishop move in the current position. // // Returns: // // TRUE if this is a legal bisop move - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsBishopMove(int intSource, int intDest) { int intDistance = abs(intDest - intSource); // must be moved diagonally if ((intDistance % 7 != 0) && (intDistance % 9 != 0)) { return FALSE; } // bishops cannot move to opposite color squares if ((IsLightSquare(intSource) && !IsLightSquare(intDest)) || (!IsLightSquare(intSource) && IsLightSquare(intDest))) { return FALSE; } // now make sure there are no pieces blocking us and // that we don't jump off the board // first, the right top to left bottom diagonal if (intDistance % 7 == 0 && intDistance != 63) { // are we moving from top to bottom? if (intDest > intSource) { for (int i = intSource + 7; i < intDest; i += 7) { // is a piece in our way? if (position[i] != '-') { return FALSE; } // don't move beyond the bottom or left edge of the board if (IsBoardTop(i) || IsAFile(i)) { return FALSE; } } // dont't land byond the bottom or left edge of the board if (IsBoardTop(intDest) || IsAFile(intDest)) { return FALSE; } } // are we moving from bottom to top? else if (intDest < intSource) { for (int i = intSource - 7; i > intDest; i -= 7) { // is a piece in our way? if (position[i] != '-') { return FALSE; } // don't move beyond the top or right edge of the board if (IsBoardBottom(i) || IsHFile(i)) { return FALSE; } } // don't land beyond the bottom or top edge of the board if (IsBoardBottom(intDest) || IsHFile(intDest)) { return FALSE; } } } // left top to right bottom diagonal below else if (intDistance % 9 == 0) { // are we moving from top to bottom? if (intDest > intSource) { for (int i = intSource + 9; i < intDest; i += 9) { // is a piece in our way? if (position[i] != '-') { return FALSE; } // don't move beyond the bottom or right edge of the board if (IsBoardTop(i) || IsHFile(i)) { return FALSE; } } // don't land on the left or top edge of the board if (IsBoardTop(intDest) || IsHFile(intDest)) { return FALSE; } } // are we moving from bottom to top? else if (intDest < intSource) { for (int i = intSource - 9; i > intDest; i -= 9) { // is a piece in our way? if (position[i] != '-') { return FALSE;; } // don't move beyond the top or right edge of the board if (IsBoardBottom(i) || IsAFile(i)) { return FALSE; } } // don't land beyond the bottom or top edge of the board if (IsBoardBottom(intDest) || IsAFile(intDest)) { return FALSE; } } } return TRUE; } //***************************************************************** // // int ChessScoreKeeper::IsRookMove(int intSource, int intDest) // // Parameters: // // intSource - the source square // intDest - the dest square // // Description: // // This method determines whether the move from intSource to // intDest is a legal rook move in the current position. // // Returns: // // TRUE if this is a legal rook move - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsRookMove(int intSource, int intDest) { int bIsXMove = FALSE; int bIsYMove = FALSE; // horizonal (x-move) or verticle (y-move) if ((intSource / 8) == (intDest / 8)) { bIsXMove = TRUE; } else if ((intSource - intDest) % 8 == 0) { bIsYMove = TRUE; } else { return FALSE; } // now make sure there are no pieces blocking us and // that we don't jump off the board // first, handle the verticle moves if (bIsYMove) { // are we moving from top to bottom? if (intDest > intSource) { for (int i = intSource + 8; i < intDest; i += 8) { // is a piece in our way? if (position[i] != '-') { return FALSE; } } } // are we moving from bottom to top? else if (intDest < intSource) { for (int i = intSource - 8; i > intDest; i -= 8) { // is a piece in our way? if (position[i] != '-') { return FALSE; } } } } // left top to right bottom diagonal below else if (bIsXMove) { // are we moving from left to right? if (intDest > intSource) { for (int i = intSource + 1; i < intDest; ++i) { // is a piece in our way? if (position[i] != '-') { return FALSE; } // don't move beyond the right edge of the board if (IsHFile(i)) { return FALSE; } } // don't land on the left edge of the board if (IsHFile(intDest)) { return FALSE; } } // are we moving from right to left? else if (intDest < intSource) { for (int i = intSource - 1; i > intDest; i--) { // is a piece in our way? if (position[i] != '-') { return FALSE;; } // don't move beyone the left edge of the board if (IsAFile(i)) { return FALSE; } } // don't land on the right edge of the board if (IsAFile(intDest)) { return FALSE; } } } return TRUE; } //***************************************************************** // // int ChessScoreKeeper::IsQueenMove(int intSource, int intDest) // // Parameters: // // intSource - the source square // intDest - the dest square // // Description: // // This method determines whether the move from intSource to // intDest is a legal queen move in the current position. // // Returns: // // TRUE if this is a legal queen move - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsQueenMove(int intSource, int intDest) { return IsRookMove(intSource, intDest) || IsBishopMove(intSource, intDest); } //***************************************************************** // // int ChessScoreKeeper::IsKingMove(int intSource, int intDest) // // Parameters: // // intSource - the source square // intDest - the dest square // // Description: // // This method determines whether the move from intSource to // intDest is a legal king move in the current position. // // Returns: // // TRUE if this is a legal king move - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsKingMove(int intSource, int intDest) { // check for castling moves if (intSource == 3 && intDest == 1) { // a white king-side castle if (bWhiteCantKCastle) { return FALSE; } // can't castle through check if (IsAttacked(1) || IsAttacked(2) || IsAttacked(3)) { return FALSE; } // make sure no pieces are betweeen the king and rook if (position[1] != '-' || position[2] !='-') { return FALSE; } // all clear Houston return TRUE; } else if (intSource == 3 && intDest == 5) { // a white queen-side castle if (bWhiteCantQCastle) { return FALSE; } // can't castle through check if (IsAttacked(3) || IsAttacked(4) || IsAttacked(5)) { return FALSE; } // make sure no pieces are betweeen the king and rook if (position[4] != '-' || position[5] !='-' || position[6] !='-') { return FALSE; } // all clear Houston return TRUE; } else if (intSource == 59 && intDest == 57) { // a black king-side castle if (bBlackCantKCastle) { return FALSE; } // can't castle through check if (IsAttacked(57) || IsAttacked(58) || IsAttacked(59)) { return FALSE; } // make sure no pieces are betweeen the king and rook if (position[57] != '-' || position[58] !='-') { return FALSE; } // all clear Houston return TRUE; } else if (intSource == 59 && intDest == 61) { // a black queen-side castle if (bBlackCantQCastle) { return FALSE; } // can't castle through check if (IsAttacked(59) || IsAttacked(60) || IsAttacked(61)) { return FALSE; } // make sure no pieces are betweeen the king and rook if (position[60] != '-' || position[61] !='-' || position[62] !='-') { return FALSE; } // all clear Houston return TRUE; } // don't jump over the edge of the board if ((IsAFile(intSource) && IsHFile(intDest)) || (IsHFile(intSource) && IsAFile(intDest))) { return FALSE; } // now look for valid king moves int intDistance = abs(intSource - intDest); if (intDistance == 1 || intDistance == 7 || intDistance == 8 || intDistance == 9) { return TRUE; } return FALSE; } //***************************************************************** // // int ChessScoreKeeper::Is?File(int intSquare) // // Parameters: // // intSquare - a square on the chessboard // // Description: // // These methods determine whether the given square is on // the ? file where ? could be A, B, C, D, E, F, G, or H. // // Returns: // // TRUE if intSquare is on the ? file - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsAFile(int intSquare) { return intSquare % 8 == 7; } int ChessScoreKeeper::IsBFile(int intSquare) { return intSquare % 8 == 6; } int ChessScoreKeeper::IsCFile(int intSquare) { return intSquare % 8 == 5; } int ChessScoreKeeper::IsDFile(int intSquare) { return intSquare % 8 == 4; } int ChessScoreKeeper::IsEFile(int intSquare) { return intSquare % 8 == 3; } int ChessScoreKeeper::IsFFile(int intSquare) { return intSquare % 8 == 2; } int ChessScoreKeeper::IsGFile(int intSquare) { return intSquare % 8 == 1; } int ChessScoreKeeper::IsHFile(int intSquare) { return intSquare % 8 == 0; } //***************************************************************** // // int ChessScoreKeeper::IsLightSquare(int intSquare) // // Parameters: // // intSquare - a square on the chessboard // // Description: // // These method determines whether intSquare is a light colored square // // Returns: // // TRUE if intSquare is a light square - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsLightSquare(int intSquare) { int intFile = intSquare % 8; int intRank = intSquare / 8; return (intFile + intRank) % 2 == 0; } //***************************************************************** // // int ChessScoreKeeper::IsBoardTop/Bottom(int intSquare) // // Parameters: // // intSquare - a square on the chessboard // // Description: // // These methods determine whether intSquare is at the top/Bottom // (first/eighth rank for White) of the board // // Returns: // // TRUE if intSquare is at the top/bottom of the board - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsBoardTop(int intSquare) { return intSquare < 8; } int ChessScoreKeeper::IsBoardBottom(int intSquare) { return intSquare > 55; } //***************************************************************** // // int ChessScoreKeeper::IsInCheck() // // Parameters: // // none // // Description: // // This method determines where the side on move is in check // // Returns: // // TRUE if the side on move is in check - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsInCheck() { char chKing = bIsWhitesMove ? 'K' : 'k'; // find the king square int intKingSquare = 0; for (int i = 0; i < 64; ++i) { if (position[i] == chKing) { intKingSquare = i; break; } } // are we in check? if (IsAttacked(intKingSquare)) { return TRUE; } return FALSE; } //***************************************************************** // // int ChessScoreKeeper::IsAttacked(int intSquare) // // Parameters: // // intSquare - a square on the chessboard // // Description: // // This method determines if the given square is attacked by // an enemy piece. The enemy is the side NOT on move. // // Returns: // // TRUE if intSquare is attacked - FALSE otherwise. // //***************************************************************** int ChessScoreKeeper::IsAttacked(int intSquare) { for (int i = 0; i < 64; ++i) { int bIsAttacked = FALSE; if ((bIsWhitesMove && islower(position[i])) || (!bIsWhitesMove && isupper(position[i]))) { switch (position[i]) { case 'P': case 'p': bIsWhitesMove = !bIsWhitesMove; bIsAttacked = IsPawnMove(i, intSquare, 'Q'); bIsWhitesMove = !bIsWhitesMove; break; case 'N': case 'n': bIsAttacked = IsKnightMove(i, intSquare); break; case 'B': case 'b': bIsAttacked = IsBishopMove(i, intSquare); break; case 'R': case 'r': bIsAttacked = IsRookMove(i, intSquare); break; case 'Q': case 'q': bIsAttacked = IsQueenMove(i, intSquare); break; case 'K': case 'k': bIsAttacked = IsKingMove(i, intSquare); break; default: break; } if (bIsAttacked) { return TRUE; } } } return FALSE; } //***************************************************************** // // int ChessScoreKeeper::IsCheckForMate() // // Parameters: // // none // // Description: // // This method determines if the side on move is mated or // stalemated in the current position // // Returns: // // 1 - there is no mate or stalemate // 2 - position is a stalemate // 3 - white is mated // 4 - black is mated // //***************************************************************** int ChessScoreKeeper::CheckForMate() { // don't recurse into this method static int bHasBeenCalled = FALSE; if (bHasBeenCalled) { return 1; } bHasBeenCalled = TRUE; // save the current position and it's state char savedPosition[64]; memcpy(&savedPosition[0], &position[0], 64); int savedLastSource = intLastSource; int savedLastDest = intLastDest; int savedLastPiece = cLastPiece; int savedEnPassent = bEnPassent; int savedWhiteCantKCastle = bWhiteCantKCastle; int savedWhiteCantQCastle = bWhiteCantQCastle; int savedBlackCantKCastle = bBlackCantKCastle; int savedBlackCantQCastle = bBlackCantQCastle; int savedIsWhitesMove = bIsWhitesMove; // first see if we have a legal move for (int intSource = 0; intSource < 64; ++intSource) { if ((bIsWhitesMove && isupper(position[intSource])) || (!bIsWhitesMove && islower(position[intSource]))) { for (int intDest = 0; intDest < 64; ++intDest) { if (move(intSource,intDest, '\0')) { // restore the position and return 1 // we don't have a mate or stalemate memcpy(&position[0], &savedPosition[0], 64); intLastSource = savedLastSource; intLastDest = savedLastDest; cLastPiece = savedLastPiece; bEnPassent = savedEnPassent; bWhiteCantKCastle = savedWhiteCantKCastle; bWhiteCantQCastle = savedWhiteCantQCastle; bBlackCantKCastle = savedBlackCantKCastle; bBlackCantQCastle = savedBlackCantQCastle; bIsWhitesMove = savedIsWhitesMove; bHasBeenCalled = FALSE; return 1; } } } } // restore the position memcpy(&position[0], &savedPosition[0], 64); intLastSource = savedLastSource; intLastDest = savedLastDest; cLastPiece = savedLastPiece; bEnPassent = savedEnPassent; bWhiteCantKCastle = savedWhiteCantKCastle; bWhiteCantQCastle = savedWhiteCantQCastle; bBlackCantKCastle = savedBlackCantKCastle; bBlackCantQCastle = savedBlackCantQCastle; bIsWhitesMove = savedIsWhitesMove; bHasBeenCalled = FALSE; // we have no legal moves - so it's either mate or stalemate if (IsInCheck()) { // it's mate! return bIsWhitesMove ? 3 : 4; } // it's stalemate return 2; } //***************************************************************** // // void ChessScoreKeeper::StoreMove(int intSource, int intDest, // char cPromotionChar, int bIsCapture, int bMate) // // Parameters: // // intSource - the source square // intdest - the dest square // cPromotionChar - the promotion piece or '\0' in there is none // bIsCapture - TRUE if this is a capturing move // bMate - TRUE if this is mate // // Description: // // This method stores the move in short PGN (algebraic) format int the char *move[] array // // Returns: // // nothing // //***************************************************************** void ChessScoreKeeper::StoreMove(int intSource, int intDest, char cPromotionChar, int bIsCapture, int bMate) { int bIsLongPGN = FALSE; char szMove[12] = {0}; if (intMoveNumber >= 200) { return; } // make a short algebraic move string out of intSource and intDest // first, convert the destSquare char szDest[3]; szDest[0] = 'h' - (intDest % 8); szDest[1] = '1' + (intDest / 8); szDest[2] = '\0'; // castling? int bCastlingMove = FALSE; if (cLastPiece == 'K' || cLastPiece == 'k') { if ((intSource == 3 && intDest == 1) || (intSource == 59 && intDest == 57)) { bCastlingMove = TRUE; strcpy(szMove, "O-O"); } else if ((intSource == 3 && intDest == 5) || (intSource == 59 && intDest == 61)) { bCastlingMove = TRUE; strcpy(szMove, "O-O-O"); } } // pawn move? if (cLastPiece == 'P' || cLastPiece == 'p') { if (bIsCapture) { // get the file of the source square sprintf(szMove, "%c", 'h' - (intSource % 8)); } } // all other moves are the piece that was last moved else if (!bCastlingMove) { sprintf(szMove, "%c", toupper(cLastPiece)); // if this is not a king (a pawn can't be here either) - see if we need more info // like Nge2 instead of Ne2 if (cLastPiece != 'K' && cLastPiece != 'k') for (int i = 0; i < 64; ++i) { char cPiece = position[i]; if (cPiece == cLastPiece && i != intDest) { int bIsLegal = FALSE; switch (cPiece) { case 'N': case 'n': bIsLegal = IsKnightMove(i, intDest); break; case 'B': case 'b': bIsLegal = IsBishopMove(i, intDest); break; case 'R': case 'r': bIsLegal = IsRookMove(i, intDest); break; case 'Q': case 'q': bIsLegal = IsQueenMove(i, intDest); break; } // if we have another piece that can move to this square // we'll the more specific notation if (bIsLegal) { // see if we can use the file (Nge2 instead of Ne2) if ((intSource % 8) != (i % 8)) { char szFile[2]; szFile[0] = 'h' - (intSource % 8); szFile[1] = '\0'; strcat(szMove, szFile); } // if the file won't do see if we can use the rank (N1e2 instead of Ne2) else if ((intSource / 8) != (i / 8)) { char szRank[2]; szRank[0] = '1' + (intDest / 8); szRank[1] = '\0'; strcat(szMove, szRank); } // if pawns have queen giving use a situation where we might have // Qg4-e2 - make it a long aglebraic move else { char szSource[3]; szSource[0] = 'h' - (intSource % 8); szSource[1] = '1' + (intSource / 8); szSource[2] = '\0'; strcat(szMove, "-"); bIsLongPGN = TRUE; } } } } } if (bIsCapture && !bIsLongPGN) { strcat(szMove, "x"); } if (!bCastlingMove) { strcat(szMove, szDest); } if (cPromotionChar) { char szPromotion[2]; szPromotion[0] = cPromotionChar; szPromotion[1] = '\0'; strcat(szMove, "="); strcat(szMove, szPromotion); } if (bMate) { strcat(szMove, "#"); } else if (IsInCheck()) { strcat(szMove, "+"); } // now add our move and increment the move number moves[intMoveNumber] = strdup(szMove); ++intMoveNumber; } //***************************************************************** // // void ChessScoreKeeper::FreeMoves() // // Parameters: // // none // // Description: // // This method frees all allocated memory int the *moves[] array // // Returns: // // nothing // //***************************************************************** void ChessScoreKeeper::FreeMoves() { for (int i = 0; i < intMoveNumber; ++i) { free(moves[i]); } } //***************************************************************** // // void ChessScoreKeeper::getMoves() // // Parameters: // // none // // Description: // // This method allocates a comma delimited string to the moves of // the current game and returns a pointer to it. The calling is // responsible to free() the buffer. For example: free(lpMoves); // // Returns: // // A pointer to the moves string // //***************************************************************** char *ChessScoreKeeper::getMoves() { if (!intMoveNumber) { return NULL; } char *lpMovesBuffer = (char *)malloc(intMoveNumber * 10); if (!lpMovesBuffer) { return NULL; } lpMovesBuffer[0] = '\0'; for (int i = 0; i < intMoveNumber; ++i) { strcat(lpMovesBuffer, moves[i]); strcat(lpMovesBuffer, ","); } // remove the trailing comma lpMovesBuffer[strlen(lpMovesBuffer) - 1] = '\0'; char *lpMoves = strdup(lpMovesBuffer); free(lpMovesBuffer); return lpMoves; } #ifdef MY_DEBUG void main() { ChessScoreKeeper *scoreKeeper = new ChessScoreKeeper(); scoreKeeper->newGame(); char szMove[7]; printf("Enter 'bye' to quit\n"); for (;;) { printf("\n\nPlease enter a move: "); gets(szMove); if (!strcmp(szMove, "bye")) { break; } if (scoreKeeper->move(szMove)); { char *szMoves = scoreKeeper->getMoves(); if (szMoves) { printf("\n%s\n", szMoves); free(szMoves); } else { puts("szMoves is NULL"); } } } delete scoreKeeper; } #endif