Skip to content

Commit a287d1c

Browse files
authored
Merge pull request #31 from saumya1317/feature/17-chess-game-ai
feat: implement Chess Game with AI using C++ (#17)
2 parents aef22ff + bf4910b commit a287d1c

File tree

15 files changed

+2092
-20
lines changed

15 files changed

+2092
-20
lines changed

Src/Chess_Game_AI/Board.cpp

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
#include "Board.h"
2+
#include <iostream>
3+
#include <algorithm>
4+
5+
Board::Board() {
6+
// Initialize empty board
7+
for (int i = 0; i < 8; i++) {
8+
for (int j = 0; j < 8; j++) {
9+
board[i][j] = nullptr;
10+
}
11+
}
12+
}
13+
14+
void Board::initialize() {
15+
// Clear board
16+
for (int i = 0; i < 8; i++) {
17+
for (int j = 0; j < 8; j++) {
18+
board[i][j] = nullptr;
19+
}
20+
}
21+
22+
// Place black pieces (row 0-1)
23+
board[0][0] = std::make_unique<Rook>(Color::BLACK, Position(0, 0));
24+
board[0][1] = std::make_unique<Knight>(Color::BLACK, Position(0, 1));
25+
board[0][2] = std::make_unique<Bishop>(Color::BLACK, Position(0, 2));
26+
board[0][3] = std::make_unique<Queen>(Color::BLACK, Position(0, 3));
27+
board[0][4] = std::make_unique<King>(Color::BLACK, Position(0, 4));
28+
board[0][5] = std::make_unique<Bishop>(Color::BLACK, Position(0, 5));
29+
board[0][6] = std::make_unique<Knight>(Color::BLACK, Position(0, 6));
30+
board[0][7] = std::make_unique<Rook>(Color::BLACK, Position(0, 7));
31+
32+
for (int col = 0; col < 8; col++) {
33+
board[1][col] = std::make_unique<Pawn>(Color::BLACK, Position(1, col));
34+
}
35+
36+
// Place white pieces (row 6-7)
37+
for (int col = 0; col < 8; col++) {
38+
board[6][col] = std::make_unique<Pawn>(Color::WHITE, Position(6, col));
39+
}
40+
41+
board[7][0] = std::make_unique<Rook>(Color::WHITE, Position(7, 0));
42+
board[7][1] = std::make_unique<Knight>(Color::WHITE, Position(7, 1));
43+
board[7][2] = std::make_unique<Bishop>(Color::WHITE, Position(7, 2));
44+
board[7][3] = std::make_unique<Queen>(Color::WHITE, Position(7, 3));
45+
board[7][4] = std::make_unique<King>(Color::WHITE, Position(7, 4));
46+
board[7][5] = std::make_unique<Bishop>(Color::WHITE, Position(7, 5));
47+
board[7][6] = std::make_unique<Knight>(Color::WHITE, Position(7, 6));
48+
board[7][7] = std::make_unique<Rook>(Color::WHITE, Position(7, 7));
49+
50+
// Set king positions
51+
whiteKingPos = Position(7, 4);
52+
blackKingPos = Position(0, 4);
53+
}
54+
55+
Piece* Board::getPiece(const Position& pos) const {
56+
if (!pos.isValid()) return nullptr;
57+
return board[pos.row][pos.col].get();
58+
}
59+
60+
bool Board::isEmpty(const Position& pos) const {
61+
if (!pos.isValid()) return false;
62+
return board[pos.row][pos.col] == nullptr;
63+
}
64+
65+
bool Board::hasEnemyPiece(const Position& pos, Color playerColor) const {
66+
if (!pos.isValid()) return false;
67+
Piece* piece = board[pos.row][pos.col].get();
68+
return piece != nullptr && piece->getColor() != playerColor;
69+
}
70+
71+
bool Board::makeMove(const Position& from, const Position& to) {
72+
if (!from.isValid() || !to.isValid()) return false;
73+
74+
Piece* piece = getPiece(from);
75+
if (!piece) return false;
76+
77+
// Check if move is legal
78+
if (!isMoveLegal(from, to, piece->getColor())) {
79+
return false;
80+
}
81+
82+
// Capture enemy piece if present
83+
if (getPiece(to)) {
84+
board[to.row][to.col] = nullptr;
85+
}
86+
87+
// Move the piece
88+
board[to.row][to.col] = std::move(board[from.row][from.col]);
89+
board[to.row][to.col]->setPosition(to);
90+
board[to.row][to.col]->setHasMoved(true);
91+
92+
// Update king position if king moved
93+
if (board[to.row][to.col]->getType() == PieceType::KING) {
94+
if (board[to.row][to.col]->getColor() == Color::WHITE) {
95+
whiteKingPos = to;
96+
} else {
97+
blackKingPos = to;
98+
}
99+
}
100+
101+
return true;
102+
}
103+
104+
bool Board::isKingInCheck(Color kingColor) const {
105+
Position kingPos = (kingColor == Color::WHITE) ? whiteKingPos : blackKingPos;
106+
Color enemyColor = (kingColor == Color::WHITE) ? Color::BLACK : Color::WHITE;
107+
108+
return isPositionUnderAttack(kingPos, enemyColor);
109+
}
110+
111+
bool Board::isPositionUnderAttack(const Position& pos, Color attackerColor) const {
112+
// Check all pieces of the attacking color
113+
for (int row = 0; row < 8; row++) {
114+
for (int col = 0; col < 8; col++) {
115+
Piece* piece = board[row][col].get();
116+
if (piece && piece->getColor() == attackerColor) {
117+
std::vector<Position> moves = piece->getValidMoves(*this);
118+
for (const Position& move : moves) {
119+
if (move == pos) {
120+
return true;
121+
}
122+
}
123+
}
124+
}
125+
}
126+
return false;
127+
}
128+
129+
bool Board::wouldKingBeSafe(const Position& from, const Position& to, Color playerColor) const {
130+
// Create a temporary board state
131+
Board tempBoard;
132+
133+
// Copy current board state
134+
for (int row = 0; row < 8; row++) {
135+
for (int col = 0; col < 8; col++) {
136+
if (board[row][col]) {
137+
tempBoard.board[row][col] = board[row][col]->clone();
138+
}
139+
}
140+
}
141+
tempBoard.whiteKingPos = whiteKingPos;
142+
tempBoard.blackKingPos = blackKingPos;
143+
144+
// Make the move on temporary board
145+
if (tempBoard.board[to.row][to.col]) {
146+
tempBoard.board[to.row][to.col] = nullptr;
147+
}
148+
tempBoard.board[to.row][to.col] = std::move(tempBoard.board[from.row][from.col]);
149+
tempBoard.board[to.row][to.col]->setPosition(to);
150+
151+
// Update king position if king moved
152+
if (tempBoard.board[to.row][to.col]->getType() == PieceType::KING) {
153+
if (playerColor == Color::WHITE) {
154+
tempBoard.whiteKingPos = to;
155+
} else {
156+
tempBoard.blackKingPos = to;
157+
}
158+
}
159+
160+
// Check if king is in check after the move
161+
return !tempBoard.isKingInCheck(playerColor);
162+
}
163+
164+
bool Board::isMoveLegal(const Position& from, const Position& to, Color playerColor) const {
165+
Piece* piece = getPiece(from);
166+
if (!piece || piece->getColor() != playerColor) return false;
167+
168+
// Check if the move is in the piece's valid moves
169+
std::vector<Position> validMoves = piece->getValidMoves(*this);
170+
bool isValidMove = false;
171+
for (const Position& move : validMoves) {
172+
if (move == to) {
173+
isValidMove = true;
174+
break;
175+
}
176+
}
177+
178+
if (!isValidMove) return false;
179+
180+
// Check if the move would leave the king in check
181+
return wouldKingBeSafe(from, to, playerColor);
182+
}
183+
184+
std::vector<std::pair<Position, Position>> Board::getAllValidMoves(Color playerColor) const {
185+
std::vector<std::pair<Position, Position>> allMoves;
186+
187+
for (int row = 0; row < 8; row++) {
188+
for (int col = 0; col < 8; col++) {
189+
Piece* piece = board[row][col].get();
190+
if (piece && piece->getColor() == playerColor) {
191+
Position from(row, col);
192+
std::vector<Position> validMoves = piece->getValidMoves(*this);
193+
194+
for (const Position& to : validMoves) {
195+
if (isMoveLegal(from, to, playerColor)) {
196+
allMoves.push_back({from, to});
197+
}
198+
}
199+
}
200+
}
201+
}
202+
203+
return allMoves;
204+
}
205+
206+
bool Board::isCheckmate(Color playerColor) const {
207+
// If not in check, it's not checkmate
208+
if (!isKingInCheck(playerColor)) return false;
209+
210+
// If there are any valid moves, it's not checkmate
211+
return getAllValidMoves(playerColor).empty();
212+
}
213+
214+
bool Board::isStalemate(Color playerColor) const {
215+
// If in check, it's not stalemate
216+
if (isKingInCheck(playerColor)) return false;
217+
218+
// If there are no valid moves, it's stalemate
219+
return getAllValidMoves(playerColor).empty();
220+
}
221+
222+
Position Board::getKingPosition(Color kingColor) const {
223+
return (kingColor == Color::WHITE) ? whiteKingPos : blackKingPos;
224+
}
225+
226+
int Board::evaluatePosition() const {
227+
int score = 0;
228+
229+
for (int row = 0; row < 8; row++) {
230+
for (int col = 0; col < 8; col++) {
231+
Piece* piece = board[row][col].get();
232+
if (piece) {
233+
int pieceValue = piece->getValue();
234+
if (piece->getColor() == Color::WHITE) {
235+
score += pieceValue;
236+
} else {
237+
score -= pieceValue;
238+
}
239+
}
240+
}
241+
}
242+
243+
return score;
244+
}
245+
246+
void Board::display() const {
247+
std::cout << "\n a b c d e f g h\n";
248+
std::cout << " +---+---+---+---+---+---+---+---+\n";
249+
250+
for (int row = 0; row < 8; row++) {
251+
std::cout << 8 - row << " |";
252+
for (int col = 0; col < 8; col++) {
253+
Piece* piece = board[row][col].get();
254+
if (piece) {
255+
std::cout << " " << piece->getSymbol() << " |";
256+
} else {
257+
std::cout << " |";
258+
}
259+
}
260+
std::cout << " " << 8 - row << "\n";
261+
std::cout << " +---+---+---+---+---+---+---+---+\n";
262+
}
263+
264+
std::cout << " a b c d e f g h\n\n";
265+
}
266+
267+
void Board::placePiece(std::unique_ptr<Piece> piece, const Position& pos) {
268+
if (pos.isValid()) {
269+
board[pos.row][pos.col] = std::move(piece);
270+
}
271+
}
272+
273+
std::unique_ptr<Board> Board::clone() const {
274+
auto clonedBoard = std::make_unique<Board>();
275+
276+
for (int row = 0; row < 8; row++) {
277+
for (int col = 0; col < 8; col++) {
278+
if (board[row][col]) {
279+
clonedBoard->board[row][col] = board[row][col]->clone();
280+
}
281+
}
282+
}
283+
284+
clonedBoard->whiteKingPos = whiteKingPos;
285+
clonedBoard->blackKingPos = blackKingPos;
286+
287+
return clonedBoard;
288+
}

Src/Chess_Game_AI/Board.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#ifndef BOARD_H
2+
#define BOARD_H
3+
4+
#include "Piece.h"
5+
#include <array>
6+
#include <memory>
7+
#include <vector>
8+
9+
struct Move {
10+
Position from;
11+
Position to;
12+
std::unique_ptr<Piece> capturedPiece;
13+
bool isCastling;
14+
bool isEnPassant;
15+
16+
Move(Position f, Position t)
17+
: from(f), to(t), capturedPiece(nullptr), isCastling(false), isEnPassant(false) {}
18+
};
19+
20+
class Board {
21+
private:
22+
std::array<std::array<std::unique_ptr<Piece>, 8>, 8> board;
23+
std::vector<Move> moveHistory;
24+
Position whiteKingPos;
25+
Position blackKingPos;
26+
27+
public:
28+
Board();
29+
30+
// Initialize the board with standard chess setup
31+
void initialize();
32+
33+
// Get piece at position
34+
Piece* getPiece(const Position& pos) const;
35+
36+
// Check if position is empty
37+
bool isEmpty(const Position& pos) const;
38+
39+
// Check if position has enemy piece
40+
bool hasEnemyPiece(const Position& pos, Color playerColor) const;
41+
42+
// Make a move
43+
bool makeMove(const Position& from, const Position& to);
44+
45+
// Undo last move
46+
void undoMove();
47+
48+
// Check if king is in check
49+
bool isKingInCheck(Color kingColor) const;
50+
51+
// Check if it's checkmate
52+
bool isCheckmate(Color playerColor) const;
53+
54+
// Check if it's stalemate
55+
bool isStalemate(Color playerColor) const;
56+
57+
// Get all valid moves for a player
58+
std::vector<std::pair<Position, Position>> getAllValidMoves(Color playerColor) const;
59+
60+
// Validate if a move is legal (doesn't leave king in check)
61+
bool isMoveLegal(const Position& from, const Position& to, Color playerColor) const;
62+
63+
// Display the board
64+
void display() const;
65+
66+
// Get king position
67+
Position getKingPosition(Color kingColor) const;
68+
69+
// Evaluate board position for AI
70+
int evaluatePosition() const;
71+
72+
// Clone board for simulation
73+
std::unique_ptr<Board> clone() const;
74+
75+
private:
76+
// Helper function to check if position is under attack
77+
bool isPositionUnderAttack(const Position& pos, Color attackerColor) const;
78+
79+
// Helper to simulate a move and check if king is safe
80+
bool wouldKingBeSafe(const Position& from, const Position& to, Color playerColor) const;
81+
82+
// Place a piece on the board
83+
void placePiece(std::unique_ptr<Piece> piece, const Position& pos);
84+
};
85+
86+
#endif // BOARD_H

Src/Chess_Game_AI/CMakeLists.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
project(ChessGameAI)
3+
4+
# Set C++ standard
5+
set(CMAKE_CXX_STANDARD 14)
6+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
7+
8+
# Add executable
9+
add_executable(chess_game
10+
main.cpp
11+
Piece.cpp
12+
Board.cpp
13+
Player.cpp
14+
Game.cpp
15+
)
16+
17+
# Enable warnings
18+
if(MSVC)
19+
target_compile_options(chess_game PRIVATE /W4)
20+
else()
21+
target_compile_options(chess_game PRIVATE -Wall -Wextra -pedantic)
22+
endif()

0 commit comments

Comments
 (0)