ChessBoard LLD

PHOTO EMBED

Sun Sep 15 2024 19:24:58 GMT+0000 (Coordinated Universal Time)

Saved by @codestored #cpp

#include <bits/stdc++.h>

using namespace std;

class Piece
{
private:
    virtual bool AreSquaresLegal(int srcRow, int srcCol, int destRow, int destCol, Piece *GameBoard[8][8]) = 0;
    char mPieceColor;

public:
    Piece(char PieceColor) : mPieceColor(PieceColor) {}
    ~Piece() {}
    virtual char GetPiece() = 0;
    char GetColor()
    {
        return mPieceColor;
    }
    bool IsLegalMove(int srcRow, int srcCol, int destRow, int destCol, Piece *GameBoard[8][8])
    {
        Piece *destPiece = GameBoard[destRow][destCol];
        if ((destPiece == 0) || (mPieceColor != destPiece->GetColor()))
        {
            return AreSquaresLegal(srcRow, srcCol, destRow, destCol, GameBoard);
        }
        return false;
    }
};



class PawnPiece : public Piece
{
public:
    PawnPiece(char PieceColor) : Piece(PieceColor) {}
    ~PawnPiece() {}

private:
    virtual char GetPiece()
    {
        return 'P';
    }
    bool AreSquaresLegal(int srcRow, int srcCol, int destRow, int destCol, Piece *GameBoard[8][8])
    {
        Piece *destPiece = GameBoard[destRow][destCol];
        if (destPiece == 0)
        {
            // Destination square is unoccupied
            if (srcCol == destCol)
            {
                if (GetColor() == 'W')
                {
                    if (destRow == srcRow + 1)
                    {
                        return true;
                    }
                }
                else
                {
                    if (destRow == srcRow - 1)
                    {
                        return true;
                    }
                }
            }
        }
        else
        {
            // Dest holds piece of opposite color
            if ((srcCol == destCol + 1) || (srcCol == destCol - 1))
            {
                if (GetColor() == 'W')
                {
                    if (destRow == srcRow + 1)
                    {
                        return true;
                    }
                }
                else
                {
                    if (destRow == srcRow - 1)
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }
};



class KnightPiece : public Piece
{
public:
    KnightPiece(char PieceColor) : Piece(PieceColor) {}
    ~KnightPiece() {}

private:
    virtual char GetPiece()
    {
        return 'N';
    }
    bool AreSquaresLegal(int srcRow, int srcCol, int destRow, int destCol, Piece *GameBoard[8][8])
    {
        // Destination square is unoccupied or occupied by opposite color
        if ((srcCol == destCol + 1) || (srcCol == destCol - 1))
        {
            if ((srcRow == destRow + 2) || (srcRow == destRow - 2))
            {
                return true;
            }
        }
        if ((srcCol == destCol + 2) || (srcCol == destCol - 2))
        {
            if ((srcRow == destRow + 1) || (srcRow == destRow - 1))
            {
                return true;
            }
        }
        return false;
    }
};



class BishopPiece : public Piece
{
public:
    BishopPiece(char PieceColor) : Piece(PieceColor) {}
    ~BishopPiece() {}

private:
    virtual char GetPiece()
    {
        return 'B';
    }
    bool AreSquaresLegal(int srcRow, int srcCol, int destRow, int destCol, Piece *GameBoard[8][8])
    {
        if ((destCol - srcCol == destRow - srcRow) || (destCol - srcCol == srcRow - destRow))
        {
            // Make sure that all invervening squares are empty
            int iRowOffset = (destRow - srcRow > 0) ? 1 : -1;
            int iColOffset = (destCol - srcCol > 0) ? 1 : -1;
            int iCheckRow;
            int iCheckCol;
            for (iCheckRow = srcRow + iRowOffset, iCheckCol = srcCol + iColOffset;
                 iCheckRow != destRow;
                 iCheckRow = iCheckRow + iRowOffset, iCheckCol = iCheckCol + iColOffset)
            {
                if (GameBoard[iCheckRow][iCheckCol] != 0)
                {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
};



class RookPiece : public Piece
{
public:
    RookPiece(char PieceColor) : Piece(PieceColor) {}
    ~RookPiece() {}

private:
    virtual char GetPiece()
    {
        return 'R';
    }
    bool AreSquaresLegal(int srcRow, int srcCol, int destRow, int destCol, Piece *GameBoard[8][8])
    {
        if (srcRow == destRow)
        {
            // Make sure that all invervening squares are empty
            int iColOffset = (destCol - srcCol > 0) ? 1 : -1;
            for (int iCheckCol = srcCol + iColOffset; iCheckCol != destCol; iCheckCol = iCheckCol + iColOffset)
            {
                if (GameBoard[srcRow][iCheckCol] != 0)
                {
                    return false;
                }
            }
            return true;
        }
        else if (destCol == srcCol)
        {
            // Make sure that all invervening squares are empty
            int iRowOffset = (destRow - srcRow > 0) ? 1 : -1;
            for (int iCheckRow = srcRow + iRowOffset; iCheckRow != destRow; iCheckRow = iCheckRow + iRowOffset)
            {
                if (GameBoard[iCheckRow][srcCol] != 0)
                {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
};



class QueenPiece : public Piece
{
public:
    QueenPiece(char PieceColor) : Piece(PieceColor) {}
    ~QueenPiece() {}

private:
    virtual char GetPiece()
    {
        return 'Q';
    }
    bool AreSquaresLegal(int srcRow, int srcCol, int destRow, int destCol, Piece *GameBoard[8][8])
    {
        if (srcRow == destRow)
        {
            // Make sure that all invervening squares are empty
            int iColOffset = (destCol - srcCol > 0) ? 1 : -1;
            for (int iCheckCol = srcCol + iColOffset; iCheckCol != destCol; iCheckCol = iCheckCol + iColOffset)
            {
                if (GameBoard[srcRow][iCheckCol] != 0)
                {
                    return false;
                }
            }
            return true;
        }
        else if (destCol == srcCol)
        {
            // Make sure that all invervening squares are empty
            int iRowOffset = (destRow - srcRow > 0) ? 1 : -1;
            for (int iCheckRow = srcRow + iRowOffset; iCheckRow != destRow; iCheckRow = iCheckRow + iRowOffset)
            {
                if (GameBoard[iCheckRow][srcCol] != 0)
                {
                    return false;
                }
            }
            return true;
        }
        else if ((destCol - srcCol == destRow - srcRow) || (destCol - srcCol == srcRow - destRow))
        {
            // Make sure that all invervening squares are empty
            int iRowOffset = (destRow - srcRow > 0) ? 1 : -1;
            int iColOffset = (destCol - srcCol > 0) ? 1 : -1;
            int iCheckRow;
            int iCheckCol;
            for (iCheckRow = srcRow + iRowOffset, iCheckCol = srcCol + iColOffset;
                 iCheckRow != destRow;
                 iCheckRow = iCheckRow + iRowOffset, iCheckCol = iCheckCol + iColOffset)
            {
                if (GameBoard[iCheckRow][iCheckCol] != 0)
                {
                    return false;
                }
            }
            return true;
        }
        return false;
    }
};



class KingPiece : public Piece
{
public:
    KingPiece(char PieceColor) : Piece(PieceColor) {}
    ~KingPiece() {}

private:
    virtual char GetPiece()
    {
        return 'K';
    }
    bool AreSquaresLegal(int srcRow, int srcCol, int destRow, int destCol, Piece *GameBoard[8][8])
    {
        int iRowDelta = destRow - srcRow;
        int iColDelta = destCol - srcCol;
        if (((iRowDelta >= -1) && (iRowDelta <= 1)) &&
            ((iColDelta >= -1) && (iColDelta <= 1)))
        {
            return true;
        }
        return false;
    }
};



class ChessBoard
{
public:
    Piece *MainGameBoard[8][8];
    ChessBoard()
    {
        for (int iRow = 0; iRow < 8; ++iRow)
        {
            for (int iCol = 0; iCol < 8; ++iCol)
            {
                MainGameBoard[iRow][iCol] = 0;
            }
        }
        // Allocate and place black pieces
        for (int iCol = 0; iCol < 8; ++iCol)
        {
            MainGameBoard[6][iCol] = new PawnPiece('B');
        }
        MainGameBoard[7][0] = new RookPiece('B');
        MainGameBoard[7][1] = new KnightPiece('B');
        MainGameBoard[7][2] = new BishopPiece('B');
        MainGameBoard[7][3] = new KingPiece('B');
        MainGameBoard[7][4] = new QueenPiece('B');
        MainGameBoard[7][5] = new BishopPiece('B');
        MainGameBoard[7][6] = new KnightPiece('B');
        MainGameBoard[7][7] = new RookPiece('B');
        // Allocate and place white pieces
        for (int iCol = 0; iCol < 8; ++iCol)
        {
            MainGameBoard[1][iCol] = new PawnPiece('W');
        }
        MainGameBoard[0][0] = new RookPiece('W');
        MainGameBoard[0][1] = new KnightPiece('W');
        MainGameBoard[0][2] = new BishopPiece('W');
        MainGameBoard[0][3] = new KingPiece('W');
        MainGameBoard[0][4] = new QueenPiece('W');
        MainGameBoard[0][5] = new BishopPiece('W');
        MainGameBoard[0][6] = new KnightPiece('W');
        MainGameBoard[0][7] = new RookPiece('W');
    }
    ~ChessBoard()
    {
        for (int iRow = 0; iRow < 8; ++iRow)
        {
            for (int iCol = 0; iCol < 8; ++iCol)
            {
                delete MainGameBoard[iRow][iCol];
                MainGameBoard[iRow][iCol] = 0;
            }
        }
    }

    void Print()
    {
        const int kiSquareWidth = 4;
        const int kiSquareHeight = 3;
        for (int iRow = 0; iRow < 8 * kiSquareHeight; ++iRow)
        {
            int iSquareRow = iRow / kiSquareHeight;
            // Print side border with numbering
            if (iRow % 3 == 1)
            {
                cout << '-' << (char)('1' + 7 - iSquareRow) << '-';
            }
            else
            {
                cout << "---";
            }
            // Print the chess board
            for (int iCol = 0; iCol < 8 * kiSquareWidth; ++iCol)
            {
                int iSquareCol = iCol / kiSquareWidth;
                if (((iRow % 3) == 1) && ((iCol % 4) == 1 || (iCol % 4) == 2) && MainGameBoard[7 - iSquareRow][iSquareCol] != 0)
                {
                    if ((iCol % 4) == 1)
                    {
                        cout << MainGameBoard[7 - iSquareRow][iSquareCol]->GetColor();
                    }
                    else
                    {
                        cout << MainGameBoard[7 - iSquareRow][iSquareCol]->GetPiece();
                    }
                }
                else
                {
                    if ((iSquareRow + iSquareCol) % 2 == 1)
                    {
                        cout << '*';
                    }
                    else
                    {
                        cout << ' ';
                    }
                }
            }
            cout << endl;
        }
        // Print the bottom border with numbers
        for (int iRow = 0; iRow < kiSquareHeight; ++iRow)
        {
            if (iRow % 3 == 1)
            {
                cout << "---";
                for (int iCol = 0; iCol < 8 * kiSquareWidth; ++iCol)
                {
                    int iSquareCol = iCol / kiSquareWidth;
                    if ((iCol % 4) == 1)
                    {
                        cout << (iSquareCol + 1);
                    }
                    else
                    {
                        cout << '-';
                    }
                }
                cout << endl;
            }
            else
            {
                for (int iCol = 1; iCol < 9 * kiSquareWidth; ++iCol)
                {
                    cout << '-';
                }
                cout << endl;
            }
        }
    }

    bool IsInCheck(char PieceColor)
    {
        // Find the king
        int iKingRow;
        int iKingCol;
        for (int iRow = 0; iRow < 8; ++iRow)
        {
            for (int iCol = 0; iCol < 8; ++iCol)
            {
                if (MainGameBoard[iRow][iCol] != 0)
                {
                    if (MainGameBoard[iRow][iCol]->GetColor() == PieceColor)
                    {
                        if (MainGameBoard[iRow][iCol]->GetPiece() == 'K')
                        {
                            iKingRow = iRow;
                            iKingCol = iCol;
                        }
                    }
                }
            }
        }
        // Run through the opponent's pieces and see if any can take the king
        for (int iRow = 0; iRow < 8; ++iRow)
        {
            for (int iCol = 0; iCol < 8; ++iCol)
            {
                if (MainGameBoard[iRow][iCol] != 0)
                {
                    if (MainGameBoard[iRow][iCol]->GetColor() != PieceColor)
                    {
                        if (MainGameBoard[iRow][iCol]->IsLegalMove(iRow, iCol, iKingRow, iKingCol, MainGameBoard))
                        {
                            return true;
                        }
                    }
                }
            }
        }

        return false;
    }

    bool CanMove(char PieceColor)
    {
        // Run through all pieces
        for (int iRow = 0; iRow < 8; ++iRow)
        {
            for (int iCol = 0; iCol < 8; ++iCol)
            {
                if (MainGameBoard[iRow][iCol] != 0)
                {
                    // If it is a piece of the current player, see if it has a legal move
                    if (MainGameBoard[iRow][iCol]->GetColor() == PieceColor)
                    {
                        for (int iMoveRow = 0; iMoveRow < 8; ++iMoveRow)
                        {
                            for (int iMoveCol = 0; iMoveCol < 8; ++iMoveCol)
                            {
                                if (MainGameBoard[iRow][iCol]->IsLegalMove(iRow, iCol, iMoveRow, iMoveCol, MainGameBoard))
                                {
                                    // Make move and check whether king is in check
                                    Piece *tempPiece = MainGameBoard[iMoveRow][iMoveCol];
                                    MainGameBoard[iMoveRow][iMoveCol] = MainGameBoard[iRow][iCol];
                                    MainGameBoard[iRow][iCol] = 0;
                                    bool moreMoves = !IsInCheck(PieceColor);
                                    // Undo the move
                                    MainGameBoard[iRow][iCol] = MainGameBoard[iMoveRow][iMoveCol];
                                    MainGameBoard[iMoveRow][iMoveCol] = tempPiece;
                                    if (moreMoves)
                                    {
                                        return true;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return false;
    }
};



class Game
{
private:
    ChessBoard board;
    char currentPlayer;

public:
    Game() : currentPlayer('W') {}
    ~Game() {}

    void Start()
    {
        do
        {
            GetNextMove(board.MainGameBoard);
            changeTurn();
        } while (!IsGameOver());
        board.Print();
    }

    void GetNextMove(Piece *GameBoard[8][8])
    {
        bool isValidMove = false;
        do
        {
            system("clear");
            board.Print();

            // Get input and convert to coordinates
            cout << currentPlayer << "'s Move: ";
            int startMove;
            cin >> startMove;
            int startRow = (startMove / 10) - 1;
            int startCol = (startMove % 10) - 1;

            cout << "To: ";
            int endMove;
            cin >> endMove;
            int iEndRow = (endMove / 10) - 1;
            int iEndCol = (endMove % 10) - 1;

            // Check that the indices are in range
            // and that the source and destination are different
            if ((startRow >= 0 && startRow <= 7) &&
                (startCol >= 0 && startCol <= 7) &&
                (iEndRow >= 0 && iEndRow <= 7) &&
                (iEndCol >= 0 && iEndCol <= 7))
            {
                // Additional checks in here
                Piece *currentPiece = GameBoard[startRow][startCol];
                // Check that the piece is the correct color
                if ((currentPiece != nullptr) && (currentPiece->GetColor() == currentPlayer))
                {
                    // Check that the destination is a valid destination
                    if (currentPiece->IsLegalMove(startRow, startCol, iEndRow, iEndCol, GameBoard))
                    {
                        // Make the move
                        Piece *tempPiece = GameBoard[iEndRow][iEndCol];
                        GameBoard[iEndRow][iEndCol] = GameBoard[startRow][startCol];
                        GameBoard[startRow][startCol] = 0;
                        // Make sure that the current player is not in check
                        if (!board.IsInCheck(currentPlayer))
                        {
                            delete tempPiece;
                            isValidMove = true;
                        }
                        else
                        { // Undo the last move
                            GameBoard[startRow][startCol] = GameBoard[iEndRow][iEndCol];
                            GameBoard[iEndRow][iEndCol] = tempPiece;
                        }
                    }
                }
            }
            if (!isValidMove)
            {
                cout << "Invalid Move!" << endl;
            }
        } while (!isValidMove);
    }

    void changeTurn()
    {
        currentPlayer = (currentPlayer == 'W') ? 'B' : 'W';
    }

    bool IsGameOver()
    {
        // Check that the current player can move
        // If not, we have a stalemate or checkmate
        bool moreMoves(false);
        moreMoves = board.CanMove(currentPlayer);
        if (!moreMoves)
        {
            if (board.IsInCheck(currentPlayer))
            {
                changeTurn();
                std::cout << "Checkmate, " << currentPlayer << " Wins!" << std::endl;
            }
            else
            {
                std::cout << "Draw!" << std::endl;
            }
        }
        return !moreMoves;
    }
};

int main()
{
    Game game;
    game.Start();
    return 0;
}
content_copyCOPY