hayantariq/Chess-Engine-C-17-Console

GitHub: hayantariq/Chess-Engine-C-17-Console

用现代 C++17 编写的全规则国际象棋引擎及终端界面,完整实现 FIDE 规则并通过 103 项自动化测试,适合作为 C++ 面向对象编程与游戏引擎设计的学习参考。

Stars: 0 | Forks: 0

♔ 国际象棋引擎 — C++17 控制台

Language Build Tests Warnings Platform License

一个功能齐全、符合标准的国际象棋引擎和终端 UI,使用现代 C++17 编写。
实现了每一项 FIDE 规则,包括王车易位、吃过路兵、升变、牵制检测以及将军/将杀/逼和评估——零编译器警告,并包含 103 个测试的自动化套件。

## 作者 | 姓名 | 角色 | |------|------| | **Hayan Tariq** | 开发者 | | **Hamza Nasir** | 开发者 | | **Ali Shahid** | 开发者 | ## 目录 - [快速开始](#quick-start) - [功能特性](#features) - [项目结构](#project-structure) - [架构](#architecture) - [类层次结构](#class-hierarchy) - [应用的面向对象原则](#oop-principles-applied) - [核心子系统](#core-subsystems) - [安装说明](#installation) - [Linux / macOS](#linux--macos) - [Windows](#windows) - [使用方法](#usage) - [走法记号](#move-notation) - [特殊走法](#special-moves) - [游戏内命令](#in-game-commands) - [测试](#testing) - [运行测试套件](#running-the-suite) - [测试覆盖率](#test-coverage) - [技术参考](#technical-reference) - [走子验证管道](#move-validation-pipeline) - [攻击检测](#attack-detection) - [内存管理](#memory-management) - [编译标志](#compilation-flags) - [已知局限性](#known-limitations) - [更新日志](#changelog) - [许可证](#license) ## 快速开始 ``` git clone https://github.com/your-username/chess_game.git cd chess_game make ./chess_game ``` 需要支持 C++17 的 GCC 7+ / Clang 6+ 以及 GNU Make。完整要求请参阅 [安装说明](#installation)。 ## 功能特性 **规则与逻辑** - 为全部六种棋子(王、后、车、象、马、兵)生成完整的合法走法 - 实时检测将军、将杀和逼和 - 王车易位(短易位和长易位),并执行所有 FIDE 先决条件检查 - 吃过路兵,一步之后自动失效 - 兵升变为后、车、象或马 - 牵制检测——被牵制的棋子不能离开牵制线 - 通过棋盘副本进行走子验证——在合法性检查期间绝不改变原始状态 **界面** - 256 色 ANSI 背景方格,结合与方格感知的棋子前景色(在所有四种棋子颜色/方格颜色组合中保证对比度) - Unicode 国际象棋符号:♔♕♖♗♘♙ / ♚♛♜♝♞♟ - 稳定的输入循环——棋盘仅在每回合开始时重绘,输入无效时不会重绘 - 状态面板,显示玩家名称、步数计数器和游戏状态 **代码质量** - 在 `-Wall -Wextra -Wpedantic` 下零编译器警告 - 103 个自动化单元/集成测试——全部通过 - 无内存泄漏:RAII 所有权,使用虚函数 `clone()` 进行深拷贝,使用 `std::unique_ptr` 管理玩家生命周期 - 严格分离头文件和实现文件 ## 项目结构 ``` chess_game/ ├── include/ # Public interface — all header files │ ├── Types.h # Enumerations (PieceColor, PieceType, GameStatus), │ │ # Position, Move structs, ANSI color constants │ ├── Piece.h # Abstract base class: generatePseudoLegalMoves(), │ │ # getUnicodeSymbol(), clone() — all pure virtual │ ├── King.h │ ├── Queen.h │ ├── Bishop.h │ ├── Knight.h │ ├── Rook.h │ ├── Pawn.h │ ├── ChessBoard.h # Board state (8×8 Piece* grid), deep copy, │ │ # apply/undo move, isSquareAttackedBy() │ ├── MoveValidator.h # Legal move generation, pin detection, │ │ # move-legality filtering via board copy │ ├── GameStateManager.h # Check / Checkmate / Stalemate evaluation │ ├── Player.h # Player identity and color │ ├── ConsoleUI.h # All rendering and user input (static class) │ └── GameManager.h # Central game controller — turn loop, move dispatch │ ├── src/ # Implementation files (.cpp) │ ├── Piece.cpp │ ├── King.cpp │ ├── Queen.cpp │ ├── Bishop.cpp │ ├── Knight.cpp │ ├── Rook.cpp │ ├── Pawn.cpp │ ├── ChessBoard.cpp │ ├── MoveValidator.cpp │ ├── GameStateManager.cpp │ ├── Player.cpp │ ├── ConsoleUI.cpp │ └── GameManager.cpp │ ├── tests/ │ └── test_suite.cpp # 103 automated tests (custom lightweight runner) │ ├── main.cpp # Entry point ├── Makefile ├── .gitignore └── README.md ``` ## 架构 ### 类层次结构 ``` Piece (abstract) ├── King ├── Queen ├── Rook ├── Bishop ├── Knight └── Pawn GameManager ├── ChessBoard — owns Piece* [8][8] (heap, deep copy via clone()) ├── Player (White) — owned via std::unique_ptr ├── Player (Black) — owned via std::unique_ptr ├── MoveValidator — static; all methods operate on board references ├── GameStateManager — static; evaluates terminal game conditions └── ConsoleUI — static; all I/O isolated from game logic ``` ### Mermaid 类图 ``` classDiagram class Piece { <> -PieceColor m_color -bool m_hasMoved +generatePseudoLegalMoves(board) vector~Move~* +getUnicodeSymbol() string* +clone() Piece** +isWhite() bool +hasMoved() bool } Piece <|-- King Piece <|-- Queen Piece <|-- Rook Piece <|-- Bishop Piece <|-- Knight Piece <|-- Pawn class ChessBoard { -Piece* m_board[8][8] -Position m_enPassantTarget +applyMove(move) +isSquareAttackedBy(pos, color) bool +getPiece(pos) Piece* +ChessBoard(other) // deep copy } class GameManager { -ChessBoard m_board -unique_ptr~Player~ m_players[2] -GameStatus m_gameStatus +startGame() +runGameLoop() } class MoveValidator { <> +getLegalMoves(board, color) vector~Move~$ +isMoveLegal(board, move) bool$ } class GameStateManager { <> +evaluateStatus(board, color) GameStatus$ +isInCheck(board, color) bool$ } class ConsoleUI { <> +displayBoard(board)$ +getMoveInput() string$ +displayGameOver(status)$ } GameManager --> ChessBoard GameManager --> MoveValidator GameManager --> GameStateManager GameManager --> ConsoleUI ChessBoard --> Piece ``` ### 应用的面向对象原则 | 原则 | 实现 | |-----------|---------------| | **抽象** | `Piece` 将 `generatePseudoLegalMoves()`、`getUnicodeSymbol()` 和 `clone()` 声明为纯虚函数 (`= 0`)。调用者依赖于接口,而不是具体类型。 | | **继承** | 所有六种棋子类型都扩展了 `Piece`,继承了颜色、走子标志和标识逻辑,同时仅覆盖走法生成和符号渲染。 | | **多态** | `ChessBoard` 存储原始的 `Piece*`。走法生成、渲染和克隆通过 vtable 在运行时进行分派——无需类型切换。 | | **封装** | 所有数据成员都使用 `m_` 前缀并且是私有的。公共接口是精简且有意设计的。 | | **组合** | `GameManager` 组合了 `ChessBoard` 和两个 `Player` 实例。`ChessBoard` 拥有完整的棋子网格。这两个类互不为子类型。 | | **RAII** | `ChessBoard` 的析构函数释放所有堆分配的棋子。`std::unique_ptr` 确保确定性的 Player 清理。用于验证的临时棋盘副本是栈作用域的。 | ### 核心子系统 #### `ChessBoard` - 在堆上拥有一个扁平的 `Piece* m_board[8][8]`。 - 拷贝构造函数和拷贝赋值运算符通过对每个非空单元调用虚函数 `Piece::clone()` 来执行 **深克隆**。 - 将过路兵目标格 (`m_enPassantTarget`) 作为棋盘状态的一部分保存,并在深拷贝期间正确传播。 - `isSquareAttackedBy(pos, color)` 执行五次独立的扫描过程(参见 [攻击检测](#attack-detection))。 #### `MoveValidator` - `getLegalMoves(board, color)` 从 `color` 方的每个棋子收集伪合法走法,然后通过 `isMoveLegal` 进行过滤。 - `isMoveLegal(board, move)` 在棋盘的 **副本** 上应用走法,并在走子方的王的位置调用 `isSquareAttackedBy`。只有在走子后王没有被将军时才返回 `true`。 #### `GameStateManager` - `evaluateStatus(board, color)` 检查 `color` 方是否被将军以及是否存在任何合法走法。 - 将死 = 被将军 且 无合法走法。 - 逼和 = 未被将军 且 无合法走法。 #### `ConsoleUI` - 完全静态的 I/O 类——无状态,与游戏逻辑无耦合。 - `displayBoard()` 根据棋子颜色和方格颜色计算每个单元格的前景色,以保证可读性(四种不同的组合)。 - `getMoveInput()` 在阻塞于 `std::getline` 之前刷新 `stdout`,防止缓冲输出显示错误。 ## 安装说明 ### Linux / macOS **前置条件** | 工具 | 最低版本 | |------|----------------| | GCC 或 Clang | GCC 7 / Clang 6 | | GNU Make | 4.0 | | 终端 | UTF-8 + ANSI 256 色 (GNOME Terminal, Konsole, iTerm2 等) | **构建** ``` # 构建所有 targets make # 运行游戏 ./chess_game # 一步完成构建并运行 make run # 移除构建产物(obj/ 和 binaries) make clean ``` ### Windows #### 选项 A — MSYS2 + MinGW-w64(推荐) ``` # 1. 从 https://www.msys2.org/ 安装 MSYS2 # 2. 打开 MSYS2 MinGW 64-bit shell 并安装 toolchain: pacman -S mingw-w64-x86_64-gcc make # 3. 导航到项目目录 cd /c/path/to/chess_game # 4. 构建并运行 make ./chess_game.exe ``` #### 选项 B — 独立版 MinGW-w64 ``` g++ -std=c++17 -Wall -I include ^ src\Bishop.cpp src\ChessBoard.cpp src\ConsoleUI.cpp ^ src\GameManager.cpp src\GameStateManager.cpp src\King.cpp ^ src\Knight.cpp src\MoveValidator.cpp src\Pawn.cpp ^ src\Piece.cpp src\Player.cpp src\Queen.cpp src\Rook.cpp ^ main.cpp -o chess_game.exe chess_game.exe ``` #### 选项 C — Visual Studio 2019 / 2022 (MSVC) 1. **文件 → 新建 → 项目 → 空白 C++ 项目** 2. 添加 `include/` 和 `src/` 中的所有文件,以及 `main.cpp` 3. **项目属性 → C/C++ → 语言 → C++ 语言标准 → ISO C++17** 4. 在 **Windows Terminal** 中构建并运行以支持 ANSI ## 使用方法 ### 走法记号 走法使用简单的 `<起点> <终点>` 格式,并采用标准代数方格名称: ``` a b c d e f g h ┌───┬───┬───┬───┬───┬───┬───┬───┐ │ │ │ │ │ │ │ │ │ 8 ├───┼───┼───┼───┼───┼───┼───┼───┤ │ │ │ │ │ │ │ │ │ 7 ├───┼───┼───┼───┼───┼───┼───┼───┤ ... ├───┼───┼───┼───┼───┼───┼───┼───┤ │ │ │ │ │ │ │ │ │ 2 ├───┼───┼───┼───┼───┼───┼───┼───┤ │ │ │ │ │ │ │ │ │ 1 └───┴───┴───┴───┴───┴───┴───┴───┘ ``` | 输入 | 动作 | |-------|--------| | `e2 e4` | 将兵从 e2 推进到 e4 | | `g1 f3` | 出马 g1 → f3 | | `e1 g1` | 短易位(白方) | | `e1 c1` | 长易位(白方) | | `e8 g8` | 短易位(黑方) | | `e8 c8` | 长易位(黑方) | ### 特殊走法 **王车易位** — 王向车移动两格: - 王和车之前不能移动过 - 它们之间的方格不能有其他棋子占据 - 王不能处于被将军状态,不能穿越被攻击的方格,也不能落在被攻击的方格上 **吃过路兵** — 斜线吃掉刚刚前进两格的相邻敌方兵: - 仅在紧接着的下一步合法;机会在一回合后失效 - 输入目标方格(空的对角线方格)——引擎会自动移除被吃的兵 **兵的升变** — 当兵到达底线时,引擎会提示: ``` Promote to: [Q]ueen [R]ook [B]ishop k[N]ight > ``` 输入字母并按回车键。如果只按回车键,则默认为后。 ### 游戏内命令 | 命令 | 效果 | |---------|--------| | `resign` | 立即认输;对手获胜 | ## 测试 ### 运行测试套件 ``` # 编译测试 binary g++ -std=c++17 -Wall -Wextra -Wpedantic -I include \ src/*.cpp tests/test_suite.cpp -o tests/run_tests # 执行 ./tests/run_tests ``` 预期输出: ``` ========================================================= COMPLETE CHESS GAME — TEST SUITE Nayan Tariq | Hamza Nasir | Ali Shahid ========================================================= --- TEST: Initial Board Setup --- [PASS] White has 16 pieces ... ========================================================= RESULTS: 103 PASSED | 0 FAILED ========================================================= ``` 测试运行器使用了一个最小化的自定义 `CHECK(condition, label)` 宏——不需要外部框架。 ### 测试覆盖率 | 测试组 | 用例数 | 验证内容 | |------------|:-----:|-----------------| | 初始棋盘设置 | 52 | 正确的棋子数量、位置和空方格 | | 合法走法数 | 2 | 每方恰好 20 种合法开局走法 | | 学者将杀 | 4 | 4 步将杀:将军标志、将杀标志、非逼和、`evaluateStatus` | | 愚者将杀 | 1 | 白方最快的将杀 | | 将军检测 | 4 | 车将军,evaluateStatus 返回 CHECK,所有逃脱走法均合法 | | 垫将 | 1 | 没有走法会使己方的王暴露 | | 吃过路兵 | 5 | 目标设置、走法生成、兵被移除、白方兵在 d6、失效 | | 吃过路兵失效 | 1 | 在后续走法后目标被清除 | | 短易位 | 7 | 合法走法、王在 g1、车在 f1、e1/h1 为空、双方的 `hasMoved` 标志 | | 长易位 | 5 | 合法走法、王在 c1、车在 d1、e1/a1 为空 | | 易位安全性 | 4 | 不能穿越被攻击的过渡格进行易位;不能在被将军时易位 | | 兵升变 | 5 | 生成 4 种走法,放置正确的棋子类型和颜色,起点清空 | | 通过吃子升变 | 1 | 对角线吃子进入升变行生成 4 种走法 | | 逼和 | 4 | 未被将军,无合法走法,正确识别,非将杀 | | 牵制检测 | 1 | 被牵制的车不能离开牵制线 | | 深拷贝 | 4 | 对副本的修改不影响原件;两者状态均正确 | | **总计** | **103** | | ## 技术参考 ### 走子验证管道 ``` generatePseudoLegalMoves(board) ← per-piece geometric moves (ignores check) │ ▼ MoveValidator::isMoveLegal(board, move) ├── Apply move on a deep-copied board ├── Locate the moving side's king on the copy └── ChessBoard::isSquareAttackedBy(kingPos, opponent) │ ├── returns false → move is LEGAL └── returns true → move is ILLEGAL (king left/entered check) ``` ### 攻击检测 `ChessBoard::isSquareAttackedBy(pos, attackerColor)` 在棋盘上运行五次独立的扫描: | 遍数 | 覆盖棋子 | 方法 | |------|---------------|--------| | 1 | 马 | 8 个固定的 L 形偏移 | | 2 | 兵 | 2 个方向相关的对角线偏移 | | 3 | 车 + 后 | 4 个正交射线(遇到第一个占据者时停止) | | 4 | 象 + 后 | 4 个对角线射线(遇到第一个占据者时停止) | | 5 | 王 | 8 个相邻方格(防止王互相靠近) | 每次基于射线的遍历都会在第一个被占据的方格处终止——正确阻挡了穿过占据方格的攻击。 ### 内存管理 | 组件 | 策略 | |-----------|---------| | `ChessBoard` 单元格 | 棋盘拥有的原始 `Piece*`;在 `~ChessBoard()` 中删除 | | 棋盘副本(验证) | 栈分配的 `ChessBoard` 副本;析构函数自动释放所有棋子 | | `Player` 实例 | `GameManager` 中的 `std::unique_ptr`——作用域退出时自动清理 | | `Piece` 子类克隆 | 虚函数 `Piece::clone()` 返回一个 `new` 分配的具体子类型;由 `ChessBoard` 拷贝构造函数 | ### 编译标志 ``` CXXFLAGS = -std=c++17 -Wall -Wextra -Wpedantic -I include ``` | 标志 | 目的 | |------|---------| | `-std=c++17` | 启用结构化绑定、`std::make_unique`、`if constexpr` 等 | | `-Wall` | 启用所有标准诊断警告 | | `-Wextra` | 启用 `-Wall` 未涵盖的额外诊断 | | `-Wpedantic` | 强制严格遵守 ISO C++17;拒绝语言扩展 | | `-I include` | 将 `include/` 添加到头文件搜索路径 | ## 已知局限性 - **无时间控制** — 无计时器;仅限无限制时间的对弈 - **无逼和判和** — 未检测三次重复局面 - **无 50 步规则** — 不追踪半回合计数器 - **无持久化游戏存档/读档** — 无 FEN 导入/导出 - **无 AI 对手** — 仅限双人本地模式 ## 更新日志 ### v1.0.0 — 2026 年 5 月(首次发布) - 完整的 FIDE 规则实现:王车易位、吃过路兵、升变、牵制、将军/将杀/逼和 - 256 色 ANSI 棋盘,带有 Unicode 棋子符号和方格感知的前景色 - 稳定的按回合输入循环(仅在成功走子时重绘棋盘) - 103 个测试的自动化套件——103 个通过,0 个失败 - 在 `-Wall -Wextra -Wpedantic` 下零编译器警告 ## 许可证 本项目为学术和教育目的而开发。 © 2026 Hayan Tariq, Hamza Nasir, Ali Shahid。保留所有权利。
标签:C++, C++17, DNS解析, FIDE规则, HTTP头分析, OOP, 兵的升变, 单元测试, 博弈论, 吃过路兵, 国际象棋, 国际象棋引擎, 学术项目, 将军, 将杀, 开源项目, 控制台应用, 数据擦除, 桌面游戏, 棋类游戏, 王车易位, 现代C++, 算法, 终端UI, 编译无警告, 走法验证, 逼和, 面向对象编程