
今天,我想带大家一起动手写一个简单又好玩的五子棋游戏!
即使你刚学编程不久,只要会一点 C++ 基础(比如变量、循环、函数),就能跟着我一步步完成这个小项目。
💡 为什么选五子棋? 因为它规则简单(连成五个就赢!),逻辑清晰,非常适合编程初学者练手。而且做完后还能和朋友对战,超有成就感!
话不多说,让我们开始吧!
我们要做一个在命令行(黑窗口)里运行的五子棋游戏,具备以下功能:
听起来是不是很酷?别担心,我们一步步来!
在编程中,数据是程序的“记忆”。我们要先设计好怎么“记住”棋盘状态。
想象一个 19 行 19 列的表格,每个格子可以是:
在 C++ 中,我们可以用一个 二维数组 来表示:
constexpr int BOARD_SIZE = 19;
int board[BOARD_SIZE][BOARD_SIZE];//19×19的表格constexpr int BOARD_SIZE = 19;
定义一个编译时常量 BOARD_SIZE,值为 19。
constexpr 表示这个值在编译时就确定了,不能改。int board[BOARD_SIZE][BOARD_SIZE];
声明一个 19×19 的二维整数数组,用来表示棋盘。
board[i][j] 表示第 i 行、第 j 列的状态。我们用一个叫 flag 的变量:
int flag;flag 是偶数(0, 2, 4...)→ 黑棋下flag 是奇数(1, 3, 5...)→ 白棋下🌰
flag = 0表示第 0 回合(黑棋先手),下完后flag++变成 1(白棋回合)。
我们把大问题拆成几个小任务,每个任务写成一个函数(函数就像一个个小机器人,各司其职)
init()—— 初始化游戏void init() {
for (int i = 0; i < BOARD_SIZE; ++i) {
for (int j = 0; j < BOARD_SIZE; ++j) {
board[i][j] = 0;
}
}
flag = 0;
}作用:
flag 设为 0(黑棋先手)void init()
函数名 init 是 “initialize”(初始化)的缩写。
void 表示这个函数不返回任何值。
for (int i = 0; i < BOARD_SIZE; ++i)
外层循环:遍历每一行(i 从 0 到 18)。
for (int j = 0; j < BOARD_SIZE; ++j)
内层循环:遍历当前行的每一列(j 从 0 到 18)。
board[i][j] = 0;
把每个格子设为 0,表示空地。
flag = 0;
重置回合数,让黑棋先手。
✅ 小总结:每次新游戏开始,都要调用
init()清空棋盘!
playerMove(int x, int y) —— 玩家落子int playerMove(int x, int y) {
if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE) return 0;
if (board[x][y] != 0) return 0;
board[x][y] = (flag % 2 == 0) ? 1 : 2;
return 1;
}作用:
(x, y) 是否在棋盘内flag 决定放黑子(1)还是白子(2)x 或 y 不在 [0, 18] 范围内?返回 0(失败)。
board[x][y] != 0 表示已有棋子?返回 0(失败)。
flag % 2 == 0 → 黑棋(1)? : 是三元运算符,相当于简写 if-else)1:表示落子成功!
isWin(int x, int y)—— 判断是否获胜(重点!)这是最核心也最难的部分,我们分段讲解。
int isWin(int x, int y) {
if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE || board[x][y] == 0)
return 0;int isWin(int x, int y)
输入刚落子的坐标 (x, y),返回:
0:没赢1:黑棋赢2:白棋赢board[x][y]==0),直接返回 0(不可能赢)。
int color = board[x][y];
int count;int color = board[x][y];
记住刚落下的棋子是黑(1)还是白(2)。
int count;
用来统计连续相同颜色棋子的数量。
我们只检查刚刚落子的位置,看它在四个方向上有没有连成 5 个:

