Chess Game Code in C++

Implement a simple chess game Code in C++ with a basic AI Implementation using the Minimax algorithm. The game allows human players to make moves and responds with AI-generated moves. The code includes a Board class to represent the game board, a Move struct to represent individual moves, and a ChessAI class to implement the AI opponent. The game loop alternates between human and AI turns, with the AI using the Minimax algorithm to find the best move. This implementation provides a basic framework for building a chess game with AI in C++.

Overview of the Code

Our code of chess game consists of the following main components:

  1. Board Class: Manages the game board and piece positions.
  2. Move Structure: Represents a chess move.
  3. ChessAI Class: Implements a basic AI to determine the best moves.
  4. Main Function: Facilitates gameplay between a human player and the AI.

NOTE

You can also download the GitHub Repository.

Or you can download the Zip File.

The Game Board

The Board class represents the game board and provides methods for manipulating and querying the board. Here’s a breakdown of the class:

  • The board member variable is a 2D array of characters, where each character represents a piece on the board.
  • The constructor initializes the board with the starting position of a chess game.
  • The print method prints the board to the console, with row and column labels.
  • The isValidMove method checks whether a given move is valid, by checking that the start and end positions are within the board boundaries and that the start position contains a piece.
  • The applyMove method applies a move to the board, by updating the start and end positions.
  • The undoMove method undoes a move, by restoring the original state of the board.
  • The pieceAt method returns the piece at a given position on the board.
  • The generateMoves method generates all possible moves for the current player, by iterating over all pieces on the board and checking all possible moves for each piece.
class Board {
public:
    char board[8][8];

    Board() {
        const std::string initialBoard =
            "rnbqkbnr"
            "pppppppp"
            "........"
            "........"
            "........"
            "........"
            "PPPPPPPP"
            "RNBQKBNR";

        for (int i = 0; i < 64; ++i)
            board[i / 8][i % 8] = initialBoard[i];
    }
    // Additional methods...
};

Move Representation

A move is represented by a struct called Move, which contains the start and end coordinates of the move. The Move struct represents a single move, with four member variables:

  • startX and startY represent the starting position of the move.
  • endX and endY represent the ending position of the move.
struct Move {
    int startX, startY, endX, endY;
};

AI Implementation

The AI opponent is implemented using the ChessAI class, The ChessAI class represents the AI opponent and provides methods for finding the best move. Here’s a breakdown of the class:

  • The findBestMove method finds the best move for the AI opponent, by generating all possible moves, applying each move, evaluating the resulting board, and then undoing the move. The move with the highest evaluation is chosen as the best move.
  • The evaluate method evaluates the board, by returning a score that represents the strength of the AI’s position. In this implementation, the evaluation is simplified to always return 0.
  • The minimax algorithm implements the Minimax algorithm, which is a recursive algorithm for finding the best move. The algorithm works by generating all possible moves, applying each move, evaluating the resulting board, and then undoing the move. The algorithm recursively calls itself, with the opponent’s perspective, to evaluate the best response to each move.

Game Loop

The game loop is implemented in the main function, which alternates between the human player and the AI Implementation. The human player is prompted to enter a move, and the AI opponent uses the findBestMove method to determine its move.

  • Move Generation: The generateMoves() method in the Board class generates potential moves. This is a simplified version, capturing basic move logic.
  • Minimax Algorithm: The minimax() function evaluates moves by recursively exploring game states to a given depth and applying alpha-beta pruning to optimize performance.
  • Move Evaluation: The evaluate() function is a placeholder that assigns a static score to the board state.
int main() {
    Board board;
    ChessAI ai;
    Player currentPlayer = HUMAN;

    while (true) {
        board.print();

        if (currentPlayer == HUMAN) {
            // ...
        } else {
            Move bestMove = ai.findBestMove(board, 3);
            board.applyMove(bestMove);
            std::cout << "AI move: " << static_cast<char>(bestMove.startX + 'a') << 8 - bestMove.startY
                      << static_cast<char>(bestMove.endX + 'a') << 8 - bestMove.endY << "\n";
            currentPlayer = HUMAN;
        }
    }

    return 0;
}

Additional Notes

  • The parseMove function is used to parse a move string entered by the human player, and convert it into a Move struct.
  • The Player enum is used to represent the current player, with two possible values: HUMAN and AI.
  • The BOARD_SIZE constant is used to represent the size of the board, which is 8×8 in this implementation.
  • The PIECES constant is used to represent the possible pieces on the board, as a string of characters.

Output

chess game

Conclusion

This implementation provides a basic framework for a chess game with AI in C++. While the AI opponent is relatively simple, it demonstrates the basic concepts of game tree search and evaluation. The code can be extended and improved by adding more advanced AI techniques, such as alpha-beta pruning and more sophisticated evaluation functions.

Source Code of Chess Game

#include <iostream>
#include <vector>
#include <limits>
#include <string>
#include <algorithm>

const int BOARD_SIZE = 8;
const std::string PIECES = "KQRBNPkqrbnp";

enum Player { HUMAN, AI };

struct Move {
    int startX, startY, endX, endY;
};

class Board {
public:
    char board[8][8];

