///////////////////////////////////////////////////////////////////////////// // // TpmChess.cpp : implementation for Chess classes // // Written by Thomas F. Mooney, III (tfm3@teleproc.com) // Copyright © 1999. // // This code may be used in compiled form in any way you desire. This // file may be redistributed unmodified by any means PROVIDING it is // not sold for profit without the authors written consent, and // providing that this notice and the authors name is included. If // the source code in this file is used in any commercial application // then acknowledgement must be made to the author of this file // (in whatever form you wish). // // This file is provided "as is" with no expressed or implied warranty. // The author accepts no liability if it causes any damage to your // computer, data, etc. // // Please report any bugs/anomalies/suggestions to the author via the // e-mail address listed above. // ///////////////////////////////////////////////////////////////////////////// static char Copyright[] = "Copyright © 1999 - TeleProcess Management, Inc."; ///////////////////////////////////////////////////////////////////////////// // This file contains the definition of several classes that // are used to simulate a game of chess. // // The classes include: // ChessMove - a single move // ChessMan - a pawn or piece - used to subclass // ChessPawn - pawn // ChessKnight - knight // ChessBishop - bishop // ChessRook - rook // ChessQueen - queen // ChessKing - king // ChessSquare - a single square on a chess board // ChessBoard - a collection of squares and men // includes game status ///////////////////////////////////////////////////////////////////////////// #include "TpmChess.h" #include // The ChessMove constructor sets all member variables // to their correct initial state. ChessMove::ChessMove() { // Call common routine to reset defaults ResetDefaults(); } // The ChessMove copy constructor duplicates an existing // move ChessMove::ChessMove(const ChessMove& OldMove) { m_ManToMove = OldMove.m_ManToMove; m_cSourceRank = OldMove.m_cSourceRank; m_cSourceFile = OldMove.m_cSourceFile; m_cTargetFile = OldMove.m_cTargetFile; m_cTargetRank = OldMove.m_cTargetRank; m_cDisambigFile = OldMove.m_cDisambigFile; m_cDisambigRank = OldMove.m_cDisambigRank; m_bCapture = OldMove.m_bCapture; m_bCheck = OldMove.m_bCheck; m_bCheckmate = OldMove.m_bCheckmate; m_bPromote = OldMove.m_bPromote; m_bCastleKingside = OldMove.m_bCastleKingside; m_bCastleQueenside = OldMove.m_bCastleQueenside; m_PromoteTo = OldMove.m_PromoteTo; m_EnPassantTarget = OldMove.m_EnPassantTarget; m_PiecePlacementAfter = OldMove.m_PiecePlacementAfter; m_CastleAvailabilityAfter = OldMove.m_CastleAvailabilityAfter; m_bLegalMove = OldMove.m_bLegalMove; m_ErrorMessage = OldMove.m_ErrorMessage; m_bMoveAltered = OldMove.m_bMoveAltered; m_bLastInPv = OldMove.m_bLastInPv; } // The ~ChessMove destructor is a stub function that does // nothing. ChessMove::~ChessMove() { } // The ChessMove assignment operator duplicates an existing // move ChessMove& ChessMove::operator=(const ChessMove& OldMove) { if (&OldMove != this) { m_ManToMove = OldMove.m_ManToMove; m_cSourceRank = OldMove.m_cSourceRank; m_cSourceFile = OldMove.m_cSourceFile; m_cTargetFile = OldMove.m_cTargetFile; m_cTargetRank = OldMove.m_cTargetRank; m_cDisambigFile = OldMove.m_cDisambigFile; m_cDisambigRank = OldMove.m_cDisambigRank; m_bCapture = OldMove.m_bCapture; m_bCheck = OldMove.m_bCheck; m_bCheckmate = OldMove.m_bCheckmate; m_bPromote = OldMove.m_bPromote; m_bCastleKingside = OldMove.m_bCastleKingside; m_bCastleQueenside = OldMove.m_bCastleQueenside; m_PromoteTo = OldMove.m_PromoteTo; m_EnPassantTarget = OldMove.m_EnPassantTarget; m_PiecePlacementAfter = OldMove.m_PiecePlacementAfter; m_CastleAvailabilityAfter = OldMove.m_CastleAvailabilityAfter; m_bLegalMove = OldMove.m_bLegalMove; m_ErrorMessage = OldMove.m_ErrorMessage; m_bMoveAltered = OldMove.m_bMoveAltered; m_bLastInPv = OldMove.m_bLastInPv; } return *this; } // The ChessMove less-than operator is used for list // sorting. bool ChessMove::operator<(ChessMove& HigherMove) { // compare based on generated SAN if (CreateSAN() < HigherMove.CreateSAN()) { return true; } else { return false; } } // ParseSAN takes a SAN notation chess move and parses it // into its constituent parts. Extensive error checking is // performed. void ChessMove::ParseSAN(SAN San) { int nSanLength; std::string WorkSan; std::string TestKingside; std::string TestQueenside; std::string KingsideCastle = "O-O"; std::string QueensideCastle = "O-O-O"; // We parse by nibbling known bits off the ends of the // supplied SAN move. We keep track of the length remaining // and at the end check to see that the entire supplied // move has been parsed. //////////////////////////////////////////////////////////// // Note!!! // // Contrary to best programming practices this routine has // multiple exit points. As soon as any error is found we // return from the routine. // // //////////////////////////////////////////////////////////// Note!!! // Force object back to intial state ResetDefaults(); // Make a copy of supplied move for parsing and get size WorkSan = San; nSanLength = WorkSan.size(); // Determine if move indicates check (+) or checkmate (#). if (nSanLength >= 1) { if (WorkSan[nSanLength - 1] == '+') { m_bCheck = true; WorkSan = WorkSan.substr(0, nSanLength - 1); nSanLength = WorkSan.size(); } else if (WorkSan[nSanLength - 1] == '#') { m_bCheck = true; m_bCheckmate = true; WorkSan = WorkSan.substr(0, nSanLength - 1); nSanLength = WorkSan.size(); } } else { // If length < 1 there's no move. Flag the error. m_bLegalMove = false; m_ErrorMessage = "No move supplied for parsing"; return; // No sense in going on. } // Check to see if castling has been requested. TestKingside = WorkSan.substr(0, KingsideCastle.size()); TestQueenside = WorkSan.substr(0, QueensideCastle.size()); if (TestQueenside == QueensideCastle) { m_ManToMove = King; m_bCastleQueenside = true; WorkSan = WorkSan.substr(QueensideCastle.size(), nSanLength - QueensideCastle.size()); nSanLength = WorkSan.size(); } else if (TestKingside == KingsideCastle) { m_ManToMove = King; m_bCastleKingside = true; WorkSan = WorkSan.substr(KingsideCastle.size(), nSanLength - KingsideCastle.size()); nSanLength = WorkSan.size(); } else { // Check to see if this move results in pawn promotion. if (nSanLength >= 2) { if (WorkSan[nSanLength - 2] == '=') { m_bPromote = true; if (WorkSan[nSanLength - 1] == 'Q') { m_PromoteTo = Queen; } else if (WorkSan[nSanLength - 1] == 'N') { m_PromoteTo = Knight; } else if (WorkSan[nSanLength - 1] == 'R') { m_PromoteTo = Rook; } else if (WorkSan[nSanLength - 1] == 'B') { m_PromoteTo = Bishop; } else { m_bLegalMove = false; m_ErrorMessage = "Invalid promotion piece"; return; // No sense in going on. } WorkSan = WorkSan.substr(0, nSanLength - 2); nSanLength = WorkSan.size(); } } // Get target file and rank for move, verify reasonability. m_cTargetFile = WorkSan[nSanLength - 2]; m_cTargetRank = WorkSan[nSanLength - 1]; WorkSan = WorkSan.substr(0, nSanLength - 2); nSanLength = WorkSan.size(); if ((m_cTargetFile < FileBase) || (m_cTargetFile >= FileBase + FileCount) || (m_cTargetRank < RankBase) || (m_cTargetRank >= RankBase + RankCount)) { m_bLegalMove = false; m_ErrorMessage = "Invalid target file/rank"; return; // No sense in going on. } // Check for capture if (nSanLength >= 1) { if (WorkSan[nSanLength - 1] == 'x') { m_bCapture = true; WorkSan = WorkSan.substr(0, nSanLength - 1); nSanLength = WorkSan.size(); } } // What's on the front at this point should either be // the disambiguation file for a pawn capture or the // type of piece to move. if (nSanLength >= 1) { if ((WorkSan[0] >= FileBase) && (WorkSan[0] <= (FileBase + (FileCount - 1)))) { if ((m_ManToMove == Pawn) && (m_bCapture)) { m_cDisambigFile = WorkSan[0]; WorkSan = WorkSan.substr(1, nSanLength - 1); nSanLength = WorkSan.size(); } else { m_bLegalMove = false; m_ErrorMessage = "Invalid piece designator or pawn disambiguation file"; return; // No sense in going on. } } else if (WorkSan[0] == 'N') { m_ManToMove = Knight; WorkSan = WorkSan.substr(1, nSanLength - 1); nSanLength = WorkSan.size(); } else if (WorkSan[0] == 'B') { m_ManToMove = Bishop; WorkSan = WorkSan.substr(1, nSanLength - 1); nSanLength = WorkSan.size(); } else if (WorkSan[0] == 'R') { m_ManToMove = Rook; WorkSan = WorkSan.substr(1, nSanLength - 1); nSanLength = WorkSan.size(); } else if (WorkSan[0] == 'Q') { m_ManToMove = Queen; WorkSan = WorkSan.substr(1, nSanLength - 1); nSanLength = WorkSan.size(); } else if (WorkSan[0] == 'K') { m_ManToMove = King; WorkSan = WorkSan.substr(1, nSanLength - 1); nSanLength = WorkSan.size(); } else { m_bLegalMove = false; m_ErrorMessage = "Invalid piece designator or pawn disambiguation file"; return; // No sense in going on. } } if ((nSanLength > 0) && (m_ManToMove != Pawn)) { // Anything left must be disambiguation information. // Check for reasonability. if (nSanLength == 2) { m_cDisambigFile = WorkSan[0]; m_cDisambigRank = WorkSan[1]; WorkSan = WorkSan.substr(2, nSanLength - 2); nSanLength = WorkSan.size(); if ((m_cDisambigFile < FileBase) || (m_cDisambigFile >= FileBase + FileCount) || (m_cDisambigRank < RankBase) || (m_cDisambigRank >= RankBase + RankCount)) { m_bLegalMove = false; m_ErrorMessage = "Invalid disambiguation file/rank"; return; // No sense in going on. } } else { if ((WorkSan[0] >= FileBase) && (WorkSan[0] < (FileBase + FileCount))) { m_cDisambigFile = WorkSan[0]; WorkSan = WorkSan.substr(1, nSanLength - 1); nSanLength = WorkSan.size(); } else if ((WorkSan[0] >= RankBase) && (WorkSan[0] < (RankBase + RankCount))) { m_cDisambigRank = WorkSan[0]; WorkSan = WorkSan.substr(1, nSanLength - 1); nSanLength = WorkSan.size(); } } } } // If we have anything left over it's an error. if (nSanLength != 0) { m_bLegalMove = false; m_ErrorMessage = "Invalid SAN, extra characters"; return; // No sense in going on. } // All non-castling moves must have a target file and rank if ((!m_bCastleKingside) && (!m_bCastleQueenside) && ((m_cTargetFile == ' ') || (m_cTargetRank == ' '))) { m_bLegalMove = false; m_ErrorMessage = "Target file and/or rank not specified"; return; // No sense in going on. } // Only pawns may promote. if ((m_bPromote) && (m_ManToMove != Pawn)) { m_bLegalMove = false; m_ErrorMessage = "Only pawns may promote"; return; // No sense in going on. } // Pawns to last rank must promote. if ((m_ManToMove == Pawn) && (!m_bPromote) && ((m_cTargetRank == '1') || (m_cTargetRank == '8'))) { m_bLegalMove = false; m_ErrorMessage = "Pawn moves to last rank must promote"; return; // No sense in going on. } // Pawn captures must specify a disambiguation file. if ((m_bCapture) && (m_ManToMove == Pawn)) { if (m_cDisambigFile == ' ') { m_bLegalMove = false; m_ErrorMessage = "Pawn capture must specify disambiguation file"; return; // No sense in going on. } } return; } // CreateSAN takes the information for a move and generates the // appropriate SAN. SAN ChessMove::CreateSAN() { SAN San; San = ""; // First take care of castling if (m_bCastleKingside) { San = "O-O"; } else if (m_bCastleQueenside) { San = "O-O-O"; } else { // Indicate which type of piece to move. switch (m_ManToMove) { case Pawn: break; case Knight: San = "N"; break; case Bishop: San = "B"; break; case Rook: San = "R"; break; case Queen: San = "Q"; break; case King: San = "K"; break; } // Put in disambiguation information if (m_cDisambigFile != ' ') { San += m_cDisambigFile; } if (m_cDisambigRank != ' ') { San += m_cDisambigRank; } // Indicate capture if (m_bCapture) { San += 'x'; } // Indicate target square San += m_cTargetFile; San += m_cTargetRank; // Indicate promotion if (m_bPromote) { San += '='; switch (m_PromoteTo) { case Queen: San += 'Q'; break; case Knight: San += 'N'; break; case Bishop: San += 'B'; break; case Rook: San += 'R'; break; } } } // Indicate checkmate and check if (m_bCheckmate) { San += '#'; } else if (m_bCheck) { San += '+'; } return San; } // IsLegalMove provides a public interface to a protected variable bool ChessMove::IsLegalMove() { return m_bLegalMove; } // GetErrorMsg provides a public interface to a protected variable std::string ChessMove::GetErrorMsg() { return m_ErrorMessage; } // IsMoveAltered provides a public interface to a protected variable bool ChessMove::IsMoveAltered() { return m_bMoveAltered; } // GetManToMove provides a public interface to a protected variable ChessManType ChessMove::GetManToMove() { return m_ManToMove; } // IsCapture provides a public interface to a protected variable bool ChessMove::IsCapture() { return m_bCapture; } // GetEnPassantTarget provides a public interface to a protected variable std::string ChessMove::GetEnPassantTarget() { return m_EnPassantTarget; } // GetPiecePlacementAfter provides a public interface to a protected variable EPD ChessMove::GetPiecePlacementAfter() { return m_PiecePlacementAfter; } // GetCastleAvailabilityAfter provides a public interface to a protected variable std::string ChessMove::GetCastleAvailabilityAfter() { return m_CastleAvailabilityAfter; } // SetLastInPv provides a public interface to a protected variable void ChessMove::SetLastInPv(bool Last) { m_bLastInPv = Last; } // ResetDefaults resets all member variables to their default states. void ChessMove::ResetDefaults() { m_ManToMove = Pawn; m_cSourceFile = ' '; m_cSourceRank = ' '; m_cTargetFile = ' '; m_cTargetRank = ' '; m_cDisambigFile = ' '; m_cDisambigRank = ' '; m_bCapture = false; m_bCheck = false; m_bCheckmate = false; m_bPromote = false; m_bCastleKingside = false; m_bCastleQueenside = false; m_PromoteTo = Queen; m_EnPassantTarget = ""; m_PiecePlacementAfter = ""; m_CastleAvailabilityAfter = ""; m_bLegalMove = true; m_ErrorMessage = ""; m_bMoveAltered = false; m_bLastInPv = false; } // ChessMan is an abstract class that represents a pawn or piece // in a chess game. It represents the attributes that are common // to all men; color, square, type, etc. // The ChessMan contructor initializes member variables to their // defaults. ChessMan::ChessMan() : m_Type(Pawn), m_bWhite(true), m_Value(1.0), m_pSquare(NULL) { } // The ChessMan copy constructor duplicates an existing man ChessMan::ChessMan(ChessMan& OldMan) { m_Type = OldMan.m_Type; m_bWhite = OldMan.m_bWhite; m_Value = OldMan.m_Value; m_pSquare = NULL; } // The ~ChessMan destructor is a stub function that does // nothing. ChessMan::~ChessMan() { } // The ChessMan assignment operator copies one man to another ChessMan& ChessMan::operator=(ChessMan& OldMan) { if (&OldMan != this) { m_Type = OldMan.m_Type; m_bWhite = OldMan.m_bWhite; m_Value = OldMan.m_Value; m_pSquare = NULL; } return *this; } // SetType and GetType offer public access to protected variables void ChessMan::SetType(ChessManType Type) { m_Type = Type; } ChessManType ChessMan::GetType() { return m_Type; } // SetColorWhite and IsColorWhite offer public access to protected // variables void ChessMan::SetColorWhite(bool White) { m_bWhite = White; } bool ChessMan::IsColorWhite() { return m_bWhite; } // SetValue and GetValue offer public access to protected // variables void ChessMan::SetValue(float Value) { m_Value = Value; } float ChessMan::GetValue() { return m_Value; } // SetSquare and GetSquare offer public access to protected // variables void ChessMan::SetSquare(ChessSquare* pSquare) { assert(pSquare != NULL); m_pSquare = pSquare; } ChessSquare* ChessMan::GetSquare() { assert(m_pSquare != NULL); return m_pSquare; } // GetBoard offers public access to a protected variable ChessBoard* ChessMan::GetBoard() { assert(m_pSquare != NULL); return m_pSquare->GetBoard(); } // GetFile returns the file the man is on char ChessMan::GetFile() { assert(m_pSquare != NULL); return m_pSquare->GetFile(); } // GetRank returns the rank the man is on char ChessMan::GetRank() { assert(m_pSquare != NULL); return m_pSquare->GetRank(); } // Capture simulates a man being capture. It is removed from // the square/board. Once removed it is deleted. void ChessMan::Capture() { if (m_pSquare != NULL) { m_pSquare->RemovePiece(); m_pSquare = NULL; } delete this; } // The ChessPawn constructor initializes a pawn ChessPawn::ChessPawn() : ChessMan() { SetType(Pawn); SetValue(1.0); } // The ChessPawn copy constructor duplicates a pawn ChessPawn::ChessPawn(ChessPawn& OldPawn) : ChessMan(OldPawn) { } // The ~ChessPawn destructor is a stub function that does nothing ChessPawn::~ChessPawn() { } // The ChessPawn assignment operator duplicates a pawn ChessPawn& ChessPawn::operator=(ChessPawn& OldPawn) { if (&OldPawn != this) { } return *this; } // CanMoveTo determines whether the pawn can reach and/or capture // a target square. Note: capture logic does not check that there // is a piece to capture. That logic takes place elsewhere. bool ChessPawn::CanMoveTo(char TargetFile, char TargetRank, bool Capture) { bool bCanMoveTo = false; // set default return code char cCurrentFile; char cCurrentRank; int nFileDelta; int nRankDelta; ChessBoard* pBoard; ChessSquare* pSquare; // Get our current location pBoard = GetBoard(); cCurrentFile = GetFile(); cCurrentRank = GetRank(); if (Capture) { // Captures are on diagonals nFileDelta = abs(cCurrentFile - TargetFile); if (nFileDelta == 1) { if (IsColorWhite()) { nRankDelta = TargetRank - cCurrentRank; } else { nRankDelta = cCurrentRank - TargetRank; } if (nRankDelta == 1) { bCanMoveTo = true; } } } else { // Non-captures move one or two squares forward. // If two squares, make certain first is empty. if (cCurrentFile == TargetFile) { if (IsColorWhite()) { nRankDelta = TargetRank - cCurrentRank; if (nRankDelta == 1) { bCanMoveTo = true; } else { if (nRankDelta == 2) { pSquare = pBoard->GetSquare(TargetFile, TargetRank - 1); if (pSquare->GetOccupant() == NULL) { bCanMoveTo = true; } } } } else { nRankDelta = cCurrentRank - TargetRank; if (nRankDelta == 1) { bCanMoveTo = true; } else { if (nRankDelta == 2) { pSquare = pBoard->GetSquare(TargetFile, TargetRank + 1); if (pSquare->GetOccupant() == NULL) { bCanMoveTo = true; } } } } } } return bCanMoveTo; } // The ChessKnight constructor initializes a piece ChessKnight::ChessKnight() : ChessMan() { SetType(Knight); SetValue(3.0); } // The ChessKnight copy constructor duplicates a piece ChessKnight::ChessKnight(ChessKnight& OldKnight) : ChessMan(OldKnight) { } // The ~ChessKnight destructor is a stub function that does nothing ChessKnight::~ChessKnight() { } // The ChessKnight assignment operator duplicates a piece ChessKnight& ChessKnight::operator=(ChessKnight& OldKnight) { if (&OldKnight != this) { } return *this; } // CanMoveTo determines whether the piece can reach the target // square. bool ChessKnight::CanMoveTo(char TargetFile, char TargetRank, bool Capture) { bool bCanMoveTo = false; // set default return code char cCurrentFile; char cCurrentRank; int nFileDelta; int nRankDelta; // Get the current location cCurrentFile = GetFile(); cCurrentRank = GetRank(); // Get the "offset" to the target location nFileDelta = abs(cCurrentFile - TargetFile); nRankDelta = abs(cCurrentRank - TargetRank); // All knight moves must be 2:1 if (nFileDelta == 1) { if (nRankDelta == 2) { bCanMoveTo = true; } } else if (nFileDelta == 2) { if (nRankDelta == 1) { bCanMoveTo = true; } } return bCanMoveTo; } // The ChessBishop constructor initializes a piece ChessBishop::ChessBishop() : ChessMan() { SetType(Bishop); SetValue(3.0); } // The ChessBishop copy constructor duplicates a piece ChessBishop::ChessBishop(ChessBishop& OldBishop) : ChessMan(OldBishop) { } // The ~ChessBishop destructor is a stub function that does nothing ChessBishop::~ChessBishop() { } // The ChessBishop assignment operator duplicates a piece ChessBishop& ChessBishop::operator=(ChessBishop& OldBishop) { if (&OldBishop != this) { } return *this; } // CanMoveTo determines whether the piece can reach the target // square. bool ChessBishop::CanMoveTo(char TargetFile, char TargetRank, bool Capture) { bool bCanMoveTo = false; // set default return code char cCurrentFile; char cCurrentRank; int nFileDelta; int nRankDelta; ChessBoard* pBoard; ChessSquare* pCurrentSquare; ChessSquare* pTargetSquare; ChessMan* pOccupant; // Get current location cCurrentFile = GetFile(); cCurrentRank = GetRank(); // Get offset to target location nFileDelta = TargetFile - cCurrentFile; nRankDelta = TargetRank - cCurrentRank; // Bishops must move along a diagonal if ((abs(nFileDelta) == abs(nRankDelta)) && (nFileDelta != 0)) { bCanMoveTo = true; // Get the target square pCurrentSquare = GetSquare(); pBoard = pCurrentSquare->GetBoard(); pTargetSquare = pBoard->GetSquare(TargetFile, TargetRank); // Get the next square on the correct diagonal if (nFileDelta > 0) { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetDiagUpRight(); } else { pCurrentSquare = pCurrentSquare->GetDiagDownRight(); } } else { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetDiagUpLeft(); } else { pCurrentSquare = pCurrentSquare->GetDiagDownLeft(); } } // Get the occupant of the square pOccupant = pCurrentSquare->GetOccupant(); // Traverse the diagonal until we reach the target // or until we are obstructed while ((pCurrentSquare != pTargetSquare) && (pOccupant == NULL)) { if (nFileDelta > 0) { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetDiagUpRight(); } else { pCurrentSquare = pCurrentSquare->GetDiagDownRight(); } } else { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetDiagUpLeft(); } else { pCurrentSquare = pCurrentSquare->GetDiagDownLeft(); } } pOccupant = pCurrentSquare->GetOccupant(); } // If we haven't reached the target it must be because // there is someone blocking our path if (pCurrentSquare != pTargetSquare) { bCanMoveTo = false; } } return bCanMoveTo; } // The ChessRook constructor initializes a piece ChessRook::ChessRook() : ChessMan() { SetType(Rook); SetValue(5.0); } // The ChessRook copy constructor duplicates a piece ChessRook::ChessRook(ChessRook& OldRook) : ChessMan(OldRook) { } // The ~ChessRook destructor is a stub function that does nothing ChessRook::~ChessRook() { } // The ChessRook assignment operator duplicates a piece ChessRook& ChessRook::operator=(ChessRook& OldRook) { if (&OldRook != this) { } return *this; } // CanMoveTo determines whether the piece can reach the target // square. bool ChessRook::CanMoveTo(char TargetFile, char TargetRank, bool Capture) { bool bCanMoveTo = false; // set default return code char cCurrentFile; char cCurrentRank; int nFileDelta; int nRankDelta; ChessBoard* pBoard; ChessSquare* pCurrentSquare; ChessSquare* pTargetSquare; ChessMan* pOccupant; // Get our current location cCurrentFile = GetFile(); cCurrentRank = GetRank(); pCurrentSquare = GetSquare(); // Get the target location pBoard = pCurrentSquare->GetBoard(); pTargetSquare = pBoard->GetSquare(TargetFile, TargetRank); // Rooks move straight along files and ranks if (cCurrentFile == TargetFile) { if (cCurrentRank != TargetRank) { bCanMoveTo = true; nRankDelta = TargetRank - cCurrentRank; // Get the next square on the file if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetFileUp(); } else { pCurrentSquare = pCurrentSquare->GetFileDown(); } // Get occupant of the square pOccupant = pCurrentSquare->GetOccupant(); // Traverse the file until we reach the target // or until we are obstructed while ((pCurrentSquare != pTargetSquare) && (pOccupant == NULL)) { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetFileUp(); } else { pCurrentSquare = pCurrentSquare->GetFileDown(); } pOccupant = pCurrentSquare->GetOccupant(); } // If we haven't reached the target it must be // because there is someone blocking our path if (pCurrentSquare != pTargetSquare) { bCanMoveTo = false; } } } else if (cCurrentRank == TargetRank) { if (cCurrentFile != TargetFile) { bCanMoveTo = true; nFileDelta = TargetFile - cCurrentFile; // Get the next square on the file if (nFileDelta > 0) { pCurrentSquare = pCurrentSquare->GetRankRight(); } else { pCurrentSquare = pCurrentSquare->GetRankLeft(); } // Get occupant of the square pOccupant = pCurrentSquare->GetOccupant(); // Traverse the rank until we reach the target // or until we are obstructed while ((pCurrentSquare != pTargetSquare) && (pOccupant == NULL)) { if (nFileDelta > 0) { pCurrentSquare = pCurrentSquare->GetRankRight(); } else { pCurrentSquare = pCurrentSquare->GetRankLeft(); } pOccupant = pCurrentSquare->GetOccupant(); } // If we haven't reached the target it must be // because there is someone blocking our path if (pCurrentSquare != pTargetSquare) { bCanMoveTo = false; } } } return bCanMoveTo; } // The ChessQueen constructor initializes a piece ChessQueen::ChessQueen() : ChessMan() { SetType(Queen); SetValue(9.0); } // The ChessQueen copy constructor duplicates a piece ChessQueen::ChessQueen(ChessQueen& OldQueen) : ChessMan(OldQueen) { } // The ~ChessQueen destructor is a stub function that does nothing ChessQueen::~ChessQueen() { } // The ChessQueen assignment operator duplicates a piece ChessQueen& ChessQueen::operator=(ChessQueen& OldQueen) { if (&OldQueen != this) { } return *this; } // CanMoveTo determines whether the piece can reach the target // square. bool ChessQueen::CanMoveTo(char TargetFile, char TargetRank, bool Capture) { bool bCanMoveTo = false; // set default return code char cCurrentFile; char cCurrentRank; int nFileDelta; int nRankDelta; ChessBoard* pBoard; ChessSquare* pCurrentSquare; ChessSquare* pTargetSquare; ChessMan* pOccupant; // Get the current location cCurrentFile = GetFile(); cCurrentRank = GetRank(); pCurrentSquare = GetSquare(); // Get the target location pBoard = pCurrentSquare->GetBoard(); pTargetSquare = pBoard->GetSquare(TargetFile, TargetRank); // Queens may move straight along files, ranks and diagonals if (cCurrentFile == TargetFile) { if (cCurrentRank != TargetRank) { bCanMoveTo = true; nRankDelta = TargetRank - cCurrentRank; // Get the next square on the file if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetFileUp(); } else { pCurrentSquare = pCurrentSquare->GetFileDown(); } // Get occupant of the square pOccupant = pCurrentSquare->GetOccupant(); // Traverse the file until we reach the target // or until we are obstructed while ((pCurrentSquare != pTargetSquare) && (pOccupant == NULL)) { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetFileUp(); } else { pCurrentSquare = pCurrentSquare->GetFileDown(); } pOccupant = pCurrentSquare->GetOccupant(); } // If we haven't reached the target it must be // because there is someone blocking our path if (pCurrentSquare != pTargetSquare) { bCanMoveTo = false; } } } else if (cCurrentRank == TargetRank) { if (cCurrentFile != TargetFile) { bCanMoveTo = true; nFileDelta = TargetFile - cCurrentFile; // Get the next square on the rank if (nFileDelta > 0) { pCurrentSquare = pCurrentSquare->GetRankRight(); } else { pCurrentSquare = pCurrentSquare->GetRankLeft(); } // Get occupant of the square pOccupant = pCurrentSquare->GetOccupant(); // Traverse the rank until we reach the target // or until we are obstructed while ((pCurrentSquare != pTargetSquare) && (pOccupant == NULL)) { if (nFileDelta > 0) { pCurrentSquare = pCurrentSquare->GetRankRight(); } else { pCurrentSquare = pCurrentSquare->GetRankLeft(); } pOccupant = pCurrentSquare->GetOccupant(); } // If we haven't reached the target it must be // because there is someone blocking our path if (pCurrentSquare != pTargetSquare) { bCanMoveTo = false; } } } else { nFileDelta = TargetFile - cCurrentFile; nRankDelta = TargetRank - cCurrentRank; if ((abs(nFileDelta) == abs(nRankDelta)) && (nFileDelta != 0)) { bCanMoveTo = true; // Get the next square on the correct diagonal if (nFileDelta > 0) { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetDiagUpRight(); } else { pCurrentSquare = pCurrentSquare->GetDiagDownRight(); } } else { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetDiagUpLeft(); } else { pCurrentSquare = pCurrentSquare->GetDiagDownLeft(); } } // Get occupant of the square pOccupant = pCurrentSquare->GetOccupant(); // Traverse the diagonal until we reach the target // or until we are obstructed while ((pCurrentSquare != pTargetSquare) && (pOccupant == NULL)) { if (nFileDelta > 0) { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetDiagUpRight(); } else { pCurrentSquare = pCurrentSquare->GetDiagDownRight(); } } else { if (nRankDelta > 0) { pCurrentSquare = pCurrentSquare->GetDiagUpLeft(); } else { pCurrentSquare = pCurrentSquare->GetDiagDownLeft(); } } pOccupant = pCurrentSquare->GetOccupant(); } // If we haven't reached the target it must be // because there is someone blocking our path if (pCurrentSquare != pTargetSquare) { bCanMoveTo = false; } } } return bCanMoveTo; } // The ChessKing constructor initializes a piece ChessKing::ChessKing() : ChessMan() { SetType(King); SetValue(1000.0); } // The ChessKing copy constructor duplicates a piece ChessKing::ChessKing(ChessKing& OldKing) : ChessMan(OldKing) { } // The ~ChessKing destructor is a stub function that does nothing ChessKing::~ChessKing() { } // The ChessKing assignment operator duplicates a piece ChessKing& ChessKing::operator=(ChessKing& OldKing) { if (&OldKing != this) { } return *this; } // CanMoveTo determines whether the piece can reach the target // square. bool ChessKing::CanMoveTo(char TargetFile, char TargetRank, bool Capture) { bool bCanMoveTo = false; // set default return code char cCurrentFile; char cCurrentRank; int nFileDelta; int nRankDelta; // Get the current location cCurrentFile = GetFile(); cCurrentRank = GetRank(); // Get the offset to the target location nFileDelta = abs(cCurrentFile - TargetFile); nRankDelta = abs(cCurrentRank - TargetRank); // A king may only move one square in any direction if ((nFileDelta <= 1) && (nRankDelta <= 1) && ((nFileDelta != 0) || (nRankDelta != 0))) { bCanMoveTo = true; } return bCanMoveTo; } // The ChessSquare constructor sets all member variables // to their correct value ChessSquare::ChessSquare() : m_bLight(true), m_File('?'), m_Rank('?'), m_bAttackAnalyzed(false), m_pBoard(NULL), m_pFileUp(NULL), m_pFileDown(NULL), m_pRankLeft(NULL), m_pRankRight(NULL), m_pDiagUpLeft(NULL), m_pDiagUpRight(NULL), m_pDiagDownLeft(NULL), m_pDiagDownRight(NULL), m_pOccupant(NULL) { m_AttackCount[0] = 0; m_AttackCount[1] = 0; m_AttackValue[0] = 0.0; m_AttackValue[1] = 0.0; } // The ChessSquare copy constructor duplicates an existing // square ChessSquare::ChessSquare(ChessSquare& OldSquare) { m_bLight = OldSquare.m_bLight; m_File = OldSquare.m_File; m_Rank = OldSquare.m_Rank; m_bAttackAnalyzed = false; m_pBoard = NULL; m_pFileUp = NULL; m_pFileDown = NULL; m_pRankLeft = NULL; m_pRankRight = NULL; m_pDiagUpLeft = NULL; m_pDiagUpRight = NULL; m_pDiagDownLeft = NULL; m_pDiagDownRight = NULL; m_pOccupant = NULL; m_AttackCount[0] = 0; m_AttackCount[1] = 0; m_AttackValue[0] = 0.0; m_AttackValue[1] = 0.0; } // The ~ChessSquare destructor is a stub function that does // nothing. ChessSquare::~ChessSquare() { } // The ChessSquare assignment operator duplicates an existing // move ChessSquare& ChessSquare::operator=(ChessSquare& OldSquare) { if (&OldSquare != this) { m_bLight = OldSquare.m_bLight; m_File = OldSquare.m_File; m_Rank = OldSquare.m_Rank; m_bAttackAnalyzed = false; m_pBoard = NULL; m_pFileUp = NULL; m_pFileDown = NULL; m_pRankLeft = NULL; m_pRankRight = NULL; m_pDiagUpLeft = NULL; m_pDiagUpRight = NULL; m_pDiagDownLeft = NULL; m_pDiagDownRight = NULL; m_pOccupant = NULL; m_AttackCount[0] = 0; m_AttackCount[1] = 0; m_AttackValue[0] = 0.0; m_AttackValue[1] = 0.0; } return *this; } // SetColorLight sets the color of the square void ChessSquare::SetColorLight(bool Light) { m_bLight = Light; } // IsColorLight determines whether or not the square is light bool ChessSquare::IsColorLight() { return m_bLight; } // SetFile sets the file id of the square void ChessSquare::SetFile(char File) { if ((File >= FileBase) && (File <= FileBase + (FileCount - 1))) { m_File = File; } } // SetFile retrieves the file id of the square char ChessSquare::GetFile() { return m_File; } // SetFile sets the rank id of the square void ChessSquare::SetRank(char Rank) { if ((Rank >= RankBase) && (Rank <= RankBase + (RankCount - 1))) { m_Rank = Rank; } } // SetFile retrieves the rank id of the square char ChessSquare::GetRank() { return m_Rank; } // GetID gets the file/rank id of the square (eg a1, e4, etc.) std::string ChessSquare::GetID() { std::string ID; ID = GetFile(); ID += GetRank(); return ID; } // SetBoard sets the board pointer for the square void ChessSquare::SetBoard(ChessBoard* pBoard) { m_pBoard = pBoard; } // GetBoard gets the board pointer for the square ChessBoard* ChessSquare::GetBoard() { return m_pBoard; } // SetOccupant sets the pointer to the pawn/piece on the square. // Setting this pointer to NULL indicates square is empty. void ChessSquare::SetOccupant(ChessMan* pChessMan) { m_pOccupant = pChessMan; } // ResetAttack resets the variables that track how each side // attacks the square. void ChessSquare::ResetAttack() { m_bAttackAnalyzed = false; m_AttackCount[0] = 0; m_AttackCount[1] = 0; m_AttackValue[0] = 0.0; m_AttackValue[1] = 0.0; } // UpdateAttack updates the variables that track how a side // attacks the square. void ChessSquare::UpdateAttack(bool ByWhite, float AttackValue) { if (ByWhite) { m_AttackCount[0]++; m_AttackValue[0] += AttackValue; } else { m_AttackCount[1]++; m_AttackValue[1] += AttackValue; } } // GetAttackCount gets the number of men that attack this // square by the designated side. int ChessSquare::GetAttackCount(bool ByWhite) { if (!m_bAttackAnalyzed) { m_pBoard->AnalyzeAttack(); } if (ByWhite) { return m_AttackCount[WHITE]; } else { return m_AttackCount[BLACK]; } } // GetAttackValue gets the cumulative value of the men that // attack this square by the designated side. float ChessSquare::GetAttackValue(bool ByWhite) { if (!m_bAttackAnalyzed) { m_pBoard->AnalyzeAttack(); } if (ByWhite) { return m_AttackValue[WHITE]; } else { return m_AttackValue[BLACK]; } } // AddPiece places a new pawn/piece on this square. Mutual // pointers are updated. void ChessSquare::AddPiece(ChessMan* pChessMan) { m_pOccupant = pChessMan; pChessMan->SetSquare(this); } // RemovePiece removes a pawn/piece from this square. It // calls the ChessBoard::RemovePiece because the board "owns" // all of the ChessMan. void ChessSquare::RemovePiece() { m_pBoard->RemovePiece(m_File, m_Rank); } // PickUpPiece temporarily removes the piece from the square. // This is used in conjunction with SetDownPiece to move a piece // from one square to another. void ChessSquare::PickUpPiece() { m_pOccupant = NULL; } // SetDownPiece returns a piece to a square after it has been // temporarily removed. This is used in conjunction with // SetDownPiece to move a piece from one square to another. void ChessSquare::SetDownPiece(ChessMan* pChessMan) { m_pOccupant = pChessMan; pChessMan->m_pSquare = this; } // ClearSquare sets the occupant pointer to NULL. When a piece // is removed from a square, ChessSquare::RemovePiece invokes // ChessBoard::RemovePiece which calls ClearSquare. void ChessSquare::ClearSquare() { m_pOccupant = NULL; } // The ChessBoard constructor initializes member variables, // generates an array of ChessSquare objects, and links those // square along files, ranks and diagonals. ChessBoard::ChessBoard() : m_bWhiteToMove(true), m_EnPassantTarget(""), m_nHalfMoveClock(0), m_nFullMoveNumber(1) { int nFile; int nRank; bool bLight; ChessSquare* pSquare; for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { pSquare = new ChessSquare(); pSquare->SetBoard(this); bLight = true; if ((nRank % 2) == 0) { if ((nFile % 2) == 0) { bLight = false; } } else { if ((nFile % 2) != 0) { bLight = false; } } pSquare->SetColorLight(bLight); pSquare->SetFile(nFile + FileBase); pSquare->SetRank(nRank + RankBase); m_pSquare[nFile][nRank] = pSquare; } } m_bMayCastleKingside[WHITE] = true; m_bMayCastleQueenside[WHITE] = true; m_bMayCastleKingside[BLACK] = true; m_bMayCastleQueenside[BLACK] = true; LinkSquares(); ResetAttack(); } // The ChessBoard copy constructor makes a copy of an existing // ChessBoard object. So that the new object is independent of // the old object, an entire new set of ChessSquare objects and // ChessMan objects is generated. ChessBoard::ChessBoard(ChessBoard& OldBoard) { int nFile; int nRank; ChessSquare* pSquare; PieceList::iterator pPiece; ChessMan* pOldMan; ChessPawn* pChessPawn; ChessKnight* pChessKnight; ChessBishop* pChessBishop; ChessRook* pChessRook; ChessQueen* pChessQueen; ChessKing* pChessKing; ChessMan* pChessMan; char cFile; char cRank; m_bWhiteToMove = OldBoard.m_bWhiteToMove; m_EnPassantTarget = OldBoard.m_EnPassantTarget; m_nHalfMoveClock = OldBoard.m_nHalfMoveClock; m_nFullMoveNumber = OldBoard.m_nFullMoveNumber; m_bMayCastleKingside[WHITE] = OldBoard.m_bMayCastleKingside[WHITE]; m_bMayCastleQueenside[WHITE] = OldBoard.m_bMayCastleQueenside[WHITE]; m_bMayCastleKingside[BLACK] = OldBoard.m_bMayCastleKingside[BLACK]; m_bMayCastleQueenside[BLACK] = OldBoard.m_bMayCastleQueenside[BLACK]; for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { pSquare = new ChessSquare(*(OldBoard.m_pSquare[nFile][nRank])); pSquare->SetBoard(this); m_pSquare[nFile][nRank] = pSquare; } } LinkSquares(); pPiece = OldBoard.m_PieceList.begin(); while (pPiece != OldBoard.m_PieceList.end()) { pOldMan = *pPiece; pSquare = pOldMan->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); switch (pOldMan->GetType()) { case Pawn: pChessPawn = (ChessPawn*) pOldMan; pChessMan = new ChessPawn(*pChessPawn); break; case Knight: pChessKnight = (ChessKnight*) pOldMan; pChessMan = new ChessKnight(*pChessKnight); break; case Bishop: pChessBishop = (ChessBishop*) pOldMan; pChessMan = new ChessBishop(*pChessBishop); break; case Rook: pChessRook = (ChessRook*) pOldMan; pChessMan = new ChessRook(*pChessRook); break; case Queen: pChessQueen = (ChessQueen*) pOldMan; pChessMan = new ChessQueen(*pChessQueen); break; case King: pChessKing = (ChessKing*) pOldMan; pChessMan = new ChessKing(*pChessKing); break; } AddPiece(cFile, cRank, pChessMan); pPiece++; } ResetAttack(); } // The ~ChessBoard destructor deletes all of its associated // ChessSquare and ChessMan objects. ChessBoard::~ChessBoard() { int nFile; int nRank; ChessSquare* pSquare; ChessMan* pChessMan; for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { pSquare = m_pSquare[nFile][nRank]; pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { delete pChessMan; } delete pSquare; m_pSquare[nFile][nRank] = NULL; } } } // The ChessBoard assignment operator makes a copy of an existing // ChessBoard object. So that the new object is independent of // the old object, an entire new set of ChessSquare objects and // ChessMan objects is generated. ChessBoard& ChessBoard::operator=(ChessBoard& OldBoard) { int nFile; int nRank; ChessSquare* pSquare; PieceList::iterator pPiece; ChessMan* pOldMan; ChessPawn* pChessPawn; ChessKnight* pChessKnight; ChessBishop* pChessBishop; ChessRook* pChessRook; ChessQueen* pChessQueen; ChessKing* pChessKing; ChessMan* pChessMan; char cFile; char cRank; if (&OldBoard != this) { m_bWhiteToMove = OldBoard.m_bWhiteToMove; m_EnPassantTarget = OldBoard.m_EnPassantTarget; m_nHalfMoveClock = OldBoard.m_nHalfMoveClock; m_nFullMoveNumber = OldBoard.m_nFullMoveNumber; m_bMayCastleKingside[WHITE] = OldBoard.m_bMayCastleKingside[WHITE]; m_bMayCastleQueenside[WHITE] = OldBoard.m_bMayCastleQueenside[WHITE]; m_bMayCastleKingside[BLACK] = OldBoard.m_bMayCastleKingside[BLACK]; m_bMayCastleQueenside[BLACK] = OldBoard.m_bMayCastleQueenside[BLACK]; for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { pSquare = new ChessSquare(*(OldBoard.m_pSquare[nFile][nRank])); pSquare->SetBoard(this); m_pSquare[nFile][nRank] = pSquare; } } LinkSquares(); pPiece = OldBoard.m_PieceList.begin(); while (pPiece != OldBoard.m_PieceList.end()) { pOldMan = *pPiece; pSquare = pOldMan->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); switch (pOldMan->GetType()) { case Pawn: pChessPawn = (ChessPawn*) pOldMan; pChessMan = new ChessPawn(*pChessPawn); break; case Knight: pChessKnight = (ChessKnight*) pOldMan; pChessMan = new ChessKnight(*pChessKnight); break; case Bishop: pChessBishop = (ChessBishop*) pOldMan; pChessMan = new ChessBishop(*pChessBishop); break; case Rook: pChessRook = (ChessRook*) pOldMan; pChessMan = new ChessRook(*pChessRook); break; case Queen: pChessQueen = (ChessQueen*) pOldMan; pChessMan = new ChessQueen(*pChessQueen); break; case King: pChessKing = (ChessKing*) pOldMan; pChessMan = new ChessKing(*pChessKing); break; } AddPiece(cFile, cRank, pChessMan); pPiece++; } ResetAttack(); } return *this; } // Reset removes all of the ChessMan objects from the board. void ChessBoard::Reset() { int nFile; int nRank; ChessSquare* pSquare; ChessMan* pChessMan; for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { pSquare = m_pSquare[nFile][nRank]; pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { delete pChessMan; pSquare->SetOccupant(NULL); } } } m_PieceList.clear(); ResetAttack(); } // GetSquare returns a pointer to the square at the specified // file and rank. ChessSquare* ChessBoard::GetSquare(char File, char Rank) { assert((File >= FileBase) && (File < (FileBase + FileCount))); assert((Rank >= RankBase) && (Rank < (RankBase + RankCount))); assert(m_pSquare[File - FileBase][Rank - RankBase] != NULL); return m_pSquare[File - FileBase][Rank - RankBase]; } // GetSquare returns a pointer to the square at the specified // id. ChessSquare* ChessBoard::GetSquare(std::string ID) { char File; char Rank; int Size; Size = ID.size(); assert(Size == 2); // Break the ID into file and rank if (Size == 2) { File = ID[0]; if ((File < FileBase) || (File >= FileBase + FileCount)) { File = FileBase; } Rank = ID[1]; if ((Rank < RankBase) || (Rank >= RankBase + RankCount)) { Rank = RankBase; } } else { File = FileBase; Rank = RankBase; } return GetSquare(File, Rank); } // SetPiecePlacement sets up the board according to an EPD // piece placement string. void ChessBoard::SetPiecePlacement(EPD PiecePlacement) { char* pOneRank; char* pRank[RankCount]; char* pEpdSquare; int nSquaresToSkip; int nRankNo; int nFileNo; char cRank; char cFile; char RankSeparator[] = "/"; // Remove any existing pawns/pieces. Reset(); // Parse EPD string into ranks. nRankNo = RankCount - 1; pOneRank = strtok(PiecePlacement.begin(), RankSeparator); while (pOneRank != NULL) { pRank[nRankNo] = pOneRank; nRankNo--; pOneRank = strtok(NULL, RankSeparator); } ChessMan* pChessMan; bool bWhite; char cManType; // Parse each rank into its constituent parts; pawn/piece // identifiers and empty square placeholders. for (nRankNo = 0, cRank = '1'; nRankNo < RankCount; nRankNo++, cRank++) { pOneRank = pRank[nRankNo]; cFile = 'a'; nFileNo = 0; pEpdSquare = pOneRank; while (pEpdSquare < pOneRank + strlen(pOneRank)) { if ((*pEpdSquare >= '1') && // Empty squares? (*pEpdSquare <= '8')) { // Skip empty squares nSquaresToSkip = *pEpdSquare - '1'; cFile += nSquaresToSkip; } else { // Get type of piece cManType = *pEpdSquare; // Check piece color, upper=white, lower=black if (isupper(cManType)) { bWhite = true; cManType = tolower(cManType); } else { bWhite = false; } // Generate proper piece switch (cManType) { case 'p': pChessMan = new ChessPawn(); break; case 'n': pChessMan = new ChessKnight(); break; case 'b': pChessMan = new ChessBishop(); break; case 'r': pChessMan = new ChessRook(); break; case 'q': pChessMan = new ChessQueen(); break; case 'k': pChessMan = new ChessKing(); break; } // Set color and add to board pChessMan->SetColorWhite(bWhite); AddPiece(cFile, cRank, pChessMan); } cFile++; pEpdSquare++; } } } // SetWhiteToMove provides a public interface to a protected // variable void ChessBoard::SetWhiteToMove(bool WhiteToMove) { m_bWhiteToMove = WhiteToMove; } // SetBlackToMove provides a public interface to a protected // variable void ChessBoard::SetBlackToMove(bool BlackToMove) { m_bWhiteToMove = !BlackToMove; } // SetCastleAvailability parses an EPD castle availability string // and sets boolean flags to indicate what castling is still // available. void ChessBoard::SetCastleAvailability(std::string CastleAvailability) { int nSize; int nIndex; std::string OneCastle; // default to no castling MayCastleKingside(false, WHITE); MayCastleQueenside(false, WHITE); MayCastleKingside(false, BLACK); MayCastleQueenside(false, BLACK); nSize = CastleAvailability.size(); nIndex = 0; while (nIndex < nSize) { // Parse the string, change the values OneCastle = CastleAvailability.substr(nIndex, 1); if (OneCastle == "K") { MayCastleKingside(true, WHITE); } else if (OneCastle == "Q") { MayCastleQueenside(true, WHITE); } else if (OneCastle == "k") { MayCastleKingside(true, BLACK); } else if (OneCastle == "q") { MayCastleQueenside(true, BLACK); } nIndex++; } } // SetEnPassantTarget provides a public interface to a protected // variable. void ChessBoard::SetEnPassantTarget(std::string EnPassantTarget) { m_EnPassantTarget = EnPassantTarget; } // SetFullMoveNumber provides a public interface to a protected // variable. void ChessBoard::SetFullMoveNumber(int FullMove) { m_nFullMoveNumber = FullMove; } // SetHalfMoveClock provides a public interface to a protected // variable. void ChessBoard::SetHalfMoveClock(int HalfMove) { m_nHalfMoveClock = HalfMove; } void ChessBoard::SetPosition(char* Position) { assert(false); // under construction } // Move is to root function for moving a piece on the board. void ChessBoard::Move(ChessMove& Move) { ChessSquare* pSquare; ChessMan* pOccupant; bool bWhiteToMove; bool bWhiteInCheck; bool bBlackInCheck; bool bWhiteInCheckmate; bool bBlackInCheckmate; std::string EnPassantTarget; char cEnPassantFile; char cEnPassantRank; // Who's turn is it to move? bWhiteToMove = IsWhiteToMove(); // First, some last-minute error checking common to all // moves. If we are capturing, the target square must have // a piece of the opposite color. If we are not capturing, // the target square must be empty. if ((!Move.m_bCastleKingside) && (!Move.m_bCastleQueenside)) { pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); pOccupant = pSquare->GetOccupant(); if (Move.m_bCapture) { if (pOccupant == NULL) { if (Move.m_ManToMove == Pawn) { EnPassantTarget = GetEnPassantTarget(); if (EnPassantTarget != "") { cEnPassantFile = EnPassantTarget[0]; cEnPassantRank = EnPassantTarget[1]; if ((Move.m_cTargetFile == cEnPassantFile) && (Move.m_cTargetRank == cEnPassantRank)) { if (cEnPassantRank == '3') { cEnPassantRank = '4'; } else { cEnPassantRank = '5'; } pSquare = GetSquare(cEnPassantFile, cEnPassantRank); pOccupant = pSquare->GetOccupant(); if (pOccupant == NULL) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Target square of capture is empty"; } else { if (bWhiteToMove == pOccupant->IsColorWhite()) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Capture must be opposite color"; } } } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Target square of capture is empty"; } } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Target square of capture is empty"; } } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Target square of capture is empty"; } } else { if (bWhiteToMove == pOccupant->IsColorWhite()) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Capture must be opposite color"; } } } else { if (pOccupant != NULL) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Target square of non-capture is occupied"; } } } // If we're still legal, move the proper type of piece. if (Move.m_bLegalMove) { switch (Move.m_ManToMove) { case Pawn: MovePawn(Move); break; case Knight: MoveKnight(Move); break; case Bishop: MoveBishop(Move); break; case Rook: MoveRook(Move); break; case Queen: MoveQueen(Move); break; case King: MoveKing(Move); break; } // Something has moved, the attack analysis is no longer // valid. ResetAttack(); if (Move.m_bLegalMove) { // Find out who's in check now bWhiteInCheck = IsKingChecked(WHITE); bBlackInCheck = IsKingChecked(BLACK); // Assume no checkmate for now. bWhiteInCheckmate = false; bBlackInCheckmate = false; // Make certain we haven't moved into check. // Also, if we have achieved check and this is the // last move in the PV, check to see if we've also // achieved checkmate. We do this by making a copy // of the board, and seeing if the other player has // a legal move. if (bWhiteToMove) { if (bWhiteInCheck) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "May not move into check"; } else if (bBlackInCheck) { if (Move.m_bLastInPv == true) { ChessBoard* pCheckmateBoard; MOVE_LIST CheckmateList; pCheckmateBoard = new ChessBoard(*this); pCheckmateBoard->SetEnPassantTarget(Move.m_EnPassantTarget); pCheckmateBoard ->SetWhiteToMove(!pCheckmateBoard->IsWhiteToMove()); CheckmateList = pCheckmateBoard->GetLegalMoveList(false); if (CheckmateList.size() == 0) { bBlackInCheckmate = true; } delete pCheckmateBoard; } } } else { if (bBlackInCheck) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "May not move into check"; } else if (bWhiteInCheck) { if (Move.m_bLastInPv) { ChessBoard* pCheckmateBoard; MOVE_LIST CheckmateList; pCheckmateBoard = new ChessBoard(*this); pCheckmateBoard->SetEnPassantTarget(Move.m_EnPassantTarget); pCheckmateBoard ->SetWhiteToMove(!pCheckmateBoard->IsWhiteToMove()); CheckmateList = pCheckmateBoard->GetLegalMoveList(false); if (CheckmateList.size() == 0) { bWhiteInCheckmate = true; } delete pCheckmateBoard; } } } // Check to make sure that any claims of check or // checkmate have been validated. if (Move.m_bLegalMove) { if (Move.m_bCheckmate) { if ((bWhiteToMove && !bBlackInCheckmate) || (!bWhiteToMove && !bWhiteInCheckmate)) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Move falsely indicates checkmate"; } } if (Move.m_bLegalMove) { if (Move.m_bCheck) { if ((bWhiteToMove && !bBlackInCheck) || (!bWhiteToMove && !bWhiteInCheck)) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Move falsely indicates check"; } } } } // If we've achieved check or checkmate but the move // didn't claim to achieve that state, we modify the // move and flag it so the user knows it's been fixed. if (Move.m_bLegalMove) { if (bWhiteToMove) { if ((bBlackInCheck) && (!Move.m_bCheck)) { Move.m_bCheck = true; Move.m_bMoveAltered = true; } if ((bBlackInCheckmate) && (!Move.m_bCheckmate)) { Move.m_bCheckmate = true; Move.m_bMoveAltered = true; } } else { if ((bWhiteInCheck) && (!Move.m_bCheck)) { Move.m_bCheck = true; Move.m_bMoveAltered = true; } if ((bWhiteInCheckmate) && (!Move.m_bCheckmate)) { Move.m_bCheckmate = true; Move.m_bMoveAltered = true; } } } // Finally, update the board so we're ready for // the next move. SetEnPassantTarget(Move.m_EnPassantTarget); if ((Move.m_ManToMove == Pawn) || (Move.m_bCapture)) { SetHalfMoveClock(0); } else { SetHalfMoveClock(GetHalfMoveClock() + 1); } SetWhiteToMove(!IsWhiteToMove()); if (IsWhiteToMove()) { SetFullMoveNumber(GetFullMoveNumber() + 1); } } } } // MovePawn moves a pawn. void ChessBoard::MovePawn(ChessMove& Move) { PieceList::iterator pPiece; ChessMan* pPawn; ChessMan* pCaptured; ChessSquare* pSquare; char cRank; char cFile; PieceList CandidateList; // Find the pawn to move. It must match the color that is // moving and be able to get to the target square. CandidateList.clear(); pPiece = m_PieceList.begin(); pPawn = *pPiece; while (pPiece != m_PieceList.end()) { pPawn = *pPiece; pSquare = pPawn->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); if ((pPawn->GetType() == Pawn) && (m_bWhiteToMove == pPawn->IsColorWhite()) && ((Move.m_cDisambigFile == ' ') || (Move.m_cDisambigFile == cFile)) && ((Move.m_cDisambigRank == ' ') || (Move.m_cDisambigRank == cRank))) { if (pPawn->CanMoveTo(Move.m_cTargetFile, Move.m_cTargetRank, Move.m_bCapture)) { CandidateList.push_back(pPawn); } } pPiece++; } // We should now have one and only one pawn that meets our // criteria. if (CandidateList.size() == 1) { pPiece = CandidateList.begin(); pPawn = *pPiece; pSquare = pPawn->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); // Detect and flag en passant moves if (abs(cRank - Move.m_cTargetRank) == 2) { Move.m_EnPassantTarget = cFile; Move.m_EnPassantTarget += ((cRank + Move.m_cTargetRank) / 2); } // Take the pawn off the board. pSquare->PickUpPiece(); if (Move.m_bCapture) { // Permanently remove the captured piece. pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); pCaptured = pSquare->GetOccupant(); if (pCaptured == NULL) // indicates en passant { if (m_bWhiteToMove) { pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank - 1); } else { pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank + 1); } pCaptured = pSquare->GetOccupant(); } pCaptured->Capture(); } // Set the pawn back on its new square. pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); pSquare->SetDownPiece(pPawn); // Handle pawn promotion if (Move.m_bPromote) { // Generate the new piece. ChessMan* pNewPiece; switch (Move.m_PromoteTo) { case Queen: pNewPiece = new ChessQueen(); break; case Knight: pNewPiece = new ChessKnight(); break; case Rook: pNewPiece = new ChessRook(); break; case Bishop: pNewPiece = new ChessBishop(); break; } pNewPiece->SetColorWhite(pPawn->IsColorWhite()); // "Capture" the pawn. pPawn->Capture(); // Replace it with the new piece. AddPiece(Move.m_cTargetFile, Move.m_cTargetRank, pNewPiece); } } else if (CandidateList.size() == 0) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "No pawn available for this move"; } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Internal Error! - pawn disambiguation"; } CheckCastlingAfterCapture(Move); } void ChessBoard::MoveKnight(ChessMove& Move) { PieceList::iterator pPiece; ChessMan* pKnight; ChessMan* pCaptured; ChessSquare* pSquare; char cRank; char cFile; PieceList CandidateList; PieceList FinalList; // Find all of the pieces that meet our criteria; proper // color, disambiguation-qualified, able to reach the // target square. CandidateList.clear(); pPiece = m_PieceList.begin(); pKnight = *pPiece; while (pPiece != m_PieceList.end()) { pKnight = *pPiece; pSquare = pKnight->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); if ((pKnight->GetType() == Knight) && (m_bWhiteToMove == pKnight->IsColorWhite()) && ((Move.m_cDisambigFile == ' ') || (Move.m_cDisambigFile == cFile)) && ((Move.m_cDisambigRank == ' ') || (Move.m_cDisambigRank == cRank))) { if (pKnight->CanMoveTo(Move.m_cTargetFile, Move.m_cTargetRank, Move.m_bCapture)) { CandidateList.push_back(pKnight); } } pPiece++; } // If we find a single piece that meets our criteria, make // the move. If we find multiple pieces, we must figure // which are eliminated because the are absolute-pinned. // If we can't get one and only one piece to move, it is // an error. if (CandidateList.size() == 1) { // Pick up the piece, if necessary remove a captured // piece, set the piece down. pPiece = CandidateList.begin(); pKnight = *pPiece; pSquare = pKnight->GetSquare(); pSquare->PickUpPiece(); pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); if (Move.m_bCapture) { pCaptured = pSquare->GetOccupant(); pCaptured->Capture(); } pSquare->SetDownPiece(pKnight); } else if (CandidateList.size() == 0) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "No knight available for this move"; } else { // Eliminate pieces the may not be moved because they // are pinned to their kings FinalList.clear(); pPiece = CandidateList.begin(); while (pPiece != CandidateList.end()) { pKnight = *pPiece; pSquare = pKnight->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); if (!AbsolutePinnedPiece(cFile, cRank, Move.m_cTargetFile, Move.m_cTargetRank, Move.m_bCapture, m_bWhiteToMove)) { FinalList.push_back(pKnight); } pPiece++; } if (FinalList.size() == 1) { // Pick up the piece, if necessary remove a captured // piece, set the piece down. pPiece = FinalList.begin(); pKnight = *pPiece; pSquare = pKnight->GetSquare(); pSquare->PickUpPiece(); pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); if (Move.m_bCapture) { pCaptured = pSquare->GetOccupant(); pCaptured->Capture(); } pSquare->SetDownPiece(pKnight); } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Disambiguation for knight failed"; } } CheckCastlingAfterCapture(Move); } void ChessBoard::MoveBishop(ChessMove& Move) { PieceList::iterator pPiece; ChessMan* pBishop; ChessMan* pCaptured; ChessSquare* pSquare; char cRank; char cFile; PieceList CandidateList; PieceList FinalList; // Find all of the pieces that meet our criteria; proper // color, disambiguation-qualified, able to reach the // target square. CandidateList.clear(); pPiece = m_PieceList.begin(); pBishop = *pPiece; while (pPiece != m_PieceList.end()) { pBishop = *pPiece; pSquare = pBishop->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); if ((pBishop->GetType() == Bishop) && (m_bWhiteToMove == pBishop->IsColorWhite()) && ((Move.m_cDisambigFile == ' ') || (Move.m_cDisambigFile == cFile)) && ((Move.m_cDisambigRank == ' ') || (Move.m_cDisambigRank == cRank))) { if (pBishop->CanMoveTo(Move.m_cTargetFile, Move.m_cTargetRank, Move.m_bCapture)) { CandidateList.push_back(pBishop); } } pPiece++; } // If we find a single piece that meets our criteria, make // the move. If we find multiple pieces, we must figure // which are eliminated because the are absolute-pinned. // If we can't get one and only one piece to move, it is // an error. if (CandidateList.size() == 1) { // Pick up the piece, if necessary remove a captured // piece, set the piece down. pPiece = CandidateList.begin(); pBishop = *pPiece; pSquare = pBishop->GetSquare(); pSquare->PickUpPiece(); pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); if (Move.m_bCapture) { pCaptured = pSquare->GetOccupant(); pCaptured->Capture(); } pSquare->SetDownPiece(pBishop); } else if (CandidateList.size() == 0) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "No bishop available for this move"; } else { // Eliminate pieces that may not be moved because they // are pinned to their kings FinalList.clear(); pPiece = CandidateList.begin(); while (pPiece != CandidateList.end()) { pBishop = *pPiece; pSquare = pBishop->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); if (!AbsolutePinnedPiece(cFile, cRank, Move.m_cTargetFile, Move.m_cTargetRank, Move.m_bCapture, m_bWhiteToMove)) { FinalList.push_back(pBishop); } pPiece++; } if (FinalList.size() == 1) { // Pick up the piece, if necessary remove a captured // piece, set the piece down. pPiece = FinalList.begin(); pBishop = *pPiece; pSquare = pBishop->GetSquare(); pSquare->PickUpPiece(); pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); if (Move.m_bCapture) { pCaptured = pSquare->GetOccupant(); pCaptured->Capture(); } pSquare->SetDownPiece(pBishop); } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Disambiguation for bishop failed"; } } CheckCastlingAfterCapture(Move); } void ChessBoard::MoveRook(ChessMove& Move) { PieceList::iterator pPiece; ChessMan* pRook; ChessMan* pCaptured; ChessSquare* pSquare; char cRank; char cFile; PieceList CandidateList; PieceList FinalList; // Find all of the pieces that meet our criteria; proper // color, disambiguation-qualified, able to reach the // target square. CandidateList.clear(); pPiece = m_PieceList.begin(); pRook = *pPiece; while (pPiece != m_PieceList.end()) { pRook = *pPiece; pSquare = pRook->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); if ((pRook->GetType() == Rook) && (m_bWhiteToMove == pRook->IsColorWhite()) && ((Move.m_cDisambigFile == ' ') || (Move.m_cDisambigFile == cFile)) && ((Move.m_cDisambigRank == ' ') || (Move.m_cDisambigRank == cRank))) { if (pRook->CanMoveTo(Move.m_cTargetFile, Move.m_cTargetRank, Move.m_bCapture)) { CandidateList.push_back(pRook); } } pPiece++; } // If we find a single piece that meets our criteria, make // the move. If we find multiple pieces, we must figure // which are eliminated because the are absolute-pinned. // If we can't get one and only one piece to move, it is // an error. if (CandidateList.size() == 1) { // Pick up the piece, if necessary remove a captured // piece, set the piece down. pPiece = CandidateList.begin(); pRook = *pPiece; pSquare = pRook->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); // Moving a rook may negate future ability to castle. if (pRook->IsColorWhite()) { if (cRank == '1') { if (cFile == 'a') { MayCastleQueenside(false, WHITE); } else if (cFile == 'h') { MayCastleKingside(false, WHITE); } } } else { if (cRank == '8') { if (cFile == 'a') { MayCastleQueenside(false, BLACK); } else if (cFile == 'h') { MayCastleKingside(false, BLACK); } } } pSquare->PickUpPiece(); pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); if (Move.m_bCapture) { pCaptured = pSquare->GetOccupant(); pCaptured->Capture(); } pSquare->SetDownPiece(pRook); } else if (CandidateList.size() == 0) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "No rook available for this move"; } else { // Eliminate pieces that may not be moved because they // are pinned to their kings FinalList.clear(); pPiece = CandidateList.begin(); while (pPiece != CandidateList.end()) { pRook = *pPiece; pSquare = pRook->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); if (!AbsolutePinnedPiece(cFile, cRank, Move.m_cTargetFile, Move.m_cTargetRank, Move.m_bCapture, m_bWhiteToMove)) { FinalList.push_back(pRook); } pPiece++; } if (FinalList.size() == 1) { // Pick up the piece, if necessary remove a captured // piece, set the piece down. pPiece = FinalList.begin(); pRook = *pPiece; pSquare = pRook->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); // Moving a rook may negate future ability to castle. if (pRook->IsColorWhite()) { if (cRank == '1') { if (cFile == 'a') { MayCastleQueenside(false, WHITE); } else if (cFile == 'h') { MayCastleKingside(false, WHITE); } } } else { if (cRank == '8') { if (cFile == 'a') { MayCastleQueenside(false, BLACK); } else if (cFile == 'h') { MayCastleKingside(false, BLACK); } } } pSquare->PickUpPiece(); pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); if (Move.m_bCapture) { pCaptured = pSquare->GetOccupant(); pCaptured->Capture(); } pSquare->SetDownPiece(pRook); } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Disambiguation for rook failed"; } } CheckCastlingAfterCapture(Move); } void ChessBoard::MoveQueen(ChessMove& Move) { PieceList::iterator pPiece; ChessMan* pQueen; ChessMan* pCaptured; ChessSquare* pSquare; char cRank; char cFile; PieceList CandidateList; PieceList FinalList; // Find all of the pieces that meet our criteria; proper // color, disambiguation-qualified, able to reach the // target square. CandidateList.clear(); pPiece = m_PieceList.begin(); pQueen = *pPiece; while (pPiece != m_PieceList.end()) { pQueen = *pPiece; pSquare = pQueen->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); if ((pQueen->GetType() == Queen) && (m_bWhiteToMove == pQueen->IsColorWhite()) && ((Move.m_cDisambigFile == ' ') || (Move.m_cDisambigFile == cFile)) && ((Move.m_cDisambigRank == ' ') || (Move.m_cDisambigRank == cRank))) { if (pQueen->CanMoveTo(Move.m_cTargetFile, Move.m_cTargetRank, Move.m_bCapture)) { CandidateList.push_back(pQueen); } } pPiece++; } // If we find a single piece that meets our criteria, make // the move. If we find multiple pieces, we must figure // which are eliminated because the are absolute-pinned. // If we can't get one and only one piece to move, it is // an error. if (CandidateList.size() == 1) { // Pick up the piece, if necessary remove a captured // piece, set the piece down. pPiece = CandidateList.begin(); pQueen = *pPiece; pSquare = pQueen->GetSquare(); pSquare->PickUpPiece(); pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); if (Move.m_bCapture) { pCaptured = pSquare->GetOccupant(); pCaptured->Capture(); } pSquare->SetDownPiece(pQueen); } else if (CandidateList.size() == 0) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "No queen available for this move"; } else { // Eliminate pieces that may not be moved because they // are pinned to their kings FinalList.clear(); pPiece = CandidateList.begin(); while (pPiece != CandidateList.end()) { pQueen = *pPiece; pSquare = pQueen->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); if (!AbsolutePinnedPiece(cFile, cRank, Move.m_cTargetFile, Move.m_cTargetRank, Move.m_bCapture, m_bWhiteToMove)) { FinalList.push_back(pQueen); } pPiece++; } if (FinalList.size() == 1) { // Pick up the piece, if necessary remove a captured // piece, set the piece down. pPiece = FinalList.begin(); pQueen = *pPiece; pSquare = pQueen->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); pSquare->PickUpPiece(); pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); if (Move.m_bCapture) { pCaptured = pSquare->GetOccupant(); pCaptured->Capture(); } pSquare->SetDownPiece(pQueen); } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Disambiguation for queen failed"; } } CheckCastlingAfterCapture(Move); } void ChessBoard::MoveKing(ChessMove& Move) { PieceList::iterator pPiece; ChessMan* pKing; ChessMan* pRook; ChessMan* pCaptured; ChessSquare* pSquare; ChessSquare* pKingSquare; ChessSquare* pRookSquare; ChessSquare* pAdjacentSquare1; ChessSquare* pAdjacentSquare2; ChessSquare* pAdjacentSquare3; char cRank; char cFile; int Color; // Find the king of the proper color. if (m_bWhiteToMove) { cRank = '1'; Color = WHITE; } else { cRank = '8'; Color = BLACK; } pPiece = m_PieceList.begin(); while (pPiece != m_PieceList.end()) { pKing = *pPiece; if ((pKing->GetType() == King) && (m_bWhiteToMove == pKing->IsColorWhite())) { break; } pPiece++; } // If we are castling, there is extensive error checking // to do first. Castling to the designated side must still // be allowed. The king may not be in check. The squares // the king is movig through may not be attacked. if (Move.m_bCastleKingside) { if (MayCastleKingside(Color)) { pKingSquare = GetSquare('e', cRank); pAdjacentSquare1 = GetSquare('f', cRank); pAdjacentSquare2 = GetSquare('g', cRank); pRookSquare = GetSquare('h', cRank); pKing = pKingSquare->GetOccupant(); pRook = pRookSquare->GetOccupant(); if ((pKing == NULL) || (pKing->GetType() != King) || (pKing->IsColorWhite() != m_bWhiteToMove) || (pRook == NULL) || (pRook->GetType() != Rook) || (pRook->IsColorWhite() != m_bWhiteToMove) || (pKingSquare->GetAttackCount(!m_bWhiteToMove) > 0) || (pAdjacentSquare1->GetAttackCount(!m_bWhiteToMove) > 0) || (pAdjacentSquare2->GetAttackCount(!m_bWhiteToMove) > 0) || (pAdjacentSquare1->GetOccupant() != NULL) || (pAdjacentSquare2->GetOccupant() != NULL)) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Request to castle invalid"; } else { // Pick up the king, set it down. Repeat with // the rook. pKingSquare->PickUpPiece(); pAdjacentSquare2->SetDownPiece(pKing); pRookSquare->PickUpPiece(); pAdjacentSquare1->SetDownPiece(pRook); } } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Request to castle invalid"; } } else if (Move.m_bCastleQueenside) { if (MayCastleQueenside(Color)) { pKingSquare = GetSquare('e', cRank); pAdjacentSquare1 = GetSquare('d', cRank); pAdjacentSquare2 = GetSquare('c', cRank); pAdjacentSquare3 = GetSquare('b', cRank); pRookSquare = GetSquare('a', cRank); pKing = pKingSquare->GetOccupant(); pRook = pRookSquare->GetOccupant(); if ((pKing == NULL) || (pKing->GetType() != King) || (pKing->IsColorWhite() != m_bWhiteToMove) || (pRook == NULL) || (pRook->GetType() != Rook) || (pRook->IsColorWhite() != m_bWhiteToMove) || (pKingSquare->GetAttackCount(!m_bWhiteToMove) > 0) || (pAdjacentSquare1->GetAttackCount(!m_bWhiteToMove) > 0) || (pAdjacentSquare2->GetAttackCount(!m_bWhiteToMove) > 0) || (pAdjacentSquare1->GetOccupant() != NULL) || (pAdjacentSquare2->GetOccupant() != NULL) || (pAdjacentSquare3->GetOccupant() != NULL)) { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Request to castle invalid"; } else { // Pick up the king, set it down. Repeat with // the rook. pKingSquare->PickUpPiece(); pAdjacentSquare2->SetDownPiece(pKing); pRookSquare->PickUpPiece(); pAdjacentSquare1->SetDownPiece(pRook); } } else { Move.m_bLegalMove = false; Move.m_ErrorMessage = "Request to castle invalid"; } } else { // If we're not castling, it's a simple move. Pick up // the king, remove any captured piece, and set the // king down. pSquare = pKing->GetSquare(); cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); pSquare->PickUpPiece(); pSquare = GetSquare(Move.m_cTargetFile, Move.m_cTargetRank); if (Move.m_bCapture) { pCaptured = pSquare->GetOccupant(); pCaptured->Capture(); } pSquare->SetDownPiece(pKing); } // Any king move means we can no longer castle. MayCastleKingside(false, Color); MayCastleQueenside(false, Color); CheckCastlingAfterCapture(Move); } // GetPiecePlacement returns a string that represents the piece // placement data for an EPD (of FEN) string. EPD ChessBoard::GetPiecePlacement() { EPD PiecePlacement; int nSquaresToSkip; int nRank; int nFile; ChessSquare* pSquare; ChessMan* pChessMan; char cManSymbol; char cFormat[5]; char RankSeparator[] = "/"; PiecePlacement = ""; nSquaresToSkip = 0; // Pass through the ranks '8' to '1' and the files 'a' to 'h'. for (nRank = RankCount - 1; nRank >= 0; nRank--) { for (nFile = 0; nFile < FileCount; nFile++) { // Get the square and the piece that's on it pSquare = m_pSquare[nFile][nRank]; pChessMan = pSquare->GetOccupant(); if (pChessMan == NULL) { // Update number of empty squares nSquaresToSkip++; } else { if (nSquaresToSkip > 0) { // Format empty square placeholder sprintf(cFormat, "%d", nSquaresToSkip); PiecePlacement += cFormat; nSquaresToSkip = 0; } // Indicate pawn/piece type. switch (pChessMan->GetType()) { default: case Pawn: cManSymbol = 'p'; break; case Knight: cManSymbol = 'n'; break; case Bishop: cManSymbol = 'b'; break; case Rook: cManSymbol = 'r'; break; case Queen: cManSymbol = 'q'; break; case King: cManSymbol = 'k'; break; } // White pieces uppercase, black lowercase. if (pChessMan->IsColorWhite()) { cManSymbol = toupper(cManSymbol); } // Append the piece to the string PiecePlacement += cManSymbol; } } // Format placeholder string if (nSquaresToSkip > 0) { sprintf(cFormat, "%d", nSquaresToSkip); PiecePlacement += cFormat; nSquaresToSkip = 0; } // Separate ranks with slashes "/". if (nRank > 0) { PiecePlacement += RankSeparator; } } return PiecePlacement; } char* ChessBoard::GetBinaryPosition() { static char Buffer[EPD_BINARY_LENGTH]; char ExpandedBuffer[EPD_BINARY_LENGTH * 8]; char* pBoard = ExpandedBuffer; char* pPiece = pBoard + 64; bool bWhiteToMove; bool bWhiteMayCastleKingside; bool bWhiteMayCastleQueenside; bool bBlackMayCastleKingside; bool bBlackMayCastleQueenside; std::string EnPassantTarget; int nFile; char cFile; char cEnPassantFile; int nRank; char cRank; char cEnPassantRank; ChessSquare* pSquare; ChessMan* pChessMan; bool bWhiteMan; memset(Buffer, 0, EPD_BINARY_LENGTH); memset(ExpandedBuffer, 0, sizeof(ExpandedBuffer)); bWhiteToMove = IsWhiteToMove(); bWhiteMayCastleKingside = MayCastleKingside(WHITE); bWhiteMayCastleQueenside = MayCastleQueenside(WHITE); bBlackMayCastleKingside = MayCastleKingside(BLACK); bBlackMayCastleQueenside = MayCastleQueenside(BLACK); EnPassantTarget = GetEnPassantTarget(); if (EnPassantTarget != "") { cEnPassantFile = EnPassantTarget[0]; cEnPassantRank = EnPassantTarget[1]; } else { cEnPassantFile = '\0'; cEnPassantRank = '\0'; } for (nRank = RankCount - 1; nRank >= 0; nRank--) { for (nFile = 0; nFile < FileCount; nFile++) { // Get the square and the piece that's on it pSquare = m_pSquare[nFile][nRank]; cFile = pSquare->GetFile(); cRank = pSquare->GetRank(); pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { *pBoard = '\x01'; bWhiteMan = pChessMan->IsColorWhite(); // Indicate pawn/piece type. switch (pChessMan->GetType()) { default: case Pawn: if (bWhiteMan) { if ((cFile == cEnPassantFile) && (cRank == cEnPassantRank + 1)) { memcpy(pPiece, BinaryEpPawn, sizeof(BinaryEpPawn)); pPiece += sizeof(BinaryEpPawn); } else { memcpy(pPiece, BinaryWhitePawn, sizeof(BinaryWhitePawn)); pPiece += sizeof(BinaryWhitePawn); } } else { if ((cFile == cEnPassantFile) && (cRank == cEnPassantRank - 1)) { memcpy(pPiece, BinaryEpPawn, sizeof(BinaryEpPawn)); pPiece += sizeof(BinaryEpPawn); } else { memcpy(pPiece, BinaryBlackPawn, sizeof(BinaryBlackPawn)); pPiece += sizeof(BinaryBlackPawn); } } break; case Knight: if (bWhiteMan) { memcpy(pPiece, BinaryWhiteKnight, sizeof(BinaryWhiteKnight)); pPiece += sizeof(BinaryWhiteKnight); } else { memcpy(pPiece, BinaryBlackKnight, sizeof(BinaryBlackKnight)); pPiece += sizeof(BinaryBlackKnight); } break; case Bishop: if (bWhiteMan) { memcpy(pPiece, BinaryWhiteBishop, sizeof(BinaryWhiteBishop)); pPiece += sizeof(BinaryWhiteBishop); } else { memcpy(pPiece, BinaryBlackBishop, sizeof(BinaryBlackBishop)); pPiece += sizeof(BinaryBlackBishop); } break; case Rook: if (bWhiteMan) { if ((cFile == 'a') && (cRank == '1') && (bWhiteMayCastleQueenside)) { memcpy(pPiece, BinaryWhiteRookMayCastle, sizeof(BinaryWhiteRookMayCastle)); pPiece += sizeof(BinaryWhiteRookMayCastle); } else if ((cFile == 'h') && (cRank == '1') && (bWhiteMayCastleKingside)) { memcpy(pPiece, BinaryWhiteRookMayCastle, sizeof(BinaryWhiteRookMayCastle)); pPiece += sizeof(BinaryWhiteRookMayCastle); } else { memcpy(pPiece, BinaryWhiteRook, sizeof(BinaryWhiteRook)); pPiece += sizeof(BinaryWhiteRook); } } else { if ((cFile == 'a') && (cRank == '8') && (bBlackMayCastleQueenside)) { memcpy(pPiece, BinaryBlackRookMayCastle, sizeof(BinaryBlackRookMayCastle)); pPiece += sizeof(BinaryBlackRookMayCastle); } else if ((cFile == 'h') && (cRank == '8') && (bBlackMayCastleKingside)) { memcpy(pPiece, BinaryBlackRookMayCastle, sizeof(BinaryBlackRookMayCastle)); pPiece += sizeof(BinaryBlackRookMayCastle); } else { memcpy(pPiece, BinaryBlackRook, sizeof(BinaryBlackRook)); pPiece += sizeof(BinaryBlackRook); } } break; case Queen: if (bWhiteMan) { memcpy(pPiece, BinaryWhiteQueen, sizeof(BinaryWhiteQueen)); pPiece += sizeof(BinaryWhiteQueen); } else { memcpy(pPiece, BinaryBlackQueen, sizeof(BinaryBlackQueen)); pPiece += sizeof(BinaryBlackQueen); } break; case King: if (bWhiteMan) { if (bWhiteToMove) { memcpy(pPiece, BinaryWhiteKingWhiteToMove, sizeof(BinaryWhiteKingWhiteToMove)); pPiece += sizeof(BinaryWhiteKingWhiteToMove); } else { memcpy(pPiece, BinaryWhiteKingBlackToMove, sizeof(BinaryWhiteKingBlackToMove)); pPiece += sizeof(BinaryWhiteKingBlackToMove); } } else { memcpy(pPiece, BinaryBlackKing, sizeof(BinaryBlackKing)); pPiece += sizeof(BinaryBlackKing); } break; } } pBoard++; } } // Now compress image from bytes to bits char* pSource; char* pTarget; int nCount; char cTarget; pSource = ExpandedBuffer; pTarget = Buffer; nCount = 0; while (nCount < sizeof(Buffer)) { int nBits; nBits = 0; cTarget = '\x00'; while (nBits < 8) { cTarget <<= 1; if (*pSource != '\x00') { cTarget ^= '\x01'; } pSource++; nBits++; } *pTarget = cTarget; pTarget++; nCount++; } return Buffer; } // IsWhiteToMove provides a public interface to a protected // variable. bool ChessBoard::IsWhiteToMove() { return m_bWhiteToMove; } // IsBlackToMove provides a public interface to a protected // variable. bool ChessBoard::IsBlackToMove() { return !m_bWhiteToMove; } // GetColorToMove returns a value that may be used as an index // into arrays where the 0th entry is for white and the 1st // entry is for black. int ChessBoard::GetColorToMove() { if (m_bWhiteToMove) { return WHITE; } else { return BLACK; } } // GetCastleAvailability returns a string that is formatted // as it should be for an EPD. Note: if there is no castling // available, a null string "" is returned, not "-". std::string ChessBoard::GetCastleAvailability() { std::string CastleAvailability; CastleAvailability = ""; if (MayCastleKingside(WHITE)) { CastleAvailability += "K"; } if (MayCastleQueenside(WHITE)) { CastleAvailability += "Q"; } if (MayCastleKingside(BLACK)) { CastleAvailability += "k"; } if (MayCastleQueenside(BLACK)) { CastleAvailability += "q"; } return CastleAvailability; } // GetEnPassantTarget returns a string that is formatted // as it should be for an EPD. Note: if there is no en // passant target, a null string "" is returned, not "-". std::string ChessBoard::GetEnPassantTarget() { return m_EnPassantTarget; } // IsEnPassantCapturePossible determines whether an en passant // capture is possible at this time. bool ChessBoard::IsEnPassantCapturePossible() { bool bCapturePossible = false; // Probably not char cFile; char cRank; char cAttackRank; ChessSquare* pSquare; ChessMan* pChessMan; if (m_EnPassantTarget != "") { cFile = m_EnPassantTarget[0]; cRank = m_EnPassantTarget[1]; if (cRank == '3') { cAttackRank = '4'; } else { cAttackRank = '5'; } if (cFile > FileBase) { pSquare = GetSquare(cFile - 1, cAttackRank); pChessMan = pSquare->GetOccupant(); if ((pChessMan != NULL) && (pChessMan->GetType() == Pawn) && (pChessMan->IsColorWhite() == m_bWhiteToMove)) { bCapturePossible = true; } } if ((!bCapturePossible) && (cFile < (FileBase + (FileCount - 1)))) { pSquare = GetSquare(cFile + 1, cAttackRank); pChessMan = pSquare->GetOccupant(); if ((pChessMan != NULL) && (pChessMan->GetType() == Pawn) && (pChessMan->IsColorWhite() == m_bWhiteToMove)) { bCapturePossible = true; } } } return bCapturePossible; } // GetFullMoveNumber returns the move number for the game in // progress on the board. int ChessBoard::GetFullMoveNumber() { return m_nFullMoveNumber; } // GetHalfMoveClock returns the number of half-moves since the // last pawn move or capture. int ChessBoard::GetHalfMoveClock() { return m_nHalfMoveClock; } // MayCastleKingside is used to set castling availability on/off // for the specified color. void ChessBoard::MayCastleKingside(bool MayCastle, int Color) { m_bMayCastleKingside[Color] = MayCastle; } // MayCastleQueenside is used to set castling availability on/off // for the specified color. void ChessBoard::MayCastleQueenside(bool MayCastle, int Color) { m_bMayCastleQueenside[Color] = MayCastle; } // MayCastleKingside is used to determine whether castling is // available for the specified color. bool ChessBoard::MayCastleKingside(int Color) { return m_bMayCastleKingside[Color]; } // MayCastleQueenside is used to determine whether castling is // available for the specified color. bool ChessBoard::MayCastleQueenside(int Color) { return m_bMayCastleQueenside[Color]; } // AddPiece adds a piece to the board. The square and the piece // are set to point to each other and the piece is put into a list // of pieces on the board. void ChessBoard::AddPiece(char File, char Rank, ChessMan* pChessMan) { ChessSquare* pSquare; pSquare = GetSquare(File, Rank); pSquare->AddPiece(pChessMan); m_PieceList.push_back(pChessMan); } // RemovePiece takes a piece off the board. The square's pointer // is cleared and the piece is removed from the list of pieces on // the board. void ChessBoard::RemovePiece(char File, char Rank) { PieceList::iterator pPiece; bool bFound; ChessSquare* pSquare; ChessMan* pChessMan; pSquare = GetSquare(File, Rank); pChessMan = pSquare->GetOccupant(); pPiece = m_PieceList.begin(); bFound = false; while ((pPiece != m_PieceList.end()) && (!bFound)) { if (*pPiece == pChessMan) { m_PieceList.erase(pPiece); bFound = true; } if (!bFound) { pPiece++; } } pSquare->ClearSquare(); } // ResetAttack sets flags for the board and all of its squares // to indicate attack analysis has not been done. void ChessBoard::ResetAttack() { int nFile; int nRank; for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { m_pSquare[nFile][nRank]->ResetAttack(); } } m_bAttackAnalyzed = false; } // AnalyzeAttack analyzes which squares are attacked by how // many pieces and the cumulative value of the pieces // attacking each square. Each square separately tracks // attacks by white and black. // Each pawn attacks it two forward diagonals. Each piece // attacks any square it may reach up to and including the // first occupied square in its path (regardless of the color // of the occupant). void ChessBoard::AnalyzeAttack() { int nFile; int nRank; ChessSquare* pSquare; ChessSquare* pAttacked; ChessMan* pChessMan; bool bWhite; float fAttackValue; bool bOccupied; // Reset all attack values. if (m_bAttackAnalyzed) { ResetAttack(); } // Process all squares by rank within file. for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { // Get the square and its occupant. pSquare = m_pSquare[nFile][nRank]; pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { // Save the color and attack value bWhite = pChessMan->IsColorWhite(); fAttackValue = pChessMan->GetValue(); switch (pChessMan->GetType()) { case Pawn: // Pawns attack only the first forward diagonals. if (bWhite) { pAttacked = pSquare->GetDiagUpLeft(); if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } pAttacked = pSquare->GetDiagUpRight(); if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } } else { pAttacked = pSquare->GetDiagDownLeft(); if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } pAttacked = pSquare->GetDiagDownRight(); if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } } break; case Knight: // Knights attack 2:1. if (nFile > 0) { if (nRank > 1) { pAttacked = m_pSquare[nFile - 1][nRank - 2]; pAttacked->UpdateAttack(bWhite, fAttackValue); } if (nRank < RankCount - 2) { pAttacked = m_pSquare[nFile - 1][nRank + 2]; pAttacked->UpdateAttack(bWhite, fAttackValue); } } if (nFile > 1) { if (nRank > 0) { pAttacked = m_pSquare[nFile - 2][nRank - 1]; pAttacked->UpdateAttack(bWhite, fAttackValue); } if (nRank < RankCount - 1) { pAttacked = m_pSquare[nFile - 2][nRank + 1]; pAttacked->UpdateAttack(bWhite, fAttackValue); } } if (nFile < FileCount - 1) { if (nRank > 1) { pAttacked = m_pSquare[nFile + 1][nRank - 2]; pAttacked->UpdateAttack(bWhite, fAttackValue); } if (nRank < RankCount - 2) { pAttacked = m_pSquare[nFile + 1][nRank + 2]; pAttacked->UpdateAttack(bWhite, fAttackValue); } } if (nFile < FileCount - 2) { if (nRank > 0) { pAttacked = m_pSquare[nFile + 2][nRank - 1]; pAttacked->UpdateAttack(bWhite, fAttackValue); } if (nRank < RankCount - 1) { pAttacked = m_pSquare[nFile + 2][nRank + 1]; pAttacked->UpdateAttack(bWhite, fAttackValue); } } break; case Bishop: // Bishops attack along all four diagonals pAttacked = pSquare->GetDiagUpRight(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetDiagUpRight(); } } pAttacked = pSquare->GetDiagUpLeft(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetDiagUpLeft(); } } pAttacked = pSquare->GetDiagDownRight(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetDiagDownRight(); } } pAttacked = pSquare->GetDiagDownLeft(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetDiagDownLeft(); } } break; case Rook: // Rooks attack up and down files and right // and left on ranks. pAttacked = pSquare->GetFileUp(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetFileUp(); } } pAttacked = pSquare->GetFileDown(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetFileDown(); } } pAttacked = pSquare->GetRankRight(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetRankRight(); } } pAttacked = pSquare->GetRankLeft(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetRankLeft(); } } break; case Queen: // Queens attack like combined bishops and // rooks; on all four diagonals, up and down // files, left and right on ranks. pAttacked = pSquare->GetDiagUpRight(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetDiagUpRight(); } } pAttacked = pSquare->GetDiagUpLeft(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetDiagUpLeft(); } } pAttacked = pSquare->GetDiagDownRight(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetDiagDownRight(); } } pAttacked = pSquare->GetDiagDownLeft(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetDiagDownLeft(); } } pAttacked = pSquare->GetFileUp(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetFileUp(); } } pAttacked = pSquare->GetFileDown(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetFileDown(); } } pAttacked = pSquare->GetRankRight(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetRankRight(); } } pAttacked = pSquare->GetRankLeft(); bOccupied = false; while ((pAttacked != NULL) && (!bOccupied)) { pAttacked->UpdateAttack(bWhite, fAttackValue); if (pAttacked->GetOccupant() != NULL) { bOccupied = true; } else { pAttacked = pAttacked->GetRankLeft(); } } break; case King: // Kings only attack adjacent squares. pAttacked = m_pSquare[nFile][nRank]->m_pFileUp; if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } pAttacked = m_pSquare[nFile][nRank]->m_pFileDown; if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } pAttacked = m_pSquare[nFile][nRank]->m_pRankRight; if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } pAttacked = m_pSquare[nFile][nRank]->m_pRankLeft; if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } pAttacked = m_pSquare[nFile][nRank]->m_pDiagUpRight; if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } pAttacked = m_pSquare[nFile][nRank]->m_pDiagUpLeft; if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } pAttacked = m_pSquare[nFile][nRank]->m_pDiagDownRight; if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } pAttacked = m_pSquare[nFile][nRank]->m_pDiagDownLeft; if (pAttacked != NULL) { pAttacked->UpdateAttack(bWhite, fAttackValue); } break; } } } } // Flag all squares as having been analyzed. for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { m_pSquare[nFile][nRank]->m_bAttackAnalyzed = true; } } // Indicate analysis complete! m_bAttackAnalyzed = true; } // IsKingChecked finds the king of the specified color and // determines whether its square is attacked by the opposite // color bool ChessBoard::IsKingChecked(int Color) { bool bIsChecked; int nFile; int nRank; ChessSquare* pSquare; ChessMan* pChessMan; bool bWhite; // If attack analysis hasn't been done, do it now. if (!m_bAttackAnalyzed) { AnalyzeAttack(); } // Convert color index to boolean. if (Color == WHITE) { bWhite = true; } else { bWhite = false; } bIsChecked = false; // Find the king of the appropriate color. for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { pSquare = m_pSquare[nFile][nRank]; pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { if (pChessMan->GetType() == King) { if (pChessMan->IsColorWhite() == bWhite) { // Is its square attacked by the opposition? if (pSquare->GetAttackCount(!bWhite) > 0) { bIsChecked = true; // Indicate check status } nFile = FileCount; // Force exit from loop nRank = RankCount; } } } } } return bIsChecked; } // Link squares properly initializes the eight pointers on each // square that point to adjacent squares along files, ranks and // diagonals. The edges/corners of the board are designated by // NULL pointers. New squares have all pointers initialized to // NULL so we don't have to set those. void ChessBoard::LinkSquares() { int nFile; int nRank; ChessSquare* pSquare; // Process all squares by rank within file for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { // Get a square. pSquare = m_pSquare[nFile][nRank]; // Set file pointers. if (nRank > 0) { pSquare->m_pFileDown = m_pSquare[nFile][nRank - 1]; } if (nRank < (RankCount - 1)) { pSquare->m_pFileUp = m_pSquare[nFile][nRank + 1]; } // Set rank pointers. if (nFile > 0) { pSquare->m_pRankLeft = m_pSquare[nFile - 1][nRank]; if (nRank > 0) { pSquare->m_pDiagDownLeft = m_pSquare[nFile - 1][nRank - 1]; } if (nRank < (RankCount - 1)) { pSquare->m_pDiagUpLeft = m_pSquare[nFile - 1][nRank + 1]; } } // Set diagonal pointers if (nFile < (FileCount - 1)) { pSquare->m_pRankRight = m_pSquare[nFile + 1][nRank]; if (nRank > 0) { pSquare->m_pDiagDownRight = m_pSquare[nFile + 1][nRank - 1]; } if (nRank < (RankCount - 1)) { pSquare->m_pDiagUpRight = m_pSquare[nFile + 1][nRank + 1]; } } } } } // ValidateCheckStatus determines if the side that has just // moved has left itself in check. bool ChessBoard::ValidateCheckStatus() { bool bValidCheckStatus = true; // assume correct if (IsWhiteToMove()) { if (IsKingChecked(BLACK)) { bValidCheckStatus = false; } } else { if (IsKingChecked(WHITE)) { bValidCheckStatus = false; } } return bValidCheckStatus; } // ValidateEnPassantStatus checks to see if the en passant target // square is on the correct rank and is properly adjacent to a // pawn of the correct color. bool ChessBoard::ValidateEnPassantStatus() { bool bValidEnPassantStatus = true; std::string EnPassantTarget; char cFile; char cRank; bool bWhiteToMove; ChessSquare* pSquare; ChessMan* pChessMan; EnPassantTarget = GetEnPassantTarget(); if (EnPassantTarget != "") { cFile = EnPassantTarget[0]; cRank = EnPassantTarget[1]; bWhiteToMove = IsWhiteToMove(); if (bWhiteToMove) { pSquare = GetSquare(cFile, cRank - 1); } else { pSquare = GetSquare(cFile, cRank + 1); } pChessMan = pSquare->GetOccupant(); if ((pChessMan == NULL) || (pChessMan->GetType() != Pawn)) { bValidEnPassantStatus = false; } else { if (bWhiteToMove == pChessMan->IsColorWhite()) { bValidEnPassantStatus = false; } } } return bValidEnPassantStatus; } // ValidatePawnPlacement checks to make sure that there are // no pawns on the 1st or 8th rank. bool ChessBoard::ValidatePawnPlacement() { bool bValidPawnPlacement = true; char cFile; char cRank; ChessSquare* pSquare; ChessMan* pChessMan; cRank = RankBase; cFile = FileBase; while (cFile < (FileBase + FileCount)) { pSquare = GetSquare(cFile, cRank); pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { if (pChessMan->GetType() == Pawn) { bValidPawnPlacement = false; break; } } cFile++; } if (bValidPawnPlacement) { cRank = RankBase + (RankCount - 1); cFile = FileBase; while (cFile < (FileBase + FileCount)) { pSquare = GetSquare(cFile, cRank); pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { if (pChessMan->GetType() == Pawn) { bValidPawnPlacement = false; break; } } cFile++; } } return bValidPawnPlacement; } // ValidateCastleAvailability checks the castle availability // flags against the position of the appropriate kings and rooks. // If a castle is deemed available, the king and appropriate // rook of that color must be on their original squares. bool ChessBoard::ValidateCastleAvailability() { bool bValidCastleAvailability = true; bool bWhiteKingOriginal = false; bool bWhiteKingsRookOriginal = false; bool bWhiteQueensRookOriginal = false; bool bBlackKingOriginal = false; bool bBlackKingsRookOriginal = false; bool bBlackQueensRookOriginal = false; char cFile; char cRank; ChessSquare* pSquare; ChessMan* pChessMan; ChessManType Type; bool bWhite; // Check white queenside rook. cFile = 'a'; cRank = '1'; pSquare = GetSquare(cFile, cRank); pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { Type = pChessMan->GetType(); if (Type == Rook) { bWhite = pChessMan->IsColorWhite(); if (bWhite) { bWhiteQueensRookOriginal = true; } } } // Check white king. cFile = 'e'; cRank = '1'; pSquare = GetSquare(cFile, cRank); pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { Type = pChessMan->GetType(); if (Type == King) { bWhite = pChessMan->IsColorWhite(); if (bWhite) { bWhiteKingOriginal = true; } } } // Check white kingside rook. cFile = 'h'; cRank = '1'; pSquare = GetSquare(cFile, cRank); pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { Type = pChessMan->GetType(); if (Type == Rook) { bWhite = pChessMan->IsColorWhite(); if (bWhite) { bWhiteKingsRookOriginal = true; } } } // Check black queenside rook. cFile = 'a'; cRank = '8'; pSquare = GetSquare(cFile, cRank); pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { Type = pChessMan->GetType(); if (Type == Rook) { bWhite = pChessMan->IsColorWhite(); if (!bWhite) { bBlackQueensRookOriginal = true; } } } // Check black king. cFile = 'e'; cRank = '8'; pSquare = GetSquare(cFile, cRank); pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { Type = pChessMan->GetType(); if (Type == King) { bWhite = pChessMan->IsColorWhite(); if (!bWhite) { bBlackKingOriginal = true; } } } // Check black kingside rook cFile = 'h'; cRank = '8'; pSquare = GetSquare(cFile, cRank); pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { Type = pChessMan->GetType(); if (Type == Rook) { bWhite = pChessMan->IsColorWhite(); if (!bWhite) { bBlackKingsRookOriginal = true; } } } // Now check piece placement against advertised availability. if ((bValidCastleAvailability) && (MayCastleQueenside(WHITE))) { if ((!bWhiteQueensRookOriginal) || (!bWhiteKingOriginal)) { bValidCastleAvailability = false; } } if ((bValidCastleAvailability) && (MayCastleKingside(WHITE))) { if ((!bWhiteKingsRookOriginal) || (!bWhiteKingOriginal)) { bValidCastleAvailability = false; } } if ((bValidCastleAvailability) && (MayCastleQueenside(BLACK))) { if ((!bBlackQueensRookOriginal) || (!bBlackKingOriginal)) { bValidCastleAvailability = false; } } if ((bValidCastleAvailability) && (MayCastleKingside(BLACK))) { if ((!bBlackKingsRookOriginal) || (!bBlackKingOriginal)) { bValidCastleAvailability = false; } } return bValidCastleAvailability; } // GetLegalMoveList gets a list of all moves that may be // played from this position by the side whose turn it is to // move next. // First a list off all moves is produced. Then each move is // "played" to see if it illegaly leaves us in check. If it // is still legal, we also want to know if it achieve check or // checkmate. // Note: the bIterate parameter is used to control recursion. // If a move that we are considering achieves check, we want to // see if it is actually checkmate. To do this, we call this // routine to see if the opponent has any legal moves to escape // check. When we do this, we set the flag to "false" to stop // the recursion (if, for example, the opponent has a move that // produces check). MOVE_LIST ChessBoard::GetLegalMoveList(bool bIterate) { MOVE_LIST CandidateList; // all candidates MOVE_LIST LegalList; // legal moves only MOVE_LIST DisambigList; // used for "identical" move // during disambiguation MOVE_LIST FinalList; // final list for user. int nFile; int nRank; MOVE_LIST::iterator pMove; ChessMove Move; std::string PiecePlacement; bool bWhiteToMove; std::string CastleAvailability; std::string EnPassantTarget; ChessBoard* pNewBoard; // board to test moves // first ply ChessBoard* pCheckmateBoard; // board to test checkmate // second ply // Make new boards pNewBoard = new ChessBoard(); pCheckmateBoard = new ChessBoard(); // Save status of this board PiecePlacement = GetPiecePlacement(); bWhiteToMove = IsWhiteToMove(); CastleAvailability = GetCastleAvailability(); EnPassantTarget = GetEnPassantTarget(); // Go get all candidates GetCandidateMoveList(CandidateList); // Who's on offense, who's on defense int nOffenseColor; int nDefenseColor; if (m_bWhiteToMove) { nOffenseColor = WHITE; nDefenseColor = BLACK; } else { nOffenseColor = BLACK; nDefenseColor = WHITE; } // For each candidate move, set up a board as a copy // of this board, play the move, and see how it worked out. LegalList.clear(); pMove = CandidateList.begin(); while (pMove != CandidateList.end()) { Move = *pMove; // Set up board. pNewBoard->SetPiecePlacement(PiecePlacement); pNewBoard->SetWhiteToMove(bWhiteToMove); pNewBoard->SetCastleAvailability(CastleAvailability); pNewBoard->SetEnPassantTarget(EnPassantTarget); // Make the move. Disambiguation is temporarily maximized. switch (Move.m_ManToMove) { case Pawn: pNewBoard->MovePawn(Move); break; case Knight: Move.m_cDisambigFile = Move.m_cSourceFile; Move.m_cDisambigRank = Move.m_cSourceRank; pNewBoard->MoveKnight(Move); Move.m_cDisambigFile = ' '; Move.m_cDisambigRank = ' '; break; case Bishop: Move.m_cDisambigFile = Move.m_cSourceFile; Move.m_cDisambigRank = Move.m_cSourceRank; pNewBoard->MoveBishop(Move); Move.m_cDisambigFile = ' '; Move.m_cDisambigRank = ' '; break; case Rook: Move.m_cDisambigFile = Move.m_cSourceFile; Move.m_cDisambigRank = Move.m_cSourceRank; pNewBoard->MoveRook(Move); Move.m_cDisambigFile = ' '; Move.m_cDisambigRank = ' '; break; case Queen: Move.m_cDisambigFile = Move.m_cSourceFile; Move.m_cDisambigRank = Move.m_cSourceRank; pNewBoard->MoveQueen(Move); Move.m_cDisambigFile = ' '; Move.m_cDisambigRank = ' '; break; case King: pNewBoard->MoveKing(Move); break; } if (Move.m_bLegalMove) { // If we've check our opponent, see if its checkmate. if ((pNewBoard->IsKingChecked(nDefenseColor)) && (bIterate)) { Move.m_bCheck = true; MOVE_LIST CheckmateList; // Set up another board for the next ply. pCheckmateBoard->SetPiecePlacement(pNewBoard->GetPiecePlacement()); pCheckmateBoard->SetWhiteToMove(!pNewBoard->IsWhiteToMove()); pCheckmateBoard->SetCastleAvailability(pNewBoard->GetCastleAvailability()); pCheckmateBoard->SetEnPassantTarget(Move.m_EnPassantTarget); // Get a list of moves for the next ply. CheckmateList = pCheckmateBoard->GetLegalMoveList(false); // If there aren't any legal moves, it's checkmate. if (CheckmateList.size() == 0) { Move.m_bCheckmate = true; } } // If the move didn't leave us in check, it's legal. if (!pNewBoard->IsKingChecked(nOffenseColor)) { Move.m_PiecePlacementAfter = pNewBoard->GetPiecePlacement(); Move.m_CastleAvailabilityAfter = pNewBoard->GetCastleAvailability(); LegalList.push_back(Move); } } pMove++; } CandidateList.clear(); FinalList.clear(); DisambigList.clear(); SAN San; SAN SaveSan; int nFileCount[FileCount]; int nRankCount[RankCount]; MOVE_LIST::iterator pDisambig; MOVE_LIST::iterator pErase; // Sort the legal moves in SAN collating sequence for // disambiguation. LegalList.sort(); pMove = LegalList.begin(); San = (*pMove).CreateSAN(); SaveSan = San; while (pMove != LegalList.end()) { // Get all moves by a single piece type to a single // square. These require disambiguation. while ((pMove != LegalList.end()) && (San == SaveSan)) { DisambigList.push_back(*pMove); pMove++; if (pMove != LegalList.end()) { San = (*pMove).CreateSAN(); } } SaveSan = San; // Initialize file and rank counters. if (DisambigList.size() > 1) { for (nFile = 0; nFile < FileCount; nFile++) { nFileCount[nFile] = 0; } for (nRank = 0; nRank < RankCount; nRank++) { nRankCount[nRank] = 0; } // Count the number of pieces on each file and each // rank. pDisambig = DisambigList.begin(); while (pDisambig != DisambigList.end()) { nFile = (*pDisambig).m_cSourceFile - FileBase; nFileCount[nFile]++; nRank = (*pDisambig).m_cSourceRank - RankBase; nRankCount[nRank]++; pDisambig++; } for (nFile = 0; nFile < FileCount; nFile++) { if (nFileCount[nFile] == 1) { // Any piece that is the only piece on that // file requires only file disambiguation. pDisambig = DisambigList.begin(); while (pDisambig != DisambigList.end()) { if (nFile == (*pDisambig).m_cSourceFile - FileBase) { // Get the move, set the disambiguation, // move it from one list to the other. pErase = pDisambig; pDisambig++; (*pErase).m_cDisambigFile = (*pErase).m_cSourceFile; FinalList.push_back(*pErase); DisambigList.erase(pErase); } else { pDisambig++; } } } } if (DisambigList.size() > 0) { for (nRank = 0; nRank < RankCount; nRank++) { if (nRankCount[nRank] == 1) { // Any piece that is the only piece on that // rank requires only rank disambiguation. pDisambig = DisambigList.begin(); while (pDisambig != DisambigList.end()) { if (nRank == (*pDisambig).m_cSourceRank - RankBase) { // Get the move, set the disambiguation, // move it from one list to the other. pErase = pDisambig; pDisambig++; (*pErase).m_cDisambigRank = (*pErase).m_cSourceRank; FinalList.push_back(*pErase); DisambigList.erase(pErase); } else { pDisambig++; } } } } if (DisambigList.size() > 0) { // All remaining moves require both file and // rank disambiguation. pDisambig = DisambigList.begin(); while (pDisambig != DisambigList.end()) { // Get the move, set the disambiguation, // move it from one list to the other. pErase = pDisambig; pDisambig++; (*pErase).m_cDisambigFile = (*pErase).m_cSourceFile; (*pErase).m_cDisambigRank = (*pErase).m_cSourceRank; FinalList.push_back(*pErase); DisambigList.erase(pErase); pDisambig++; } } } DisambigList.clear(); } else { // No disambiguation needed. pDisambig = DisambigList.begin(); while (pDisambig != DisambigList.end()) { FinalList.push_back(*pDisambig); pDisambig++; } DisambigList.clear(); } } delete pNewBoard; delete pCheckmateBoard; return FinalList; } // GetCandidateMoveList gets a list of all moves that may be // played from this position by the side whose turn it is to // move next. Even moves that will leave us in check if they // are played are put into the list. void ChessBoard::GetCandidateMoveList(MOVE_LIST& CandidateList) { ChessMove Move; int nFile; int nRank; ChessSquare* pSquare; ChessSquare* pAttacked; ChessMan* pChessMan; ChessMan* pVictim; std::string EnPassantTarget; ChessSquare* pEnPassantSquare; bool bEnPassantWhite; CandidateList.clear(); Move.ResetDefaults(); // Process all squares by rank within file. for (nFile = 0; nFile < FileCount; nFile++) { for (nRank = 0; nRank < RankCount; nRank++) { // Get the square and its occupant. pSquare = m_pSquare[nFile][nRank]; pChessMan = pSquare->GetOccupant(); if (pChessMan != NULL) { if (m_bWhiteToMove == pChessMan->IsColorWhite()) { // If its a man that can move, get all of its // moves. switch (pChessMan->GetType()) { case Pawn: // Predetermine en passant possibilities. EnPassantTarget = GetEnPassantTarget(); if (EnPassantTarget != "") { pEnPassantSquare = GetSquare(EnPassantTarget[0], EnPassantTarget[1]); if (EnPassantTarget[1] == '3') { bEnPassantWhite = true; } else { bEnPassantWhite = false; } } else { pEnPassantSquare = NULL; } // Get possible pawn capture along diagonal. if (m_bWhiteToMove) { pAttacked = pSquare->GetDiagUpLeft(); if (pAttacked != NULL) { pVictim = pAttacked->GetOccupant(); } else { pVictim = NULL; } if (pVictim != NULL) { if (m_bWhiteToMove != pVictim->IsColorWhite()) { Move.m_ManToMove = Pawn; Move.m_cSourceFile = pSquare->GetFile(); Move.m_cSourceRank = pSquare->GetRank(); Move.m_cTargetFile = pAttacked->GetFile(); Move.m_cTargetRank = pAttacked->GetRank(); Move.m_bCapture = true; Move.m_cDisambigFile = Move.m_cSourceFile; // Handle pawn promotions. if (Move.m_cTargetRank == '8') { Move.m_bPromote = true; Move.m_PromoteTo = Queen; CandidateList.push_back(Move); Move.m_PromoteTo = Knight; CandidateList.push_back(Move); Move.m_PromoteTo = Rook; CandidateList.push_back(Move); Move.m_PromoteTo = Bishop; CandidateList.push_back(Move); Move.ResetDefaults(); } else { CandidateList.push_back(Move); Move.ResetDefaults(); } } } else { // Handle en passant captures. if ((pAttacked != NULL) && (pAttacked == pEnPassantSquare) && (m_bWhiteToMove != bEnPassantWhite)) { Move.m_ManToMove = Pawn; Move.m_cSourceFile = pSquare->GetFile(); Move.m_cSourceRank = pSquare->GetRank(); Move.m_cTargetFile = pAttacked->GetFile(); Move.m_cTargetRank = pAttacked->GetRank(); Move.m_bCapture = true; Move.m_cDisambigFile = Move.m_cSourceFile; CandidateList.push_back(Move); Move.ResetDefaults(); } } // Get possible pawn capture along diagonal. pAttacked = pSquare->GetDiagUpRight(); if (pAttacked != NULL) { pVictim = pAttacked->GetOccupant(); } else { pVictim = NULL; } if (pVictim != NULL) { if (m_bWhiteToMove != pVictim->IsColorWhite()) { Move.m_ManToMove = Pawn; Move.m_cSourceFile = pSquare->GetFile(); Move.m_cSourceRank = pSquare->GetRank(); Move.m_cTargetFile = pAttacked->GetFile(); Move.m_cTargetRank = pAttacked->GetRank(); Move.m_bCapture = true; Move.m_cDisambigFile = Move.m_cSourceFile; // Handle pawn promotion. if (Move.m_cTargetRank == '8') { Move.m_bPromote = true; Move.m_PromoteTo = Queen; CandidateList.push_back(Move); Move.m_PromoteTo = Knight; CandidateList.push_back(Move); Move.m_PromoteTo = Rook; CandidateList.push_back(Move); Move.m_PromoteTo = Bishop; CandidateList.push_back(Move); Move.ResetDefaults(); } else { CandidateList.push_back(Move); Move.ResetDefaults(); } } } else { // Handle en passant capture. if ((pAttacked != NULL) && (pAttacked == pEnPassantSquare) && (m_bWhiteToMove != bEnPassantWhite)) { Move.m_ManToMove = Pawn; Move.m_cSourceFile = pSquare->GetFile(); Move.m_cSourceRank = pSquare->GetRank(); Move.m_cTargetFile = pAttacked->GetFile(); Move.m_cTargetRank = pAttacked->GetRank(); Move.m_bCapture = true; Move.m_cDisambigFile = Move.m_cSourceFile; CandidateList.push_back(Move); Move.ResetDefaults(); } } pAttacked = pSquare->GetFileUp(); pVictim = pAttacked->GetOccupant(); // Handle normal move, one square forward. if (pVictim == NULL) { Move.m_ManToMove = Pawn; Move.m_cSourceFile = pSquare->GetFile(); Move.m_cSourceRank = pSquare->GetRank(); Move.m_cTargetFile = pAttacked->GetFile(); Move.m_cTargetRank = pAttacked->GetRank(); // Handle pawn promotion. if (Move.m_cTargetRank == '8') { Move.m_bPromote = true; Move.m_PromoteTo = Queen; CandidateList.push_back(Move); Move.m_PromoteTo = Knight; CandidateList.push_back(Move); Move.m_PromoteTo = Rook; CandidateList.push_back(Move); Move.m_PromoteTo = Bishop; CandidateList.push_back(Move); Move.ResetDefaults(); } else { CandidateList.push_back(Move); Move.ResetDefaults(); } // If this is the first move for the pawn, we may // also make a two-square move forward. if (pAttacked->GetRank() == '3') { pAttacked = pAttacked->GetFileUp(); pVictim = pAttacked->GetOccupant(); if (pVictim == NULL) { Move.m_ManToMove = Pawn; Move.m_cSourceFile = pSquare->GetFile(); Move.m_cSourceRank = pSquare->GetRank(); Move.m_cTargetFile = p