Linux终端实战:从零构建命令行五子棋(双人对决)

张开发
2026/4/20 7:51:19 15 分钟阅读

分享文章

Linux终端实战:从零构建命令行五子棋(双人对决)
1. 为什么要在终端写五子棋第一次在Linux终端里写五子棋时我也觉得这想法挺奇怪的——放着那么多图形界面不用干嘛非要跟黑底白字的命令行较劲但真正动手后才发现这个项目简直是Linux环境编程的完美练手项目。用C语言在终端实现五子棋就像用乐高积木搭城堡。没有现成的图形库依赖所有效果都要靠最基础的printf和字符组合实现。我特别喜欢这种从轮子造起的体验它能让你真正理解如何用二维数组模拟棋盘状态怎样用最简短的代码实现棋盘可视化胜负判断算法如何做到高效准确最让我惊喜的是这个项目天然适合模块化开发。你可以清晰地划分出几个功能模块棋盘显示模块负责把数组变成可视化的点阵用户输入模块处理坐标输入和校验游戏逻辑模块核心的胜负判定算法主循环控制协调各个模块的执行顺序2. 环境准备与项目搭建2.1 基础开发环境在开始编码前建议先准备好这些工具以Ubuntu为例sudo apt update sudo apt install build-essential gdb vim我习惯用vim写代码但你可以用任何喜欢的编辑器。关键是要确保gcc编译器能正常工作可以先用个hello world测试下环境。2.2 项目目录结构创建一个清晰的目录结构会让后续开发轻松很多mkdir -p Gobang/{src,include} touch Gobang/src/{main.c,game.c} Gobang/include/game.h touch Gobang/Makefile这样划分的好处是src目录放.c源文件include目录放头文件Makefile放在项目根目录3. 核心代码实现详解3.1 棋盘数据存储游戏最核心的就是这个15x15的二维数组#define ROW 15 #define COL 15 int board[ROW][COL] {0};我用数字表示棋盘状态0表示空位1表示玩家1黑子2表示玩家2白子初始化时用memset清空棋盘是个好习惯memset(board, 0, sizeof(board));3.2 棋盘可视化技巧终端下显示棋盘需要点小技巧这是我的实现方案void ShowBoard(int board[][COL], int row, int col) { printf(\033[2J\033[H); // 清屏指令 printf( ); for(int j0; jcol; j) printf(%2d , j1); printf(\n); for(int i0; irow; i) { printf(%2d , i1); for(int j0; jcol; j) { if(board[i][j] 0) printf( . ); else if(board[i][j] 1) printf(● ); else printf(○ ); } printf(\n); } }几个关键点使用\033[2J\033[H实现清屏避免重复打印用●和○符号模拟黑白棋子显示行列坐标方便玩家输入3.3 胜负判定算法这是整个项目最有趣的部分。我的实现思路是以最后落子点为中心向8个方向检测是否有5连珠enum Direction { LEFT, RIGHT, UP, DOWN, LEFT_UP, LEFT_DOWN, RIGHT_UP, RIGHT_DOWN }; int CheckLine(int board[][COL], int x, int y, enum Direction dir) { int count 1; int player board[x][y]; for(int i1; i5; i) { int nx x, ny y; switch(dir) { case LEFT: ny - i; break; // 其他7个方向同理... } if(nx0 || nxROW || ny0 || nyCOL) break; if(board[nx][ny] ! player) break; count; } return count 5; }实际使用时需要同时检查四个轴线水平、垂直、两个对角线int IsWin(int board[][COL], int x, int y) { return CheckLine(board,x,y,LEFT) CheckLine(board,x,y,RIGHT) 4 || CheckLine(board,x,y,UP) CheckLine(board,x,y,DOWN) 4 || // 对角线检查... }4. 项目优化与扩展4.1 使用Makefile管理构建一个好的Makefile能极大提升开发效率CC gcc CFLAGS -Iinclude -Wall -g SRC src/main.c src/game.c OBJ $(SRC:.c.o) game: $(OBJ) $(CC) -o $ $^ $(CFLAGS) clean: rm -f $(OBJ) game这样每次修改后只需运行make就能重新编译make clean可以清理构建产物。4.2 添加游戏进度保存想要实现游戏存档功能可以这样扩展void SaveGame(int board[][COL]) { FILE *fp fopen(save.dat, wb); fwrite(board, sizeof(int), ROW*COL, fp); fclose(fp); } void LoadGame(int board[][COL]) { FILE *fp fopen(save.dat, rb); if(fp) { fread(board, sizeof(int), ROW*COL, fp); fclose(fp); } }4.3 人机对战实现思路如果想升级为人机对战可以加入简单的AI逻辑优先防守检查玩家是否有4连珠需要拦截寻找自己的最佳落子点随机选择空位作为兜底策略一个极简的AI示例void AIPlay(int board[][COL], int player) { // 1. 尝试寻找能立即获胜的位置 // 2. 尝试阻止玩家获胜 // 3. 随机选择空位 while(1) { int x rand() % ROW; int y rand() % COL; if(board[x][y] 0) { board[x][y] player; break; } } }5. 常见问题排查5.1 输入坐标异常处理处理用户输入时要考虑各种异常情况void GetInput(int *x, int *y) { while(1) { printf(输入坐标(行 列): ); if(scanf(%d %d, x, y) ! 2) { while(getchar() ! \n); // 清空输入缓冲区 printf(输入格式错误\n); continue; } if(*x1 || *xROW || *y1 || *yCOL) { printf(坐标超出范围\n); continue; } if(board[*x-1][*y-1] ! 0) { printf(该位置已有棋子\n); continue; } break; } }5.2 内存越界问题二维数组操作最容易出现内存越界。建议所有数组访问前先检查边界使用ROW/COL宏代替直接数字开启gcc的-Wall选项显示所有警告5.3 跨平台兼容性问题如果要在Windows下运行需要修改清屏指令改为system(cls)处理文件路径差异注意Windows下换行符是\r\n6. 项目进阶方向完成基础版本后可以考虑这些扩展网络对战用socket实现联机功能图形界面整合ncurses库实现更美观的界面AI强化实现minimax算法或机器学习AI比赛功能加入计时系统、棋谱记录等我最近给项目加了个简单的ELO评分系统可以记录玩家水平变化。实现起来也就百来行代码但让游戏的可玩性提升了不少。

更多文章