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:
- Board Class: Manages the game board and piece positions.
- Move Structure: Represents a chess move.
- ChessAI Class: Implements a basic AI to determine the best moves.
- 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
andstartY
represent the starting position of the move.endX
andendY
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 theBoard
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 aMove
struct. - The
Player
enum is used to represent the current player, with two possible values:HUMAN
andAI
. - 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

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;
}