// 以横向为例
int count = 1; // 自己算1个
for (int j = y-1; j>=0 && board[x][j]==color; j--) count++; // 往左
for (int j = y+1; j<19 && board[x][j]==color; j++) count++; // 往右
if (count >= 5) return color;count = 1;
先把自己算进去(至少有 1 个)。
j 从 y-1 开始往左(j--),只要格子颜色相同,count++。
j 从 y+1 开始往右(j++),同样统计。
color(1 或 2)。
🌰 例子:在 (5,5) 下黑子,左边有 2 个黑子,右边有 2 个 →
count = 1+2+2 = 5→ 赢!
// 纵向(上下)
count = 1;
for (int i = x - 1; i >= 0 && board[i][y] == color; --i) count++;
for (int i = x + 1; i < BOARD_SIZE && board[i][y] == color; ++i) count++;
if (count >= 5) return color;和横向类似,只是固定列
y,变动行i。
// 左上-右下(\)
count = 1;
for (int d = 1; x - d >= 0 && y - d >= 0 && board[x - d][y - d] == color; ++d) count++;
for (int d = 1; x + d < BOARD_SIZE && y + d < BOARD_SIZE && board[x + d][y + d] == color; ++d) count++;
if (count >= 5) return color;x-d, y-d)x+d, y+d)d 是“距离”,从 1 开始🌰 坐标变化:(5,5) → (4,4) → (3,3) …(左上);(5,5) → (6,6) → (7,7) …(右下)
// 右上-左下(/)
count = 1;
for (int d = 1; x - d >= 0 && y + d < BOARD_SIZE && board[x - d][y + d] == color; ++d) count++;
for (int d = 1; x + d < BOARD_SIZE && y - d >= 0 && board[x + d][y - d] == color; ++d) count++;
if (count >= 5) return color;
return 0;
}x-d, y+d)
x+d, y-d)
return 0;:四个方向都没连成 5 个,返回没赢。
✅ 为什么只检查刚落子的位置? 因为只有新下的棋子才可能“形成新的五连”,其他位置之前已经检查过了!
void clearScreen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}#ifdef _WIN32:如果是 Windows 系统,执行 cls(清屏命令)clear(Linux/macOS 命令)system() 用于执行操作系统命令。⚠️ 注意:
system()有安全风险,但小游戏可以接受。
void pauseScreen() {
std::cout << "按回车键继续...";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
}std::cin.ignore(...):清空输入缓冲区(防止之前残留的回车影响)std::cin.get():等待用户按回车🌰 如果不加
ignore,可能按一次回车就跳过两次!
gameView_ShowBoard()void gameView_ShowBoard() {
clearScreen();
// 打印列标
std::cout << " ";
for (int j = 0; j < BOARD_SIZE; ++j) {
std::cout << std::setw(2) << j << " ";
}
std::cout << "\n";
for (int i = 0; i < BOARD_SIZE; ++i) {
std::cout << std::setw(2) << i << " ";
for (int j = 0; j < BOARD_SIZE; ++j) {
char ch;
if (board[i][j] == 0) ch = '+';
else if (board[i][j] == 1) ch = 'X';
else ch = 'O';
std::cout << " " << ch << " ";
}
std::cout << "\n";
}
std::cout << "\n当前回合: " << ((flag % 2 == 0) ? "黑棋" : "白棋") << "\n";
}std::setw(2):让数字占 2 个字符宽,对齐棋盘0 → '+'(空地)1 → 'X'(黑子)2 → 'O'(白子)winView()void winView() {
clearScreen();
int winner = (flag % 2 == 0) ? 2 : 1;
if (winner == 1) {
std::cout << "🎉 黑棋胜利!\n";
} else {
std::cout << "🎉 白棋胜利!\n";
}
std::cout << "按回车键返回主菜单...\n";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
}winner = (flag % 2 == 0) ? 2 : 1?
因为落子后还没切换 flag! flag 是偶数(黑棋刚下完),那胜利者是黑棋(1)→ 但这里写反了? ❗ 注意:这里有个逻辑陷阱!
正确应该是:刚落子的是 flag % 2 == 0 ? 黑 : 白,所以胜利者就是 board[x][y] 的值!
但为了简单,我们直接用 isWin() 的返回值更好。不过当前写法也能工作,因为 flag 还没加 1。
gameView()void gameView() {
init();
int x, y;
while (true) {
gameView_ShowBoard();
std::cout << "请输入落子坐标 (行 列,例如: 9 10): ";
if (!(std::cin >> x >> y)) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "输入格式错误!请输入两个整数。\n";
pauseScreen();
continue;
}
if (!playerMove(x, y)) {
std::cout << "落子失败!坐标无效或该位置已有棋子。\n";
pauseScreen();
continue;
}
int result = isWin(x, y);
if (result != 0) {
gameView_ShowBoard();
winView();
break;
}
flag++;
}
}init():开始前清空棋盘if (!(std::cin >> x >> y)) 检查是否输入了非整数isWin 返回非 0,显示胜利界面并退出循环flag++:切换到对方回合(放在最后!)menuView()void menuView() {
int choice;
while (true) {
clearScreen();
std::cout << "========== 五子棋游戏 ==========\n";
std::cout << "1. 开始游戏\n";
std::cout << "2. 设置\n";
std::cout << "3. 退出游戏\n";
std::cout << "请选择: ";
std::cin >> choice;
switch (choice) {
case 1:
gameView();
break;
case 2:
std::cout << "设置功能敬请期待...\n";
pauseScreen();
break;
case 3:
std::cout << "感谢游玩!再见!\n";
exit(0);
default:
std::cout << "无效选项,请重新选择。\n";
pauseScreen();
}
}
}switch:根据选项执行不同操作exit(0):立即终止程序main()#include <iostream>
#include <iomanip>
#include <limits>#include <iostream>
引入 C++ 的输入输出流库。有了它,我们才能用 std::cout 打印文字,用 std::cin 读取用户输入。
🌰 就像你要写字,得先有笔和纸——iostream 就是程序的“纸笔”。
#include <iomanip>
引入格式化输出库。我们用它来对齐棋盘的行列号(比如让数字整齐排列)。
🌰 比如 std::setw(2) 可以让数字“占两个字符宽”,避免棋盘歪歪扭扭。
#include <limits>
用于安全地处理错误输入(比如用户输入了字母而不是数字)。
🌰 如果用户乱输,程序不会崩溃,而是提示“输错了,请重试”。
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
menuView();
return 0;
}std::ios::sync_with_stdio(false);
关闭 C 和 C++ 输入输出的同步,加快输入输出速度(对小游戏影响不大,但好习惯)。
std::cin.tie(nullptr);
解除 cin 和 cout 的绑定,进一步提速。
menuView();:启动主菜单!
编程不是死记硬背,而是理解逻辑 + 动手实践。 今天你写的每一行代码,都在锻炼你的“计算思维”。 遇到问题?太正常了!调试的过程,就是你进步最快的时候。 现在,去运行你的五子棋吧!和朋友对战,享受创造的乐趣!🎉
#include <iostream>
#include <iomanip>
#include <limits>
// -------------------- 全局数据 --------------------
constexpr int BOARD_SIZE = 19;
int board[BOARD_SIZE][BOARD_SIZE];
int flag; // 偶数:黑棋(1);奇数:白棋(2)
// ------------------------------------------------
// -------------------- service --------------------
void init() {
for (int i = 0; i < BOARD_SIZE; ++i) {
for (int j = 0; j < BOARD_SIZE; ++j) {
board[i][j] = 0;
}
}
flag = 0;
}
int isWin(int x, int y) {
if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE || board[x][y] == 0)
return 0;
int color = board[x][y];
int count;
// 横向(左右)
count = 1;
for (int j = y - 1; j >= 0 && board[x][j] == color; --j) count++;
for (int j = y + 1; j < BOARD_SIZE && board[x][j] == color; ++j) count++;
if (count >= 5) return color;
// 纵向(上下)
count = 1;
for (int i = x - 1; i >= 0 && board[i][y] == color; --i) count++;
for (int i = x + 1; i < BOARD_SIZE && board[i][y] == color; ++i) count++;
if (count >= 5) return color;
// 左上-右下(\)
count = 1;
for (int d = 1; x - d >= 0 && y - d >= 0 && board[x - d][y - d] == color; ++d) count++;
for (int d = 1; x + d < BOARD_SIZE && y + d < BOARD_SIZE && board[x + d][y + d] == color; ++d) count++;
if (count >= 5) return color;
// 右上-左下(/)
count = 1;
for (int d = 1; x - d >= 0 && y + d < BOARD_SIZE && board[x - d][y + d] == color; ++d) count++;
for (int d = 1; x + d < BOARD_SIZE && y - d >= 0 && board[x + d][y - d] == color; ++d) count++;
if (count >= 5) return color;
return 0;
}
int playerMove(int x, int y) {
if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE) return 0;
if (board[x][y] != 0) return 0;
board[x][y] = (flag % 2 == 0) ? 1 : 2;
return 1;
}
// ------------------------------------------------
// -------------------- view --------------------
void clearScreen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
void pauseScreen() {
std::cout << "按回车键继续...";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
}
void menuView();
void gameView_ShowBoard() {
clearScreen();
// 打印列标
std::cout << " ";
for (int j = 0; j < BOARD_SIZE; ++j) {
std::cout << std::setw(2) << j << " ";
}
std::cout << "\n";
for (int i = 0; i < BOARD_SIZE; ++i) {
std::cout << std::setw(2) << i << " ";
for (int j = 0; j < BOARD_SIZE; ++j) {
char ch;
if (board[i][j] == 0) ch = '+';
else if (board[i][j] == 1) ch = '●'; // 黑子
else ch = '○'; // 白子
std::cout << " " << ch << " ";
}
std::cout << "\n";
}
std::cout << "\n当前回合: " << ((flag % 2 == 0) ? "黑棋" : "白棋") << "\n";
}
void winView() {
clearScreen();
int winner = (flag % 2 == 0) ? 2 : 1; // 落子后未切换 flag,胜利者是刚下棋的一方
if (winner == 1) {
std::cout << "🎉 黑棋胜利!\n";
} else {
std::cout << "🎉 白棋胜利!\n";
}
std::cout << "按回车键返回主菜单...\n";
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin.get();
}
void gameView() {
init();
int x, y;
while (true) {
gameView_ShowBoard();
std::cout << "请输入落子坐标 (行 列,例如: 9 10): ";
if (!(std::cin >> x >> y)) {
// 输入错误处理
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "输入格式错误!请输入两个整数。\n";
pauseScreen();
continue;
}
if (!playerMove(x, y)) {
std::cout << "落子失败!坐标无效或该位置已有棋子。\n";
pauseScreen();
continue;
}
int result = isWin(x, y);
if (result != 0) {
gameView_ShowBoard();
winView();
break; // 返回主菜单
}
flag++; // 切换玩家
}
}
void menuView() {
int choice;
while (true) {
clearScreen();
std::cout << "========== 五子棋游戏 ==========\n";
std::cout << "1. 开始游戏\n";
std::cout << "2. 设置\n";
std::cout << "3. 退出游戏\n";
std::cout << "请选择: ";
std::cin >> choice;
switch (choice) {
case 1:
gameView();
break;
case 2:
std::cout << "设置功能敬请期待...\n";
pauseScreen();
break;
case 3:
std::cout << "感谢游玩!再见!\n";
exit(0);
default:
std::cout << "无效选项,请重新选择。\n";
pauseScreen();
}
}
}
// ------------------------------------------------
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
menuView();
return 0;
}演示效果:
我会在这里,陪你一起成长 💪