手把手教你用C++和STL写一个命令行象棋对战程序(附完整可运行代码)
从零构建C++命令行象棋:STL实战与设计模式精解
1. 项目架构设计
象棋程序的核心在于棋盘表示与棋子行为建模。我们采用面向对象设计,将棋盘抽象为ChessBoard类,棋子抽象为ChessPiece基类及其派生类。这种设计符合开闭原则,新增棋子类型无需修改现有代码。
关键数据结构选择:
- 使用
std::array<std::array<ChessPiece*, 9>, 10>表示10行9列的棋盘 - 红黑双方棋子分别用
std::list<ChessPiece*>存储,便于遍历和删除 - 坐标系统采用
Point结构体封装(x,y)位置
class ChessBoard { private: std::array<std::array<ChessPiece*, 9>, 10> board; std::list<ChessPiece*> redPieces; std::list<ChessPiece*> blackPieces; public: // 接口方法... };2. 棋子移动算法实现
每种棋子通过重写CanMoveTo虚函数实现特有移动规则。我们采用策略模式,将不同棋子的移动规则封装在各自的类中。
2.1 车(Rook)的直线移动
bool Rook::CanMoveTo(const Point& dest) const { if (dest.x != current.x && dest.y != current.y) return false; // 检查路径上是否有阻挡 if (dest.x == current.x) { int step = dest.y > current.y ? 1 : -1; for (int y = current.y + step; y != dest.y; y += step) { if (board.GetPiece({current.x, y})) return false; } } else { int step = dest.x > current.x ? 1 : -1; for (int x = current.x + step; x != dest.x; x += step) { if (board.GetPiece({x, current.y})) return false; } } return true; }2.2 马(Horse)的日字移动
马走日需考虑蹩马腿:
bool Horse::CanMoveTo(const Point& dest) const { int dx = abs(dest.x - current.x); int dy = abs(dest.y - current.y); if (!((dx == 1 && dy == 2) || (dx == 2 && dy == 1))) return false; // 检查马腿位置 Point blockPoint = dx == 2 ? Point{(current.x + dest.x)/2, current.y} : Point{current.x, (current.y + dest.y)/2}; return !board.GetPiece(blockPoint); }3. 游戏状态管理
ChessGame类负责管理游戏流程,实现以下核心功能:
- 回合制切换:使用布尔变量
isRedTurn记录当前回合 - 胜负判定:
- 将帅照面直接判胜
- 一方将/帅被吃判负
- 特殊规则处理:
- 兵过河后才能横向移动
- 象不能过河
- 士不出九宫
class ChessGame { public: bool Move(const Point& from, const Point& to) { ChessPiece* piece = board.GetPiece(from); if (!piece || piece->IsRed() != isRedTurn) return false; if (piece->CanMoveTo(to)) { // 执行移动逻辑... isRedTurn = !isRedTurn; return true; } return false; } GameState CheckGameState() const { // 胜负判定逻辑... } private: ChessBoard board; bool isRedTurn = true; // 红方先行 };4. 命令行交互实现
为提升用户体验,我们实现以下交互功能:
- 彩色棋盘显示:使用Windows API设置控制台颜色
- 输入验证:确保坐标在0-8和0-9范围内
- 游戏状态提示:显示当前回合和获胜信息
void PrintBoard(const ChessBoard& board) { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); for (int y = 0; y < 10; ++y) { for (int x = 0; x < 9; ++x) { ChessPiece* piece = board.GetPiece({x, y}); if (piece) { SetConsoleTextAttribute(hConsole, piece->IsRed() ? FOREGROUND_RED : FOREGROUND_BLUE); std::cout << piece->GetName(); } else { std::cout << "+"; } } std::cout << std::endl; } SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); }5. 内存管理与异常处理
智能指针应用:
class ChessBoard { private: std::array<std::array<std::unique_ptr<ChessPiece>, 9>, 10> board; std::list<std::unique_ptr<ChessPiece>> redPieces; std::list<std::unique_ptr<ChessPiece>> blackPieces; };移动语义优化:
ChessBoard::ChessBoard(ChessBoard&& other) noexcept : board(std::move(other.board)), redPieces(std::move(other.redPieces)), blackPieces(std::move(other.blackPieces)) {}6. 单元测试策略
为验证各棋子移动规则,我们使用Google Test框架:
TEST(RookTest, StraightMove) { ChessBoard board; Rook rook({4,4}, true, board); EXPECT_TRUE(rook.CanMoveTo({4,8})); // 垂直移动 EXPECT_TRUE(rook.CanMoveTo({8,4})); // 水平移动 EXPECT_FALSE(rook.CanMoveTo({5,5})); // 斜线移动 } TEST(HorseTest, BlockedMove) { ChessBoard board; Horse horse({2,2}, true, board); Rook blocker({2,3}, false, board); // 蹩马腿 EXPECT_FALSE(horse.CanMoveTo({1,4})); }7. 性能优化技巧
- 空间换时间:使用二维数组直接访问棋子,O(1)时间复杂度
- 延迟计算:只在需要时检查将帅照面
- 位运算优化:使用位掩码存储棋子属性
enum PieceAttributes { CAN_CROSS_RIVER = 1 << 0, CAN_CAPTURE = 1 << 1, // ... }; class ChessPiece { protected: uint8_t attributes; public: bool CanCrossRiver() const { return attributes & CAN_CROSS_RIVER; } };8. 扩展性设计
通过继承体系可轻松支持新棋子类型:
class NewChessPiece : public ChessPiece { public: NewChessPiece(const Point& pos, bool isRed, ChessBoard& board) : ChessPiece(pos, isRed, board) {} bool CanMoveTo(const Point& dest) const override { // 实现特定移动规则 } };9. 跨平台考虑
使用条件编译实现跨平台支持:
#ifdef _WIN32 #include <windows.h> void SetConsoleColor(int color) { /* Windows实现 */ } #else void SetConsoleColor(int color) { /* Linux实现 */ } #endif10. 完整项目结构
最终项目包含以下文件:
ChineseChess/ ├── Chess.h # 类声明和常量定义 ├── Chess.cpp # 类方法实现 ├── main.cpp # 主程序入口 └── tests/ # 单元测试在实现过程中,特别要注意象棋规则的各种边界情况,如:
- 兵到底线后的处理
- 将帅不能主动送吃
- 长将判负规则
- 重复局面处理
通过这个项目,开发者不仅能掌握C++面向对象编程和STL应用,还能深入理解游戏逻辑的实现原理。