    Board() {
        const std::string initialBoard =
            "rnbqkbnr"
            "pppppppp"
            "........"
            "........"
            "........"
            "........"
            "PPPPPPPP"
            "RNBQKBNR";

        for (int i = 0; i < 64; ++i)
            board[i / 8][i % 8] = initialBoard[i];
    }

    void print() const {
        std::cout << "  a b c d e f g h\n";
        for (int i = 0; i < BOARD_SIZE; ++i) {
            std::cout << 8 - i << " ";
            for (int j = 0; j < BOARD_SIZE; ++j) {
                std::cout << board[i][j] << ' ';
            }
            std::cout << 8 - i << "\n";
        }
        std::cout << "  a b c d e f g h\n";
    }

    bool isValidMove(const Move& move) const {
        // Simplified move validation
        return move.startX >= 0 && move.startX < 8 && move.startY >= 0 && move.startY < 8 &&
            move.endX >= 0 && move.endX < 8 && move.endY >= 0 && move.endY < 8 &&
            board[move.startY][move.startX] != '.'; // Ensure the start position has a piece
    }

    void applyMove(const Move& move) {
        board[move.endY][move.endX] = board[move.startY][move.startX];
        board[move.startY][move.startX] = '.';
    }

    void undoMove(const Move& move, char capturedPiece) {
        board[move.startY][move.startX] = board[move.endY][move.endX];
        board[move.endY][move.endX] = capturedPiece;
    }

    char pieceAt(int x, int y) const {
        return board[y][x];
    }

    std::vector<Move> generateMoves() const {
        std::vector<Move> moves;
        for (int y = 0; y < 8; ++y) {
            for (int x = 0; x < 8; ++x) {
                if (board[y][x] != '.') {
                    for (int dy = -1; dy <= 1; ++dy) {
                        for (int dx = -1; dx <= 1; ++dx) {
                            int ny = y + dy;
                            int nx = x + dx;
                            if (ny >= 0 && ny < 8 && nx >= 0 && nx < 8) {
                                moves.push_back({ x, y, nx, ny });
                            }
                        }
                    }
                }
            }
        }
        return moves;
    }
};

class ChessAI {
public:
    Move findBestMove(Board& board, int depth) {
        Move bestMove;
        int bestValue = std::numeric_limits<int>::min();
        for (const auto& move : board.generateMoves()) {
            char capturedPiece = board.pieceAt(move.endX, move.endY);
            board.applyMove(move);
            int moveValue = minimax(board, depth - 1, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), false);
            board.undoMove(move, capturedPiece);
            if (moveValue > bestValue) {
                bestMove = move;
                bestValue = moveValue;
            }
        }
        return bestMove;
    }

private:
    int evaluate(const Board& board) const {
        // Simplified evaluation
        return 0;
    }

    int minimax(Board& board, int depth, int alpha, int beta, bool maximizingPlayer) {
        if (depth == 0) {
            return evaluate(board);
        }

        if (maximizingPlayer) {
            int maxEval = std::numeric_limits<int>::min();
            for (const auto& move : board.generateMoves()) {
                char capturedPiece = board.pieceAt(move.endX, move.endY);
                board.applyMove(move);
                int eval = minimax(board, depth - 1, alpha, beta, false);
                board.undoMove(move, capturedPiece);
                maxEval = std::max(maxEval, eval);
                alpha = std::max(alpha, eval);
                if (beta <= alpha) {
                    break;
                }
            }
            return maxEval;
        }
        else {
            int minEval = std::numeric_limits<int>::max();
            for (const auto& move : board.generateMoves()) {
                char capturedPiece = board.pieceAt(move.endX, move.endY);
                board.applyMove(move);
                int eval = minimax(board, depth - 1, alpha, beta, true);
                board.undoMove(move, capturedPiece);
                minEval = std::min(minEval, eval);
                beta = std::min(beta, eval);
                if (beta <= alpha) {
                    break;
                }
            }
            return minEval;
        }
    }
};

Move parseMove(const std::string& moveStr) {
    return {
        moveStr[0] - 'a',
        8 - (moveStr[1] - '0'),
        moveStr[2] - 'a',
        8 - (moveStr[3] - '0')
    };
}

int main() {
    Board board;
    ChessAI ai;
    Player currentPlayer = HUMAN;

    while (true) {
        board.print();

        if (currentPlayer == HUMAN) {
            std::string moveStr;
            std::cout << "Enter your move (e.g., e2e4): ";
            std::cin >> moveStr;
            Move move = parseMove(moveStr);
            if (board.isValidMove(move)) {
                board.applyMove(move);
                currentPlayer = AI;
            }
            else {
                std::cout << "Invalid move!\n";
            }
        }
        else {
            Move bestMove = ai.findBestMove(board, 3);
            board.applyMove(bestMove);
            std::cout << "AI move: " << static_cast<char>(bestMove.startX + 'a') << 8 - bestMove.startY
                << static_cast<char>(bestMove.endX + 'a') << 8 - bestMove.endY << "\n";
            currentPlayer = HUMAN;
        }
    }

    return 0;
}

Leave a comment