尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于 linux framebuffer 实现五子棋小游戏

基于 linux framebuffer 实现五子棋小游戏 项目简介五子棋作为经典双人博弈游戏本项目基于嵌入式Linux FrameBuffer帧缓冲设备使用纯C语言开发不依赖QT、GTK等第三方图形库直接对 /dev/fb0 进行内存映射操作显存完成像素级画面渲染。实现可视化棋盘绘制、方向键光标移动、交替落子、四向胜负判定、BMP图片弹窗结算完整游戏流程适合作为Linux嵌入式入门练手项目。传统的五子棋大多运行在桌面GUI或者命令行字符界面本项目直接操作底层显示设备在无桌面的嵌入式终端上运行占用CPU、内存资源很低完整感受Linux底层图形显示原理同时练习C语言数组、循环算法、Makefile工程编译等开发技能。开发环境- 系统嵌入式Linux虚拟机Linux也可运行- 编程语言C语言- 图形接口FrameBuffer /dev/fb0- 编译工具gcc Makefile- 输入交互终端键盘输入整体功能1. 棋盘绘制渲染绘制网格棋盘把二维数组的数据同步渲染到屏幕上黑白棋子实时刷新。2. 双人交替落子方向键控制光标移动定位落子点空格键确认落子黑白棋手回合自动切换禁止重复在同一位置落子。3. 胜负算法判定落子之后以最新落下的棋子为原点向横、竖、两条斜线四个方向统计连续同色棋子连续≥5颗即判定获胜。4. 游戏结算对局胜利后加载24位BMP图片弹窗展示胜利结果支持棋盘重置开启新一轮对局。核心实现思路1. 棋盘数据模型使用二维数组 int chess_g[15][15] 本项目使用15×15标准五子棋棋盘作为游戏的数据底层数据与显示分离。c0 → 空位1 → 白棋-1 → 黑棋屏幕上看到的棋盘画面全部由这个二维数组生成修改落子的时候只修改数组数值再调用绘图函数刷新屏幕。做到逻辑层和渲染层解耦。2. FrameBuffer底层绘图打开帧缓冲设备文件 /dev/fb0 通过 mmap 把显存映射到进程虚拟地址空间直接操作内存地址修改屏幕像素。封装了一系列绘图工具函数画直线、画实心矩形、画圆、清屏、绘制BMP图片。画圆函数用到 sin() 、 cos() 数学库编译链接时必须增加 ‑lm 链接数学库否则会出现未定义引用报错。3. 光标交互逻辑游戏主循环持续读取键盘输入。上下左右方向键修改光标逻辑坐标再换算成屏幕像素坐标绘制红色光标框标记落子位置按下空格执行落子逻辑。落子前做合法性校验不能落在已有棋子的位置不能超出棋盘边界。4. 胜负判断核心算法check_win优化点不需要遍历整个棋盘只检查刚刚落下的那一颗棋子效率更高。定义方向数组四组方向代表水平、竖直、两条斜线cint dir[4][2] {{0,1},{1,0},{1,1},{1,-1}};拿到刚落子的坐标 row、col 对每一组方向1. 向正方向循环统计连续同色棋子2. 再向反方向循环统计连续同色棋子3. 总数量 正向计数 反向计数 1棋子自身4. 如果计数≥5直接返回当前棋子颜色代表胜利全部方向遍历完毕没有满足条件则返回0继续游戏。这种四向辐射的算法避免扫描整张棋盘每一次落子后只做局部检索时间性能优秀。5. Makefile工程管理项目拆分多个源文件- main.c 程序入口- game.c 游戏主逻辑、光标、落子、游戏循环- check_win.c 五子棋胜负判定函数- framebuffer.c FrameBuffer初始化、所有绘图工具函数- game.h 宏定义、全局变量、函数声明头文件手写Makefile统一管理编译一次性编译全部 .c 文件链接数学库 ‑lm 支持 make clean 清理编译产物替代冗长的gcc手动编译命令练习小型C项目工程化。makefileOBJ a.outSRC main.c game.c check_win.c framebuffer.cCC gcc$(OBJ): $(SRC)$(CC) $(SRC) -o $(OBJ) -lmclean:rm -rf $(OBJ) *.o核心实现思路 关键代码示例1. 棋盘数据模型采用15×15二维数组作为游戏底层数据数据与显示分离修改数组再调用绘图函数刷新屏幕。c// game.h#define CHESS_EMPTY 0 //空位#define CHESS_WHITE 1 //白棋#define CHESS_BLACK -1 //黑棋#define BOARD_SIZE 15extern int chess_board[BOARD_SIZE][BOARD_SIZE];extern int turn; //当前下棋棋手extern int game_over; //游戏是否结束标记c// game.c 初始化棋盘void init_board(void){for(int i 0; i BOARD_SIZE; i){for(int j 0; j BOARD_SIZE; j){chess_board[i][j] CHESS_EMPTY;}}turn CHESS_WHITE; //开局白棋先行game_over 0;}2. FrameBuffer底层绘图framebuffer.c打开帧缓冲设备 mmap 映射显存直接操作内存修改屏幕像素封装基础绘图API。c#include stdio.h#include fcntl.h#include unistd.h#include sys/mman.h#include linux/fb.h#include sys/ioctl.h#include math.h#include game.hint fd_fb;struct fb_var_screeninfo vinfo;unsigned int *fb_mem;//初始化帧缓冲int init_fb(void){fd_fb open(/dev/fb0, O_RDWR);ioctl(fd_fb, FBIOGET_VSCREENINFO, vinfo);int screen_size vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel / 8;fb_mem mmap(NULL, screen_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_fb, 0);return 0;}//画一个像素点void draw_point(int x, int y, unsigned int color){if(x 0 || x vinfo.xres || y 0 || y vinfo.yres)return;fb_mem[y * vinfo.xres_virtual x] color;}//画水平线void draw_h_line(int x0, int y0, int len, unsigned int color){for(int i 0; i len; i){draw_point(x0 i, y0, color);}}//画圆绘制棋子用到sin、cos数学函数void draw_circle(int x0, int y0, int r, unsigned int color){for(int angle 0; angle 360; angle){double rad angle * 3.1415926 / 180;int x x0 r * cos(rad);int y y0 r * sin(rad);draw_point(x, y, color);}}⚠️注意 sin() 、 cos() 需要链接数学库Makefile需要增加 ‑lm 参数。3. 棋盘刷新函数示例读取二维数组循环绘制棋盘网格以及黑白棋子。cvoid show_chess_board(void){draw_clear(0x00D2B48C); //棋盘底色浅棕褐色//绘制棋盘网格线for(int i 0; i BOARD_SIZE; i){draw_h_line(40, 40 i*40, 14*40, 0x00000000);draw_v_line(40 i*40,40,14*40, 0x00000000);}//遍历数组绘制棋子for(int row 0; row BOARD_SIZE; row){for(int col 0; col BOARD_SIZE; col){int px_x 40 col *40;int px_y 40 row *40;if(chess_board[row][col] CHESS_WHITE){draw_circle(px_x, px_y,15, 0x00FFFFFF);}else if(chess_board[row][col] CHESS_BLACK){draw_circle(px_x, px_y,15, 0x00000000);}}}}4. 落子逻辑代码示例c//row col棋盘逻辑坐标int place_chess(int row, int col){//边界判断if(row 0 || row BOARD_SIZE || col 0 || col BOARD_SIZE)return -1;//该位置已经有棋子if(chess_board[row][col] ! CHESS_EMPTY)return -1;//落子chess_board[row][col] turn;return 0;}5. 胜负判定核心代码 check_win.c只检测刚刚落下的棋子向四个方向辐射统计不需要遍历整张棋盘效率更高。c#include game.hint check_win(int row, int col){int color chess_board[row][col];if(color CHESS_EMPTY)return 0;//4组方向水平、竖直、斜1、斜2int dir[4][2] {{0,1}, {1,0}, {1,1}, {1,-1}};for(int i 0; i 4; i){int dx dir[i][0];int dy dir[i][1];int cnt 1; //自己这颗棋子初始计数为1//正向延伸int r row dx;int c col dy;while(r 0 r BOARD_SIZE c 0 c BOARD_SIZE chess_board[r][c]color){cnt;r dx;c dy;}//反向延伸r row - dx;c col - dy;while(r 0 r BOARD_SIZE c 0 c BOARD_SIZE chess_board[r][c]color){cnt;r - dx;c - dy;}if(cnt 5){return color; //返回获胜棋子颜色}}return 0; //没有胜利}6. 游戏主循环片段 game.ccvoid begin_game(void){int cur_row 7;int cur_col 7;init_board();show_chess_board();while(!game_over){//绘制光标标记落子位置draw_cursor(cur_row, cur_col);int key getchar();switch(key){case w: cur_row--; break;case s: cur_row; break;case a: cur_col--; break;case d: cur_col; break;case :if(place_chess(cur_row,cur_col) 0){//落子成功判断胜负int ret check_win(cur_row, cur_col);if(ret ! 0){game_over 1;if(ret CHESS_WHITE){draw_bmp(win_white.bmp);}else{draw_bmp(win_black.bmp);}}//切换棋手turn -turn;show_chess_board();}break;}//坐标限制防止越界if(cur_row 0) cur_row 0;if(cur_row BOARD_SIZE) cur_row BOARD_SIZE-1;if(cur_col 0) cur_col 0;if(cur_col BOARD_SIZE) cur_col BOARD_SIZE-1;}}开发踩坑记录1. sin、cos未定义引用math.h 只是提供函数声明编译链接阶段需要追加 ‑lm 参数写在Makefile链接行末尾写在编译 .o 阶段不会生效。2. 胜负判定失效、输赢弹窗黑白颠倒- 计数变量 cnt 初始值要设置为1算上自己这颗棋子初始写0永远达不到五子。- 宏定义黑白棋数值不要写反落子、判赢、切换棋手的顺序不能颠倒正确顺序落子 → 判断胜利 → 切换棋手回合顺序错乱会出现棋手乱跳。- 数组越界while循环内必须增加棋盘边界判断访问超出数组下标会造成内存乱读胜负计数异常。3. undefined reference 未定义的函数引用头文件 #include 只是告诉编译器函数声明如果 .c 源文件没有加到Makefile的SRC编译列表链接器找不到函数实体直接报错。所有用到的c文件都需要参与编译。4. 坐标换算出错棋盘是逻辑坐标0‑14屏幕是像素坐标两套坐标要做好转换很容易出现光标错位、棋子画错位置。5. Makefile语法报错 missing separator规则下面执行命令的行开头必须是Tab不能用空格代替这是Makefile硬性语法。6. BMP图片显示异常必须使用24位真彩色BMP资源图片格式不对会花屏、黑屏图片路径写错无法加载胜利弹窗。项目收获1. 理解Linux FrameBuffer帧缓冲原理学会打开设备、mmap显存映射脱离GUI图形库直接操作屏幕像素搞懂嵌入式底层显示流程。2. 学会用二维数组抽象现实模型把游戏棋盘转化程序数据做到业务逻辑与界面渲染分离的编程思想。3. 练习算法实现把五子棋“五子连珠”的业务规则转化成可执行代码方向向量简化多方向遍历减少重复代码。4. C语言工程化实践项目拆分多 .c 多头文件头文件声明、源文件实现熟练使用Makefile管理多文件项目编译。5. 调试排错能力遇到链接报错、逻辑bug使用打印变量值定位断点解决数组越界、时序逻辑错误等典型问题。后续可优化方向1. 增加简单AI人机对战模式代替双人对战。2. 增加悔棋功能使用栈保存历史落子记录。3. 增加棋盘满盘平局判断。4. 优化输入替换阻塞的getchar实现非阻塞键盘读取。5. 界面美化增加棋盘背景、棋子抗锯齿效果。这个项目非常适合嵌入式Linux入门不追求炫酷界面重在理解底层设备操作、C语言工程组织、简单游戏算法是把课本C语言知识落地的小型综合实战。
返回列表