hayantariq/Chess-Engine-C-17-Console
GitHub: hayantariq/Chess-Engine-C-17-Console
用现代 C++17 编写的全规则国际象棋引擎及终端界面,完整实现 FIDE 规则并通过 103 项自动化测试,适合作为 C++ 面向对象编程与游戏引擎设计的学习参考。
Stars: 0 | Forks: 0
## 作者
| 姓名 | 角色 |
|------|------|
| **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, 编译无警告, 走法验证, 逼和, 面向对象编程