diff --git a/Src/Chess_Game_AI/Board.cpp b/Src/Chess_Game_AI/Board.cpp new file mode 100644 index 0000000..3d3af7e --- /dev/null +++ b/Src/Chess_Game_AI/Board.cpp @@ -0,0 +1,288 @@ +#include "Board.h" +#include +#include + +Board::Board() { + // Initialize empty board + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) { + board[i][j] = nullptr; + } + } +} + +void Board::initialize() { + // Clear board + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) { + board[i][j] = nullptr; + } + } + + // Place black pieces (row 0-1) + board[0][0] = std::make_unique(Color::BLACK, Position(0, 0)); + board[0][1] = std::make_unique(Color::BLACK, Position(0, 1)); + board[0][2] = std::make_unique(Color::BLACK, Position(0, 2)); + board[0][3] = std::make_unique(Color::BLACK, Position(0, 3)); + board[0][4] = std::make_unique(Color::BLACK, Position(0, 4)); + board[0][5] = std::make_unique(Color::BLACK, Position(0, 5)); + board[0][6] = std::make_unique(Color::BLACK, Position(0, 6)); + board[0][7] = std::make_unique(Color::BLACK, Position(0, 7)); + + for (int col = 0; col < 8; col++) { + board[1][col] = std::make_unique(Color::BLACK, Position(1, col)); + } + + // Place white pieces (row 6-7) + for (int col = 0; col < 8; col++) { + board[6][col] = std::make_unique(Color::WHITE, Position(6, col)); + } + + board[7][0] = std::make_unique(Color::WHITE, Position(7, 0)); + board[7][1] = std::make_unique(Color::WHITE, Position(7, 1)); + board[7][2] = std::make_unique(Color::WHITE, Position(7, 2)); + board[7][3] = std::make_unique(Color::WHITE, Position(7, 3)); + board[7][4] = std::make_unique(Color::WHITE, Position(7, 4)); + board[7][5] = std::make_unique(Color::WHITE, Position(7, 5)); + board[7][6] = std::make_unique(Color::WHITE, Position(7, 6)); + board[7][7] = std::make_unique(Color::WHITE, Position(7, 7)); + + // Set king positions + whiteKingPos = Position(7, 4); + blackKingPos = Position(0, 4); +} + +Piece* Board::getPiece(const Position& pos) const { + if (!pos.isValid()) return nullptr; + return board[pos.row][pos.col].get(); +} + +bool Board::isEmpty(const Position& pos) const { + if (!pos.isValid()) return false; + return board[pos.row][pos.col] == nullptr; +} + +bool Board::hasEnemyPiece(const Position& pos, Color playerColor) const { + if (!pos.isValid()) return false; + Piece* piece = board[pos.row][pos.col].get(); + return piece != nullptr && piece->getColor() != playerColor; +} + +bool Board::makeMove(const Position& from, const Position& to) { + if (!from.isValid() || !to.isValid()) return false; + + Piece* piece = getPiece(from); + if (!piece) return false; + + // Check if move is legal + if (!isMoveLegal(from, to, piece->getColor())) { + return false; + } + + // Capture enemy piece if present + if (getPiece(to)) { + board[to.row][to.col] = nullptr; + } + + // Move the piece + board[to.row][to.col] = std::move(board[from.row][from.col]); + board[to.row][to.col]->setPosition(to); + board[to.row][to.col]->setHasMoved(true); + + // Update king position if king moved + if (board[to.row][to.col]->getType() == PieceType::KING) { + if (board[to.row][to.col]->getColor() == Color::WHITE) { + whiteKingPos = to; + } else { + blackKingPos = to; + } + } + + return true; +} + +bool Board::isKingInCheck(Color kingColor) const { + Position kingPos = (kingColor == Color::WHITE) ? whiteKingPos : blackKingPos; + Color enemyColor = (kingColor == Color::WHITE) ? Color::BLACK : Color::WHITE; + + return isPositionUnderAttack(kingPos, enemyColor); +} + +bool Board::isPositionUnderAttack(const Position& pos, Color attackerColor) const { + // Check all pieces of the attacking color + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + Piece* piece = board[row][col].get(); + if (piece && piece->getColor() == attackerColor) { + std::vector moves = piece->getValidMoves(*this); + for (const Position& move : moves) { + if (move == pos) { + return true; + } + } + } + } + } + return false; +} + +bool Board::wouldKingBeSafe(const Position& from, const Position& to, Color playerColor) const { + // Create a temporary board state + Board tempBoard; + + // Copy current board state + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + if (board[row][col]) { + tempBoard.board[row][col] = board[row][col]->clone(); + } + } + } + tempBoard.whiteKingPos = whiteKingPos; + tempBoard.blackKingPos = blackKingPos; + + // Make the move on temporary board + if (tempBoard.board[to.row][to.col]) { + tempBoard.board[to.row][to.col] = nullptr; + } + tempBoard.board[to.row][to.col] = std::move(tempBoard.board[from.row][from.col]); + tempBoard.board[to.row][to.col]->setPosition(to); + + // Update king position if king moved + if (tempBoard.board[to.row][to.col]->getType() == PieceType::KING) { + if (playerColor == Color::WHITE) { + tempBoard.whiteKingPos = to; + } else { + tempBoard.blackKingPos = to; + } + } + + // Check if king is in check after the move + return !tempBoard.isKingInCheck(playerColor); +} + +bool Board::isMoveLegal(const Position& from, const Position& to, Color playerColor) const { + Piece* piece = getPiece(from); + if (!piece || piece->getColor() != playerColor) return false; + + // Check if the move is in the piece's valid moves + std::vector validMoves = piece->getValidMoves(*this); + bool isValidMove = false; + for (const Position& move : validMoves) { + if (move == to) { + isValidMove = true; + break; + } + } + + if (!isValidMove) return false; + + // Check if the move would leave the king in check + return wouldKingBeSafe(from, to, playerColor); +} + +std::vector> Board::getAllValidMoves(Color playerColor) const { + std::vector> allMoves; + + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + Piece* piece = board[row][col].get(); + if (piece && piece->getColor() == playerColor) { + Position from(row, col); + std::vector validMoves = piece->getValidMoves(*this); + + for (const Position& to : validMoves) { + if (isMoveLegal(from, to, playerColor)) { + allMoves.push_back({from, to}); + } + } + } + } + } + + return allMoves; +} + +bool Board::isCheckmate(Color playerColor) const { + // If not in check, it's not checkmate + if (!isKingInCheck(playerColor)) return false; + + // If there are any valid moves, it's not checkmate + return getAllValidMoves(playerColor).empty(); +} + +bool Board::isStalemate(Color playerColor) const { + // If in check, it's not stalemate + if (isKingInCheck(playerColor)) return false; + + // If there are no valid moves, it's stalemate + return getAllValidMoves(playerColor).empty(); +} + +Position Board::getKingPosition(Color kingColor) const { + return (kingColor == Color::WHITE) ? whiteKingPos : blackKingPos; +} + +int Board::evaluatePosition() const { + int score = 0; + + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + Piece* piece = board[row][col].get(); + if (piece) { + int pieceValue = piece->getValue(); + if (piece->getColor() == Color::WHITE) { + score += pieceValue; + } else { + score -= pieceValue; + } + } + } + } + + return score; +} + +void Board::display() const { + std::cout << "\n a b c d e f g h\n"; + std::cout << " +---+---+---+---+---+---+---+---+\n"; + + for (int row = 0; row < 8; row++) { + std::cout << 8 - row << " |"; + for (int col = 0; col < 8; col++) { + Piece* piece = board[row][col].get(); + if (piece) { + std::cout << " " << piece->getSymbol() << " |"; + } else { + std::cout << " |"; + } + } + std::cout << " " << 8 - row << "\n"; + std::cout << " +---+---+---+---+---+---+---+---+\n"; + } + + std::cout << " a b c d e f g h\n\n"; +} + +void Board::placePiece(std::unique_ptr piece, const Position& pos) { + if (pos.isValid()) { + board[pos.row][pos.col] = std::move(piece); + } +} + +std::unique_ptr Board::clone() const { + auto clonedBoard = std::make_unique(); + + for (int row = 0; row < 8; row++) { + for (int col = 0; col < 8; col++) { + if (board[row][col]) { + clonedBoard->board[row][col] = board[row][col]->clone(); + } + } + } + + clonedBoard->whiteKingPos = whiteKingPos; + clonedBoard->blackKingPos = blackKingPos; + + return clonedBoard; +} diff --git a/Src/Chess_Game_AI/Board.h b/Src/Chess_Game_AI/Board.h new file mode 100644 index 0000000..7d06acf --- /dev/null +++ b/Src/Chess_Game_AI/Board.h @@ -0,0 +1,86 @@ +#ifndef BOARD_H +#define BOARD_H + +#include "Piece.h" +#include +#include +#include + +struct Move { + Position from; + Position to; + std::unique_ptr capturedPiece; + bool isCastling; + bool isEnPassant; + + Move(Position f, Position t) + : from(f), to(t), capturedPiece(nullptr), isCastling(false), isEnPassant(false) {} +}; + +class Board { +private: + std::array, 8>, 8> board; + std::vector moveHistory; + Position whiteKingPos; + Position blackKingPos; + +public: + Board(); + + // Initialize the board with standard chess setup + void initialize(); + + // Get piece at position + Piece* getPiece(const Position& pos) const; + + // Check if position is empty + bool isEmpty(const Position& pos) const; + + // Check if position has enemy piece + bool hasEnemyPiece(const Position& pos, Color playerColor) const; + + // Make a move + bool makeMove(const Position& from, const Position& to); + + // Undo last move + void undoMove(); + + // Check if king is in check + bool isKingInCheck(Color kingColor) const; + + // Check if it's checkmate + bool isCheckmate(Color playerColor) const; + + // Check if it's stalemate + bool isStalemate(Color playerColor) const; + + // Get all valid moves for a player + std::vector> getAllValidMoves(Color playerColor) const; + + // Validate if a move is legal (doesn't leave king in check) + bool isMoveLegal(const Position& from, const Position& to, Color playerColor) const; + + // Display the board + void display() const; + + // Get king position + Position getKingPosition(Color kingColor) const; + + // Evaluate board position for AI + int evaluatePosition() const; + + // Clone board for simulation + std::unique_ptr clone() const; + +private: + // Helper function to check if position is under attack + bool isPositionUnderAttack(const Position& pos, Color attackerColor) const; + + // Helper to simulate a move and check if king is safe + bool wouldKingBeSafe(const Position& from, const Position& to, Color playerColor) const; + + // Place a piece on the board + void placePiece(std::unique_ptr piece, const Position& pos); +}; + +#endif // BOARD_H diff --git a/Src/Chess_Game_AI/CMakeLists.txt b/Src/Chess_Game_AI/CMakeLists.txt new file mode 100644 index 0000000..c9389c3 --- /dev/null +++ b/Src/Chess_Game_AI/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.10) +project(ChessGameAI) + +# Set C++ standard +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Add executable +add_executable(chess_game + main.cpp + Piece.cpp + Board.cpp + Player.cpp + Game.cpp +) + +# Enable warnings +if(MSVC) + target_compile_options(chess_game PRIVATE /W4) +else() + target_compile_options(chess_game PRIVATE -Wall -Wextra -pedantic) +endif() diff --git a/Src/Chess_Game_AI/Game.cpp b/Src/Chess_Game_AI/Game.cpp new file mode 100644 index 0000000..144920f --- /dev/null +++ b/Src/Chess_Game_AI/Game.cpp @@ -0,0 +1,157 @@ +#include "Game.h" +#include +#include + +Game::Game() : currentPlayer(nullptr), gameState(GameState::PLAYING), moveCount(0) {} + +void Game::displayWelcome() const { + std::cout << "\n"; + std::cout << "========================================\n"; + std::cout << " CHESS GAME WITH AI IN C++ \n"; + std::cout << "========================================\n"; + std::cout << "\n"; + std::cout << "Piece Symbols:\n"; + std::cout << " White: K=King, Q=Queen, R=Rook, B=Bishop, N=Knight, P=Pawn\n"; + std::cout << " Black: k=king, q=queen, r=rook, b=bishop, n=knight, p=pawn\n"; + std::cout << "\n"; + std::cout << "How to play:\n"; + std::cout << " - Enter moves using algebraic notation (e.g., e2 e4)\n"; + std::cout << " - Columns: a-h, Rows: 1-8\n"; + std::cout << " - White starts first\n"; + std::cout << "\n"; + std::cout << "========================================\n\n"; +} + +void Game::initialize(bool playAgainstAI) { + displayWelcome(); + + // Initialize the board + board.initialize(); + + // Create players + whitePlayer = std::make_unique(Color::WHITE, "White Player"); + + if (playAgainstAI) { + std::cout << "Select AI difficulty:\n"; + std::cout << "1. Easy (depth 1)\n"; + std::cout << "2. Medium (depth 2)\n"; + std::cout << "3. Hard (depth 3)\n"; + std::cout << "Enter choice (1-3): "; + + int choice; + std::cin >> choice; + + if (std::cin.fail() || choice < 1 || choice > 3) { + std::cout << "Invalid choice. Setting to Medium difficulty.\n"; + choice = 2; + std::cin.clear(); + std::cin.ignore(std::numeric_limits::max(), '\n'); + } + + blackPlayer = std::make_unique(Color::BLACK, "AI", choice); + } else { + blackPlayer = std::make_unique(Color::BLACK, "Black Player"); + } + + // White starts first + currentPlayer = whitePlayer.get(); + gameState = GameState::PLAYING; + moveCount = 0; + + std::cout << "\nGame started! White moves first.\n\n"; +} + +void Game::displayGameInfo() const { + std::cout << "\n"; + std::cout << "Move #" << (moveCount / 2 + 1); + std::cout << " | Current turn: " << currentPlayer->getName(); + + Color currentColor = currentPlayer->getColor(); + if (board.isKingInCheck(currentColor)) { + std::cout << " - CHECK!"; + } + std::cout << "\n"; +} + +void Game::updateGameState() { + Color currentColor = currentPlayer->getColor(); + + if (board.isCheckmate(currentColor)) { + gameState = GameState::CHECKMATE; + } else if (board.isStalemate(currentColor)) { + gameState = GameState::STALEMATE; + } +} + +void Game::switchPlayer() { + if (currentPlayer == whitePlayer.get()) { + currentPlayer = blackPlayer.get(); + } else { + currentPlayer = whitePlayer.get(); + } +} + +bool Game::processTurn() { + // Display current game state + displayGameInfo(); + board.display(); + + // Get move from current player + auto move = currentPlayer->getMove(board); + + // Make the move + if (board.makeMove(move.first, move.second)) { + moveCount++; + + // Check game state after move + switchPlayer(); + updateGameState(); + + return true; + } else { + std::cout << "Invalid move! Try again.\n"; + return false; + } +} + +void Game::displayResult() const { + std::cout << "\n"; + std::cout << "========================================\n"; + std::cout << " GAME OVER \n"; + std::cout << "========================================\n"; + + board.display(); + + switch (gameState) { + case GameState::CHECKMATE: + std::cout << "\nCheckmate! "; + if (currentPlayer == whitePlayer.get()) { + std::cout << blackPlayer->getName() << " wins!\n"; + } else { + std::cout << whitePlayer->getName() << " wins!\n"; + } + break; + + case GameState::STALEMATE: + std::cout << "\nStalemate! The game is a draw.\n"; + break; + + case GameState::DRAW: + std::cout << "\nThe game is a draw.\n"; + break; + + default: + break; + } + + std::cout << "\nTotal moves: " << moveCount << "\n"; + std::cout << "========================================\n\n"; +} + +void Game::play() { + while (gameState == GameState::PLAYING) { + processTurn(); + } + + displayResult(); +} diff --git a/Src/Chess_Game_AI/Game.h b/Src/Chess_Game_AI/Game.h new file mode 100644 index 0000000..fda08d3 --- /dev/null +++ b/Src/Chess_Game_AI/Game.h @@ -0,0 +1,56 @@ +#ifndef GAME_H +#define GAME_H + +#include "Board.h" +#include "Player.h" +#include + +enum class GameState { + PLAYING, + CHECKMATE, + STALEMATE, + DRAW +}; + +class Game { +private: + Board board; + std::unique_ptr whitePlayer; + std::unique_ptr blackPlayer; + Player* currentPlayer; + GameState gameState; + int moveCount; + +public: + Game(); + + // Initialize the game + void initialize(bool playAgainstAI = true); + + // Main game loop + void play(); + + // Process a single turn + bool processTurn(); + + // Check game state (checkmate, stalemate, etc.) + void updateGameState(); + + // Display game information + void displayGameInfo() const; + + // Get current player + Player* getCurrentPlayer() const { return currentPlayer; } + + // Switch to next player + void switchPlayer(); + + // Display winner + void displayResult() const; + +private: + // Helper function to display welcome message + void displayWelcome() const; +}; + +#endif // GAME_H diff --git a/Src/Chess_Game_AI/INSTALLATION.md b/Src/Chess_Game_AI/INSTALLATION.md new file mode 100644 index 0000000..3015605 --- /dev/null +++ b/Src/Chess_Game_AI/INSTALLATION.md @@ -0,0 +1,244 @@ +# Chess Game AI - Installation and Compilation Guide + +## Prerequisites + +Before compiling the Chess Game, you need to have a C++ compiler installed on your system. + +--- + +## Windows + +### Option 1: Install MinGW-w64 (Recommended for Windows) + +1. Download MinGW-w64 from: https://www.mingw-w64.org/downloads/ + - Or use the installer: https://github.com/msys2/msys2-installer/releases + +2. Install and add to PATH: + - During installation, note the installation directory + - Add `C:\mingw-w64\bin` (or your installation path) to system PATH + - Open Command Prompt and verify: `g++ --version` + +3. Compile the game: + ```cmd + cd Src\Chess_Game_AI + build.bat + ``` + + Or manually: + ```cmd + g++ -std=c++14 -o chess_game.exe main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp + ``` + +4. Run the game: + ```cmd + chess_game.exe + ``` + +### Option 2: Microsoft Visual Studio + +1. Download Visual Studio Community (free): + https://visualstudio.microsoft.com/downloads/ + +2. During installation, select "Desktop development with C++" + +3. Open "Developer Command Prompt for VS" + +4. Compile: + ```cmd + cd Src\Chess_Game_AI + cl /EHsc /std:c++14 /Fe:chess_game.exe main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp + ``` + +5. Run: + ```cmd + chess_game.exe + ``` + +### Option 3: Using CMake + +1. Install CMake: https://cmake.org/download/ + +2. Build: + ```cmd + cd Src\Chess_Game_AI + mkdir build + cd build + cmake .. + cmake --build . + chess_game.exe + ``` + +--- + +## Linux + +### Ubuntu/Debian + +1. Install g++: + ```bash + sudo apt-get update + sudo apt-get install g++ build-essential + ``` + +2. Compile: + ```bash + cd Src/Chess_Game_AI + chmod +x build.sh + ./build.sh + ``` + + Or manually: + ```bash + g++ -std=c++14 -o chess_game main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp + ``` + +3. Run: + ```bash + ./chess_game + ``` + +### Fedora/RHEL/CentOS + +1. Install g++: + ```bash + sudo dnf install gcc-c++ + ``` + +2. Follow the same compilation steps as Ubuntu + +### Arch Linux + +1. Install g++: + ```bash + sudo pacman -S gcc + ``` + +2. Follow the same compilation steps as Ubuntu + +--- + +## macOS + +### Using Xcode Command Line Tools + +1. Install Xcode tools: + ```bash + xcode-select --install + ``` + +2. Compile: + ```bash + cd Src/Chess_Game_AI + chmod +x build.sh + ./build.sh + ``` + + Or manually: + ```bash + clang++ -std=c++14 -o chess_game main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp + ``` + +3. Run: + ```bash + ./chess_game + ``` + +### Using Homebrew + +1. Install g++ via Homebrew: + ```bash + brew install gcc + ``` + +2. Follow the same compilation steps + +--- + +## Using CMake (Cross-Platform) + +CMake provides a platform-independent build system. + +### Install CMake + +- **Windows**: Download from https://cmake.org/download/ +- **Linux**: `sudo apt-get install cmake` or `sudo dnf install cmake` +- **macOS**: `brew install cmake` + +### Build with CMake + +```bash +cd Src/Chess_Game_AI +mkdir build +cd build +cmake .. +cmake --build . +``` + +### Run + +- **Windows**: `chess_game.exe` +- **Linux/macOS**: `./chess_game` + +--- + +## Compilation Flags Explained + +- `-std=c++14`: Use C++14 standard +- `-Wall`: Enable all warnings +- `-Wextra`: Enable extra warnings +- `-o chess_game`: Output executable name + +--- + +## Troubleshooting + +### "g++ not recognized" (Windows) + +- Ensure g++ is installed and added to PATH +- Restart Command Prompt/PowerShell after installation + +### "Permission denied" (Linux/macOS) + +```bash +chmod +x chess_game +``` + +### "No such file or directory" + +- Ensure you're in the correct directory (`Src/Chess_Game_AI`) +- Verify all source files are present + +### Compilation Errors + +- Check that all `.h` and `.cpp` files are in the same directory +- Ensure compiler supports C++14 or later +- Update compiler if needed + +--- + +## Quick Start (After Compilation) + +1. Run the executable +2. Choose game mode: + - Option 1: Play against AI + - Option 2: Two-player mode +3. Select AI difficulty (if playing against AI) +4. Enter moves using algebraic notation (e.g., `e2 e4`) +5. Enjoy the game! + +--- + +## Files Required for Compilation + +Make sure all these files are present: +- `main.cpp` +- `Piece.h` and `Piece.cpp` +- `Board.h` and `Board.cpp` +- `Player.h` and `Player.cpp` +- `Game.h` and `Game.cpp` + +--- + +## Support + +For issues or questions, please refer to the README.md file or submit an issue on the project repository. diff --git a/Src/Chess_Game_AI/Piece.cpp b/Src/Chess_Game_AI/Piece.cpp new file mode 100644 index 0000000..6bc9c43 --- /dev/null +++ b/Src/Chess_Game_AI/Piece.cpp @@ -0,0 +1,222 @@ +#include "Piece.h" +#include "Board.h" + +// Base Piece constructor +Piece::Piece(Color c, PieceType t, Position pos) + : color(c), type(t), position(pos), hasMoved(false) {} + +// King implementation +King::King(Color c, Position pos) : Piece(c, PieceType::KING, pos) {} + +std::vector King::getValidMoves(const Board& board) const { + std::vector moves; + + // King can move one square in any direction + int directions[8][2] = { + {-1, -1}, {-1, 0}, {-1, 1}, + {0, -1}, {0, 1}, + {1, -1}, {1, 0}, {1, 1} + }; + + for (auto& dir : directions) { + Position newPos(position.row + dir[0], position.col + dir[1]); + if (newPos.isValid() && + (board.isEmpty(newPos) || board.hasEnemyPiece(newPos, color))) { + moves.push_back(newPos); + } + } + + return moves; +} + +std::unique_ptr King::clone() const { + auto cloned = std::make_unique(color, position); + cloned->setHasMoved(hasMoved); + return cloned; +} + +// Queen implementation +Queen::Queen(Color c, Position pos) : Piece(c, PieceType::QUEEN, pos) {} + +std::vector Queen::getValidMoves(const Board& board) const { + std::vector moves; + + // Queen moves like rook + bishop (all 8 directions) + int directions[8][2] = { + {-1, -1}, {-1, 0}, {-1, 1}, + {0, -1}, {0, 1}, + {1, -1}, {1, 0}, {1, 1} + }; + + for (auto& dir : directions) { + for (int i = 1; i < 8; i++) { + Position newPos(position.row + dir[0] * i, position.col + dir[1] * i); + + if (!newPos.isValid()) break; + + if (board.isEmpty(newPos)) { + moves.push_back(newPos); + } else { + if (board.hasEnemyPiece(newPos, color)) { + moves.push_back(newPos); + } + break; + } + } + } + + return moves; +} + +std::unique_ptr Queen::clone() const { + auto cloned = std::make_unique(color, position); + cloned->setHasMoved(hasMoved); + return cloned; +} + +// Rook implementation +Rook::Rook(Color c, Position pos) : Piece(c, PieceType::ROOK, pos) {} + +std::vector Rook::getValidMoves(const Board& board) const { + std::vector moves; + + // Rook moves horizontally and vertically + int directions[4][2] = { + {-1, 0}, {1, 0}, {0, -1}, {0, 1} + }; + + for (auto& dir : directions) { + for (int i = 1; i < 8; i++) { + Position newPos(position.row + dir[0] * i, position.col + dir[1] * i); + + if (!newPos.isValid()) break; + + if (board.isEmpty(newPos)) { + moves.push_back(newPos); + } else { + if (board.hasEnemyPiece(newPos, color)) { + moves.push_back(newPos); + } + break; + } + } + } + + return moves; +} + +std::unique_ptr Rook::clone() const { + auto cloned = std::make_unique(color, position); + cloned->setHasMoved(hasMoved); + return cloned; +} + +// Bishop implementation +Bishop::Bishop(Color c, Position pos) : Piece(c, PieceType::BISHOP, pos) {} + +std::vector Bishop::getValidMoves(const Board& board) const { + std::vector moves; + + // Bishop moves diagonally + int directions[4][2] = { + {-1, -1}, {-1, 1}, {1, -1}, {1, 1} + }; + + for (auto& dir : directions) { + for (int i = 1; i < 8; i++) { + Position newPos(position.row + dir[0] * i, position.col + dir[1] * i); + + if (!newPos.isValid()) break; + + if (board.isEmpty(newPos)) { + moves.push_back(newPos); + } else { + if (board.hasEnemyPiece(newPos, color)) { + moves.push_back(newPos); + } + break; + } + } + } + + return moves; +} + +std::unique_ptr Bishop::clone() const { + auto cloned = std::make_unique(color, position); + cloned->setHasMoved(hasMoved); + return cloned; +} + +// Knight implementation +Knight::Knight(Color c, Position pos) : Piece(c, PieceType::KNIGHT, pos) {} + +std::vector Knight::getValidMoves(const Board& board) const { + std::vector moves; + + // Knight moves in L-shape + int knightMoves[8][2] = { + {-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, + {1, -2}, {1, 2}, {2, -1}, {2, 1} + }; + + for (auto& move : knightMoves) { + Position newPos(position.row + move[0], position.col + move[1]); + + if (newPos.isValid() && + (board.isEmpty(newPos) || board.hasEnemyPiece(newPos, color))) { + moves.push_back(newPos); + } + } + + return moves; +} + +std::unique_ptr Knight::clone() const { + auto cloned = std::make_unique(color, position); + cloned->setHasMoved(hasMoved); + return cloned; +} + +// Pawn implementation +Pawn::Pawn(Color c, Position pos) : Piece(c, PieceType::PAWN, pos) {} + +std::vector Pawn::getValidMoves(const Board& board) const { + std::vector moves; + + int direction = (color == Color::WHITE) ? -1 : 1; // White moves up, Black moves down + int startRow = (color == Color::WHITE) ? 6 : 1; + + // Move forward one square + Position oneForward(position.row + direction, position.col); + if (oneForward.isValid() && board.isEmpty(oneForward)) { + moves.push_back(oneForward); + + // Move forward two squares from starting position + if (position.row == startRow) { + Position twoForward(position.row + 2 * direction, position.col); + if (twoForward.isValid() && board.isEmpty(twoForward)) { + moves.push_back(twoForward); + } + } + } + + // Capture diagonally + Position leftCapture(position.row + direction, position.col - 1); + if (leftCapture.isValid() && board.hasEnemyPiece(leftCapture, color)) { + moves.push_back(leftCapture); + } + + Position rightCapture(position.row + direction, position.col + 1); + if (rightCapture.isValid() && board.hasEnemyPiece(rightCapture, color)) { + moves.push_back(rightCapture); + } + + return moves; +} + +std::unique_ptr Pawn::clone() const { + auto cloned = std::make_unique(color, position); + cloned->setHasMoved(hasMoved); + return cloned; +} diff --git a/Src/Chess_Game_AI/Piece.h b/Src/Chess_Game_AI/Piece.h new file mode 100644 index 0000000..14d285e --- /dev/null +++ b/Src/Chess_Game_AI/Piece.h @@ -0,0 +1,128 @@ +#ifndef PIECE_H +#define PIECE_H + +#include +#include + +enum class PieceType { + KING, + QUEEN, + ROOK, + BISHOP, + KNIGHT, + PAWN, + NONE +}; + +enum class Color { + WHITE, + BLACK, + NONE +}; + +struct Position { + int row; + int col; + + Position(int r = 0, int c = 0) : row(r), col(c) {} + + bool operator==(const Position& other) const { + return row == other.row && col == other.col; + } + + bool isValid() const { + return row >= 0 && row < 8 && col >= 0 && col < 8; + } +}; + +class Board; // Forward declaration + +class Piece { +protected: + Color color; + PieceType type; + Position position; + bool hasMoved; + +public: + Piece(Color c, PieceType t, Position pos); + virtual ~Piece() = default; + + // Getters + Color getColor() const { return color; } + PieceType getType() const { return type; } + Position getPosition() const { return position; } + bool getHasMoved() const { return hasMoved; } + + // Setters + void setPosition(const Position& pos) { position = pos; } + void setHasMoved(bool moved) { hasMoved = moved; } + + // Pure virtual function for valid moves + virtual std::vector getValidMoves(const Board& board) const = 0; + + // Virtual function to get piece symbol + virtual char getSymbol() const = 0; + + // Get piece value for AI evaluation + virtual int getValue() const = 0; + + // Clone method for board simulation + virtual std::unique_ptr clone() const = 0; +}; + +class King : public Piece { +public: + King(Color c, Position pos); + std::vector getValidMoves(const Board& board) const override; + char getSymbol() const override { return (color == Color::WHITE) ? 'K' : 'k'; } + int getValue() const override { return 10000; } + std::unique_ptr clone() const override; +}; + +class Queen : public Piece { +public: + Queen(Color c, Position pos); + std::vector getValidMoves(const Board& board) const override; + char getSymbol() const override { return (color == Color::WHITE) ? 'Q' : 'q'; } + int getValue() const override { return 900; } + std::unique_ptr clone() const override; +}; + +class Rook : public Piece { +public: + Rook(Color c, Position pos); + std::vector getValidMoves(const Board& board) const override; + char getSymbol() const override { return (color == Color::WHITE) ? 'R' : 'r'; } + int getValue() const override { return 500; } + std::unique_ptr clone() const override; +}; + +class Bishop : public Piece { +public: + Bishop(Color c, Position pos); + std::vector getValidMoves(const Board& board) const override; + char getSymbol() const override { return (color == Color::WHITE) ? 'B' : 'b'; } + int getValue() const override { return 330; } + std::unique_ptr clone() const override; +}; + +class Knight : public Piece { +public: + Knight(Color c, Position pos); + std::vector getValidMoves(const Board& board) const override; + char getSymbol() const override { return (color == Color::WHITE) ? 'N' : 'n'; } + int getValue() const override { return 320; } + std::unique_ptr clone() const override; +}; + +class Pawn : public Piece { +public: + Pawn(Color c, Position pos); + std::vector getValidMoves(const Board& board) const override; + char getSymbol() const override { return (color == Color::WHITE) ? 'P' : 'p'; } + int getValue() const override { return 100; } + std::unique_ptr clone() const override; +}; + +#endif // PIECE_H diff --git a/Src/Chess_Game_AI/Player.cpp b/Src/Chess_Game_AI/Player.cpp new file mode 100644 index 0000000..f7fe5e4 --- /dev/null +++ b/Src/Chess_Game_AI/Player.cpp @@ -0,0 +1,204 @@ +#include "Player.h" +#include +#include +#include +#include + +// Player base class +Player::Player(Color c, const std::string& n) : color(c), name(n) {} + +// HumanPlayer implementation +HumanPlayer::HumanPlayer(Color c, const std::string& n) : Player(c, n) {} + +Position HumanPlayer::parsePosition(const std::string& pos) { + if (pos.length() != 2) { + return Position(-1, -1); // Invalid position + } + + char col = std::tolower(pos[0]); + char row = pos[1]; + + if (col < 'a' || col > 'h' || row < '1' || row > '8') { + return Position(-1, -1); // Invalid position + } + + // Convert algebraic notation to array indices + // a-h -> 0-7 (columns) + // 8-1 -> 0-7 (rows) + int colIndex = col - 'a'; + int rowIndex = 8 - (row - '0'); + + return Position(rowIndex, colIndex); +} + +std::pair HumanPlayer::getMove(const Board& board) { + std::string fromStr, toStr; + + while (true) { + std::cout << name << "'s turn. Enter move (e.g., e2 e4): "; + std::cin >> fromStr >> toStr; + + if (std::cin.fail()) { + std::cin.clear(); + std::cin.ignore(std::numeric_limits::max(), '\n'); + std::cout << "Invalid input. Please try again.\n"; + continue; + } + + Position from = parsePosition(fromStr); + Position to = parsePosition(toStr); + + if (!from.isValid() || !to.isValid()) { + std::cout << "Invalid position. Please use algebraic notation (e.g., e2 e4).\n"; + continue; + } + + Piece* piece = board.getPiece(from); + if (!piece) { + std::cout << "No piece at " << fromStr << ". Try again.\n"; + continue; + } + + if (piece->getColor() != color) { + std::cout << "That's not your piece! Try again.\n"; + continue; + } + + if (!board.isMoveLegal(from, to, color)) { + std::cout << "Illegal move. Try again.\n"; + continue; + } + + return {from, to}; + } +} + +// AIPlayer implementation +AIPlayer::AIPlayer(Color c, const std::string& n, int depth) + : Player(c, n), searchDepth(depth) {} + +int AIPlayer::evaluateBoard(const Board& board) const { + int score = 0; + + // Material evaluation + score = board.evaluatePosition(); + + // Adjust score based on perspective (AI is typically black) + if (color == Color::BLACK) { + score = -score; + } + + return score; +} + +int AIPlayer::minimax(Board& board, int depth, int alpha, int beta, bool isMaximizing) { + // Check for terminal states + Color currentColor = isMaximizing ? color : (color == Color::WHITE ? Color::BLACK : Color::WHITE); + + if (depth == 0) { + return evaluateBoard(board); + } + + if (board.isCheckmate(currentColor)) { + return isMaximizing ? -100000 : 100000; + } + + if (board.isStalemate(currentColor)) { + return 0; // Draw + } + + std::vector> allMoves = board.getAllValidMoves(currentColor); + + if (allMoves.empty()) { + return isMaximizing ? -100000 : 100000; + } + + if (isMaximizing) { + int maxEval = std::numeric_limits::min(); + + for (const auto& move : allMoves) { + // Create a copy of the board + auto boardCopy = board.clone(); + boardCopy->makeMove(move.first, move.second); + + int eval = minimax(*boardCopy, depth - 1, alpha, beta, false); + maxEval = std::max(maxEval, eval); + alpha = std::max(alpha, eval); + + if (beta <= alpha) { + break; // Alpha-beta pruning + } + } + + return maxEval; + } else { + int minEval = std::numeric_limits::max(); + + for (const auto& move : allMoves) { + // Create a copy of the board + auto boardCopy = board.clone(); + boardCopy->makeMove(move.first, move.second); + + int eval = minimax(*boardCopy, depth - 1, alpha, beta, true); + minEval = std::min(minEval, eval); + beta = std::min(beta, eval); + + if (beta <= alpha) { + break; // Alpha-beta pruning + } + } + + return minEval; + } +} + +std::pair AIPlayer::findBestMove(Board& board) { + std::vector> allMoves = board.getAllValidMoves(color); + + if (allMoves.empty()) { + return {Position(-1, -1), Position(-1, -1)}; // No valid moves + } + + int bestValue = std::numeric_limits::min(); + std::pair bestMove = allMoves[0]; + + std::cout << "AI is thinking"; + std::cout.flush(); + + for (const auto& move : allMoves) { + auto boardCopy = board.clone(); + boardCopy->makeMove(move.first, move.second); + + int moveValue = minimax(*boardCopy, searchDepth - 1, + std::numeric_limits::min(), + std::numeric_limits::max(), + false); + + if (moveValue > bestValue) { + bestValue = moveValue; + bestMove = move; + } + + std::cout << "."; + std::cout.flush(); + } + + std::cout << " Done!\n"; + + return bestMove; +} + +std::pair AIPlayer::getMove(const Board& board) { + Board boardCopy = board; // Create a non-const copy + auto move = findBestMove(boardCopy); + + // Convert positions to algebraic notation for display + char fromCol = 'a' + move.first.col; + char fromRow = '8' - move.first.row; + char toCol = 'a' + move.second.col; + char toRow = '8' - move.second.row; + + std::cout << name << " moves: " << fromCol << fromRow << " " << toCol << toRow << "\n"; + + return move; +} diff --git a/Src/Chess_Game_AI/Player.h b/Src/Chess_Game_AI/Player.h new file mode 100644 index 0000000..40ea63a --- /dev/null +++ b/Src/Chess_Game_AI/Player.h @@ -0,0 +1,52 @@ +#ifndef PLAYER_H +#define PLAYER_H + +#include "Board.h" +#include +#include + +class Player { +protected: + Color color; + std::string name; + +public: + Player(Color c, const std::string& n); + virtual ~Player() = default; + + Color getColor() const { return color; } + std::string getName() const { return name; } + + // Get move from player (pure virtual - different for human and AI) + virtual std::pair getMove(const Board& board) = 0; +}; + +class HumanPlayer : public Player { +public: + HumanPlayer(Color c, const std::string& n); + std::pair getMove(const Board& board) override; + +private: + Position parsePosition(const std::string& pos); +}; + +class AIPlayer : public Player { +private: + int searchDepth; + +public: + AIPlayer(Color c, const std::string& n, int depth = 3); + std::pair getMove(const Board& board) override; + +private: + // Minimax algorithm with alpha-beta pruning + int minimax(Board& board, int depth, int alpha, int beta, bool isMaximizing); + + // Evaluate best move + std::pair findBestMove(Board& board); + + // Helper function to evaluate board position + int evaluateBoard(const Board& board) const; +}; + +#endif // PLAYER_H diff --git a/Src/Chess_Game_AI/QUICKSTART.md b/Src/Chess_Game_AI/QUICKSTART.md new file mode 100644 index 0000000..49fd338 --- /dev/null +++ b/Src/Chess_Game_AI/QUICKSTART.md @@ -0,0 +1,214 @@ +# Chess Game - Quick Reference Guide + +## Board Layout + +``` + a b c d e f g h + +---+---+---+---+---+---+---+---+ +8 | r | n | b | q | k | b | n | r | 8 <- Black's back rank + +---+---+---+---+---+---+---+---+ +7 | p | p | p | p | p | p | p | p | 7 <- Black pawns + +---+---+---+---+---+---+---+---+ +6 | | | | | | | | | 6 + +---+---+---+---+---+---+---+---+ +5 | | | | | | | | | 5 + +---+---+---+---+---+---+---+---+ +4 | | | | | | | | | 4 + +---+---+---+---+---+---+---+---+ +3 | | | | | | | | | 3 + +---+---+---+---+---+---+---+---+ +2 | P | P | P | P | P | P | P | P | 2 <- White pawns + +---+---+---+---+---+---+---+---+ +1 | R | N | B | Q | K | B | N | R | 1 <- White's back rank + +---+---+---+---+---+---+---+---+ + a b c d e f g h +``` + +## Piece Symbols + +### White Pieces (Uppercase) +- **K** = King +- **Q** = Queen +- **R** = Rook +- **B** = Bishop +- **N** = Knight (N is used to avoid confusion with King) +- **P** = Pawn + +### Black Pieces (Lowercase) +- **k** = king +- **q** = queen +- **r** = rook +- **b** = bishop +- **n** = knight +- **p** = pawn + +## Algebraic Notation + +### Squares +- **Columns**: a, b, c, d, e, f, g, h (left to right) +- **Rows**: 1, 2, 3, 4, 5, 6, 7, 8 (bottom to top for White) +- Each square is identified by column + row (e.g., e4, a1, h8) + +### Move Format +``` +[from square] [to square] +``` + +Examples: +- `e2 e4` - Move piece from e2 to e4 +- `g1 f3` - Move piece from g1 to f3 +- `d7 d5` - Move piece from d7 to d5 + +## Common Opening Moves + +### White's Opening Moves +1. `e2 e4` - King's Pawn Opening (most popular) +2. `d2 d4` - Queen's Pawn Opening +3. `g1 f3` - King's Knight Opening +4. `c2 c4` - English Opening + +### Black's Responses to e2 e4 +1. `e7 e5` - King's Pawn (symmetrical) +2. `c7 c5` - Sicilian Defense +3. `e7 e6` - French Defense +4. `c7 c6` - Caro-Kann Defense + +## Piece Movement Rules + +### King (K/k) +- Moves one square in any direction +- Most important piece (losing it = checkmate) + +### Queen (Q/q) +- Moves any number of squares in any direction (horizontal, vertical, diagonal) +- Most powerful piece + +### Rook (R/r) +- Moves any number of squares horizontally or vertically +- Cannot move diagonally + +### Bishop (B/b) +- Moves any number of squares diagonally +- Each bishop stays on its starting color (light or dark squares) + +### Knight (N/n) +- Moves in "L" shape: 2 squares in one direction, 1 square perpendicular +- Only piece that can jump over other pieces + +### Pawn (P/p) +- Moves forward one square +- First move: can move forward two squares +- Captures diagonally (one square diagonally forward) +- Cannot move backward + +## Special Rules + +### Check +- When a king is under attack +- Player must move out of check +- Indicated by "CHECK!" message in the game + +### Checkmate +- When a king is in check and cannot escape +- Game over - checking player wins + +### Stalemate +- When a player has no legal moves but is not in check +- Game is a draw (tie) + +## Sample Game Sequence + +``` +White's turn: e2 e4 (Move pawn from e2 to e4) +Black's turn: e7 e5 (Move pawn from e7 to e5) +White's turn: g1 f3 (Move knight from g1 to f3) +Black's turn: b8 c6 (Move knight from b8 to c6) +White's turn: f1 c4 (Move bishop from f1 to c4) +Black's turn: f8 c5 (Move bishop from f8 to c5) +... +``` + +## Tips for Playing + +### For Beginners +1. Control the center (e4, e5, d4, d5) +2. Develop pieces (knights and bishops) early +3. Castle to protect your king +4. Don't move the same piece twice in the opening +5. Don't bring queen out too early + +### Playing Against AI +- **Easy**: Good for learning +- **Medium**: Balanced challenge +- **Hard**: Requires strategic thinking + +### Common Mistakes to Avoid +1. Leaving pieces undefended +2. Ignoring threats to your king +3. Moving without a plan +4. Trading good pieces for weaker ones +5. Not thinking ahead + +## Game End Scenarios + +### You Win +- Checkmate opponent's king +- Opponent resigns (in two-player mode) + +### Draw +- Stalemate (no legal moves, not in check) +- Insufficient material (e.g., king vs king) + +### You Lose +- Your king is checkmated +- You resign (in two-player mode) + +## Input Examples + +### Valid Inputs +- `e2 e4` ✓ +- `a7 a5` ✓ +- `h1 h3` ✓ + +### Invalid Inputs +- `e2e4` ✗ (missing space) +- `E2 E4` ✗ (use lowercase) +- `e9 e10` ✗ (invalid squares) +- `z5 a3` ✗ (column 'z' doesn't exist) + +## Strategy Tips + +### Opening Principles (First 10 moves) +1. Control center squares +2. Develop minor pieces (knights, bishops) +3. Castle early +4. Connect rooks + +### Middle Game +1. Look for tactical opportunities +2. Improve piece positions +3. Create threats +4. Defend against opponent's threats + +### End Game +1. Activate your king +2. Promote pawns +3. Coordinate pieces +4. Calculate precisely + +## Practice Exercises + +### Beginner +Try these opening sequences: +1. e2 e4, e7 e5, g1 f3, b8 c6 +2. d2 d4, d7 d5, c2 c4, e7 e6 + +### Intermediate +Practice knight moves: +1. Move knight from g1 to f3 +2. Then to e5 +3. Then back to f3 + +--- + +**Remember**: Chess is a game of strategy and patience. Take your time, think ahead, and enjoy the game! diff --git a/Src/Chess_Game_AI/README.md b/Src/Chess_Game_AI/README.md index ce90fe3..9ac46e2 100644 --- a/Src/Chess_Game_AI/README.md +++ b/Src/Chess_Game_AI/README.md @@ -8,36 +8,227 @@ The system is designed using **Object-Oriented Programming (OOP)** principles, m --- ## Features -- Play chess with all types of pieces: King, Queen, Rook, Bishop, Knight, and Pawn -- Human vs AI gameplay mode -- AI opponent uses **algorithmic decision-making** to determine optimal moves -- Console-based board display with ASCII representation -- Validates moves according to chess rules, including check, checkmate, and stalemate -- Optional: save and load game state +- ♟️ Play chess with all types of pieces: King, Queen, Rook, Bishop, Knight, and Pawn +- 🤖 Human vs AI gameplay mode with 3 difficulty levels +- 👥 Two-player mode (Human vs Human) +- 🧠 AI opponent uses **Minimax algorithm with Alpha-Beta pruning** for intelligent move selection +- 🎨 Console-based board display with ASCII representation +- ✅ Validates moves according to chess rules, including: + - Legal piece movements + - Check detection + - Checkmate detection + - Stalemate detection +- 🎯 Clean, modular, object-oriented design + +--- + +## Project Structure + +``` +Chess_Game_AI/ +├── Piece.h # Base Piece class and derived piece classes +├── Piece.cpp # Implementation of piece movement logic +├── Board.h # Board class for game state management +├── Board.cpp # Board operations and rule validation +├── Player.h # Player base class, HumanPlayer, and AIPlayer +├── Player.cpp # Player input handling and AI algorithm +├── Game.h # Game controller class +├── Game.cpp # Game loop and state management +├── main.cpp # Entry point with menu system +├── CMakeLists.txt # Build configuration +└── README.md # This file +``` --- ## Requirements -- **C++ Implementation**: Entirely written in C++ -- **Object-Oriented Design**: Uses classes like `Board`, `Piece`, `Player`, `AI`, `Game`, etc. -- **Clean Code Practices**: - - Meaningful variable and function names - - Proper input validation and error handling - - Well-documented methods and classes +- **C++ Compiler**: g++, clang++, or MSVC with C++14 support or higher +- **CMake**: Version 3.10 or higher (optional, for easier building) +- **Operating System**: Windows, Linux, or macOS + +--- + +## How to Build and Run + +### Option 1: Using CMake (Recommended) + +```bash +# Navigate to the Chess_Game_AI directory +cd Src/Chess_Game_AI + +# Create build directory +mkdir build +cd build + +# Generate build files +cmake .. + +# Build the project +cmake --build . + +# Run the game +./chess_game # On Linux/macOS +chess_game.exe # On Windows +``` + +### Option 2: Manual Compilation + +#### On Windows (using g++): +```bash +cd Src\Chess_Game_AI +g++ -std=c++14 -o chess_game.exe main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp +chess_game.exe +``` + +#### On Linux/macOS: +```bash +cd Src/Chess_Game_AI +g++ -std=c++14 -o chess_game main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp +./chess_game +``` --- ## Gameplay -- Players input moves using **algebraic notation**, e.g., `e2 e4`. -- AI calculates moves based on **algorithmic evaluation of the board**. -- Game ends when **checkmate**, **stalemate**, or **draw** occurs. + +### Starting the Game +1. Run the executable +2. Choose from the main menu: + - **1**: Play against AI + - **2**: Two-player mode + - **3**: Exit + +### AI Difficulty Levels +When playing against AI, select difficulty: +- **Easy (Depth 1)**: AI looks 1 move ahead +- **Medium (Depth 2)**: AI looks 2 moves ahead +- **Hard (Depth 3)**: AI looks 3 moves ahead + +### Making Moves +- Players input moves using **algebraic notation** +- Format: `[from] [to]` (e.g., `e2 e4`) +- Columns: **a-h** (left to right) +- Rows: **1-8** (bottom to top for white) + +### Examples: +- `e2 e4` - Move pawn from e2 to e4 +- `g1 f3` - Move knight from g1 to f3 +- `e7 e5` - Move pawn from e7 to e5 + +### Piece Notation +- **White pieces**: K=King, Q=Queen, R=Rook, B=Bishop, N=Knight, P=Pawn +- **Black pieces**: k=king, q=queen, r=rook, b=bishop, n=knight, p=pawn + +### Game End Conditions +- **Checkmate**: One player's king is under attack and cannot escape +- **Stalemate**: Current player has no legal moves but is not in check (Draw) --- +## Architecture -## How to Run -1. Clone the repository: +### Class Design -```bash -git clone https://github.com//ChessAI.git -cd ChessAI +#### 1. **Piece Hierarchy** +- `Piece` (Abstract base class) + - `King` + - `Queen` + - `Rook` + - `Bishop` + - `Knight` + - `Pawn` + +Each piece implements: +- `getValidMoves()`: Returns all possible moves +- `getValue()`: Returns piece value for AI evaluation +- `clone()`: Creates a copy for board simulation + +#### 2. **Board** +- Manages 8×8 game board +- Handles piece placement and movement +- Validates move legality +- Detects check, checkmate, and stalemate +- Provides board evaluation for AI + +#### 3. **Player System** +- `Player` (Abstract base class) + - `HumanPlayer`: Handles user input + - `AIPlayer`: Implements AI decision-making + +#### 4. **Game Controller** +- Manages game flow +- Handles turn alternation +- Displays board state +- Processes win/loss/draw conditions + +### AI Algorithm + +The AI uses **Minimax algorithm with Alpha-Beta pruning**: + +1. **Minimax**: Evaluates all possible moves recursively +2. **Alpha-Beta Pruning**: Optimizes search by eliminating branches that won't affect the final decision +3. **Board Evaluation**: Calculates position value based on: + - Material count (piece values) + - King: 10000, Queen: 900, Rook: 500, Bishop: 330, Knight: 320, Pawn: 100 + +--- + +## Code Highlights + +### Object-Oriented Principles +- **Encapsulation**: Each class manages its own state +- **Inheritance**: Piece hierarchy with polymorphic behavior +- **Abstraction**: Abstract base classes for extensibility +- **Polymorphism**: Virtual functions for piece-specific behavior + +### Design Patterns +- **Strategy Pattern**: Different player types (Human/AI) +- **Template Method**: Common game flow with specialized player moves +- **Prototype Pattern**: Piece cloning for board simulation + +### Best Practices +- Smart pointers (`std::unique_ptr`) for memory management +- Const correctness throughout +- Input validation and error handling +- Clear naming conventions +- Modular, testable code structure + +--- + +## Future Enhancements + +- [ ] Save/Load game state to file +- [ ] En passant and castling special moves +- [ ] Pawn promotion +- [ ] Move history with undo functionality +- [ ] Advanced AI evaluation (position-based scoring) +- [ ] GUI version using a graphics library +- [ ] Network multiplayer support +- [ ] Opening book for AI +- [ ] Time controls + +--- + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +--- + +## License + +This project is open source and available under the MIT License. + +--- + +## Author + +Developed as part of the CPP_Mini_Projects collection. + +--- + +## Acknowledgments + +- Chess rules and notation based on FIDE standards +- AI algorithm inspired by classic game theory +- Built with modern C++ best practices diff --git a/Src/Chess_Game_AI/build.bat b/Src/Chess_Game_AI/build.bat new file mode 100644 index 0000000..d951814 --- /dev/null +++ b/Src/Chess_Game_AI/build.bat @@ -0,0 +1,87 @@ +@echo off +REM Chess Game Compilation Script for Windows + +echo ======================================== +echo Chess Game AI - Build Script +echo ======================================== +echo. + +REM Check for g++ compiler +where g++ >nul 2>&1 +if %ERRORLEVEL% EQU 0 ( + echo Found g++ compiler + echo Compiling with g++... + g++ -std=c++14 -Wall -Wextra -o chess_game.exe main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp + if %ERRORLEVEL% EQU 0 ( + echo. + echo ======================================== + echo Build successful! + echo Run chess_game.exe to play + echo ======================================== + pause + exit /b 0 + ) else ( + echo Build failed! + pause + exit /b 1 + ) +) + +REM Check for MSVC compiler +where cl >nul 2>&1 +if %ERRORLEVEL% EQU 0 ( + echo Found MSVC compiler + echo Compiling with MSVC... + cl /EHsc /std:c++14 /Fe:chess_game.exe main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp + if %ERRORLEVEL% EQU 0 ( + echo. + echo ======================================== + echo Build successful! + echo Run chess_game.exe to play + echo ======================================== + pause + exit /b 0 + ) else ( + echo Build failed! + pause + exit /b 1 + ) +) + +REM Check for clang++ +where clang++ >nul 2>&1 +if %ERRORLEVEL% EQU 0 ( + echo Found clang++ compiler + echo Compiling with clang++... + clang++ -std=c++14 -o chess_game.exe main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp + if %ERRORLEVEL% EQU 0 ( + echo. + echo ======================================== + echo Build successful! + echo Run chess_game.exe to play + echo ======================================== + pause + exit /b 0 + ) else ( + echo Build failed! + pause + exit /b 1 + ) +) + +echo ======================================== +echo ERROR: No C++ compiler found! +echo ======================================== +echo. +echo Please install one of the following: +echo 1. MinGW-w64 (includes g++) +echo Download from: https://www.mingw-w64.org/ +echo. +echo 2. Microsoft Visual Studio (includes MSVC) +echo Download from: https://visualstudio.microsoft.com/ +echo. +echo 3. LLVM/Clang +echo Download from: https://releases.llvm.org/ +echo. +pause +exit /b 1 diff --git a/Src/Chess_Game_AI/build.sh b/Src/Chess_Game_AI/build.sh new file mode 100644 index 0000000..0b97dc1 --- /dev/null +++ b/Src/Chess_Game_AI/build.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# Chess Game Compilation Script for Linux/macOS + +echo "========================================" +echo "Chess Game AI - Build Script" +echo "========================================" +echo + +# Check for g++ compiler +if command -v g++ &> /dev/null; then + echo "Found g++ compiler" + echo "Compiling with g++..." + g++ -std=c++14 -Wall -Wextra -o chess_game main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp + + if [ $? -eq 0 ]; then + echo + echo "========================================" + echo "Build successful!" + echo "Run ./chess_game to play" + echo "========================================" + chmod +x chess_game + exit 0 + else + echo "Build failed!" + exit 1 + fi +fi + +# Check for clang++ +if command -v clang++ &> /dev/null; then + echo "Found clang++ compiler" + echo "Compiling with clang++..." + clang++ -std=c++14 -Wall -Wextra -o chess_game main.cpp Piece.cpp Board.cpp Player.cpp Game.cpp + + if [ $? -eq 0 ]; then + echo + echo "========================================" + echo "Build successful!" + echo "Run ./chess_game to play" + echo "========================================" + chmod +x chess_game + exit 0 + else + echo "Build failed!" + exit 1 + fi +fi + +echo "========================================" +echo "ERROR: No C++ compiler found!" +echo "========================================" +echo +echo "Please install one of the following:" +echo "Ubuntu/Debian: sudo apt-get install g++" +echo "Fedora/RHEL: sudo dnf install gcc-c++" +echo "macOS: xcode-select --install" +echo +exit 1 diff --git a/Src/Chess_Game_AI/main.cpp b/Src/Chess_Game_AI/main.cpp new file mode 100644 index 0000000..ac35962 --- /dev/null +++ b/Src/Chess_Game_AI/main.cpp @@ -0,0 +1,63 @@ +#include "Game.h" +#include +#include + +void displayMenu() { + std::cout << "\n"; + std::cout << "========================================\n"; + std::cout << " CHESS GAME MAIN MENU \n"; + std::cout << "========================================\n"; + std::cout << "1. Play against AI\n"; + std::cout << "2. Two player mode\n"; + std::cout << "3. Exit\n"; + std::cout << "========================================\n"; + std::cout << "Enter your choice: "; +} + +int main() { + bool running = true; + + while (running) { + displayMenu(); + + int choice; + std::cin >> choice; + + if (std::cin.fail()) { + std::cin.clear(); + std::cin.ignore(std::numeric_limits::max(), '\n'); + std::cout << "Invalid input. Please try again.\n"; + continue; + } + + switch (choice) { + case 1: { + Game game; + game.initialize(true); // Play against AI + game.play(); + break; + } + case 2: { + Game game; + game.initialize(false); // Two player mode + game.play(); + break; + } + case 3: + std::cout << "\nThank you for playing! Goodbye.\n"; + running = false; + break; + default: + std::cout << "Invalid choice. Please select 1, 2, or 3.\n"; + break; + } + + if (running && choice != 3) { + std::cout << "\nPress Enter to return to main menu..."; + std::cin.ignore(std::numeric_limits::max(), '\n'); + std::cin.get(); + } + } + + return 0; +}