公司动态
五子棋项目
Linux FrameBuffer 实现嵌入式五子棋完整源码 解析一、项目概述本项目基于 Linux 帧缓冲FrameBuffer(fb)手写一套极简 2D 绘图引擎不依赖 Qt、LVGL 等第三方 UI 库纯 C 实现 20×20 五子棋。功能清单FrameBuffer 底层封装点、直线、矩形、圆形、区域清屏、24 位 BMP 图片渲染20×20 棋盘绘制WASD 红色光标移动选中落子点空格落子黑白棋子自动交替已有棋子位置禁止重复落子四向胜负判断横、竖、正对角线、反对角线五子连珠弹出胜利图片环境说明系统Linux开发板 / 虚拟机均可显示设备/dev/fb0颜色格式RGB888 (32 位色)、RGB565 (16 位色) 双兼容编译工具GCC Makefile二、项目文件结构├── main.c # 程序入口初始化fb启动游戏 ├── game.c # 五子棋业务逻辑棋盘绘制、操控、胜负判断 ├── game.h # 游戏函数声明头文件 ├── framebuffer.c # FrameBuffer底层绘图驱动封装 ├── framebuffer.h # 绘图API头文件 ├── black.bmp.bmp # 黑棋胜利贴图 ├── white.bmp.bmp # 白棋胜利贴图 └── Makefile # 一键编译脚本三、源码分模块解析模块 1FrameBuffer 底层绘图驱动1.1 framebuffer.h 头文件函数对外声明#ifndef __FRAMEBUFFER_H__ #define __FRAMEBUFFER_H__ #define RGB_FMT_888 32 #define RGB_FMT_565 16 //屏幕全局分辨率 extern int fbx_g; extern int fby_g; //初始化帧缓冲设备 extern int init_fb(char *devname); //基础绘图接口 extern void draw_point(int x, int y, unsigned int col); extern void draw_h_line(int x, int y, int len, unsigned int col); extern void draw_s_line(int x,int y,int len,unsigned int col); extern void clear_ping(int x,int y,int hang,int lie,unsigned int col); extern void draw_square(int x,int y,int len,unsigned int col); extern void draw_circle(int x,int y,int r,unsigned int col); extern void draw_bmp(int x, int y, char *bmpname); #endif1.2 framebuffer.c 底层实现核心原理open打开 fb 设备 →ioctl获取屏幕参数 →mmap显存映射直接操作像素内存。#include stdio.h #include sys/types.h #include sys/stat.h #include fcntl.h #include unistd.h #include stdlib.h #include sys/ioctl.h #include linux/fb.h #include sys/mman.h #include framebuffer.h void *pmem NULL; //显存映射首地址 int fb; //fb设备文件描述符 struct fb_var_screeninfo vinfo; //屏幕可变参数 int fbx_g 0; int fby_g 0; //初始化帧缓冲 int init_fb(char *devname) { fb open(devname, O_RDWR); if (-1 fb) { perror(open fb error); return -1; } //获取屏幕分辨率、色深 int ret ioctl(fb, FBIOGET_VSCREENINFO, vinfo); if (ret 0) { perror(ioctl error); return -1; } printf(xres %d, yres %d\n, vinfo.xres, vinfo.yres); printf(bits_per_pixel %d\n, vinfo.bits_per_pixel); fbx_g vinfo.xres; fby_g vinfo.yres; //计算显存总长度建立内存映射 size_t len vinfo.xres_virtual * vinfo.yres_virtual * vinfo.bits_per_pixel/8; pmem mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fb, 0); if (pmem MAP_FAILED) { perror(mmap error); return -1; } return 0; } //绘制单个像素点适配RGB888/RGB565 void draw_point(int x, int y, unsigned int col) { if (x vinfo.xres || y vinfo.yres) return ; if (vinfo.bits_per_pixel RGB_FMT_888) { unsigned int *p pmem; *(pvinfo.xres_virtual*yx) col; } else if (vinfo.bits_per_pixel RGB_FMT_565) { unsigned short *p pmem; *(pvinfo.xres_virtual*yx) col; } } //绘制水平线 void draw_h_line(int x, int y, int len, unsigned int col) { for (int i x; i xlen; i) draw_point(i, y, col); } //绘制垂直线 void draw_s_line(int x,int y,int len,unsigned int col) { for(int iy;iylen;i) draw_point(x,i,col); } //矩形区域填充清屏/背景 void clear_ping(int x,int y,int hang,int lie,unsigned int col) { for(int ix;ihang;i) for(int jy;jhang;j) draw_point(i,j,col); } //实心正方形 void draw_square(int x,int y,int len,unsigned int col) { for(int ix;ixlen;i) for(int jy;jylen;j) draw_point(i,j,col); } //实心圆形圆方程判定像素 void draw_circle(int x,int y,int r,unsigned int col) { for(int iy-r;iyr;i) for(int jx-r;jxr;j) if((j-x)*(j-x)(i-y)*(i-y)r*r) draw_point(j,i,col); } //加载并绘制24位BMP图片 void draw_bmp(int x, int y, char *bmpname) { #pragma pack(1) //BMP文件头 typedef struct tagBITMAPFILEHEADER { short bfType; int bfSize; short bfReserved1; short bfReserved2; int bfOffBits; }Bmp_file_head_t; //BMP信息头 typedef struct tagBITMAPINFOHEADER { int biSize; int biWidth; int biHeight; short biPlanes; short biBitCount; int biCompression; int biSizeImage; int biXPelsPerMeter; int biYPelsPerMeter; int biClrUsed; int biClrImportant; }Bmp_info_t; #pragma pack() Bmp_file_head_t headinfo; Bmp_info_t bmpinfo; int fd open(bmpname, O_RDONLY); if (-1 fd) { perror(open bmp error); return ; } read(fd, headinfo, sizeof(headinfo)); read(fd, bmpinfo, sizeof(bmpinfo)); unsigned char *buff malloc(bmpinfo.biHeight*bmpinfo.biWidth*bmpinfo.biBitCount/8); read(fd, buff, bmpinfo.biHeight*bmpinfo.biWidth*bmpinfo.biBitCount/8); close(fd); unsigned char *p buff; unsigned char r, g, b; //BMP像素上下倒置逆序遍历 for (int j bmpinfo.biHeight-1; j 0; j--) for (int i 0; i bmpinfo.biWidth; i) { b *p;p; g *p;p; r *p;p; unsigned int col (r 16) | (g 8) | b; draw_point(ix, jy, col); } free(buff); }模块 2五子棋游戏业务层2.1 game.h 函数声明#ifndef _GAME_H #define _GAME_H extern void show_chess_board(); extern void begin_game(); extern int check_win(int x,int y,int col); #endif2.2 game.c 棋盘、操控、胜负逻辑#includestdio.h #includegame.h #includeframebuffer.h //RGB888颜色宏 格式0x00RRGGBB #define RED 0x00FF0000 #define GREEN 0x0000FF00 #define BLUE 0x000000FF #define BLACK 0x00000000 #define WHITE 0x00FFFFFF #define YELLOW 0x00FFFF00 #define BACKGROUND 0x00FFDAB9 //棋盘布局参数 #define BEGIN_X 100 #define BEGIN_Y 100 #define CNT_X 20 #define CNT_Y 20 #define ONE_W 25 #define WHITE_CHESS 1 #define BLACK_CHESS -1 //全局变量 int gb_xBEGIN_X; //光标屏幕坐标X int gb_yBEGIN_Y; //光标屏幕坐标Y int chess_g[CNT_Y][CNT_X]{0}; //棋盘数组 0空 1白 -1黑 int cishu1; //落子次数控制黑白交替 int chess_col0; //本次落子棋子颜色 //绘制完整棋盘背景、网格、光标、所有棋子 void show_chess_board() { clear_ping(0,0,800,600,BACKGROUND); //绘制横线 for(int i0;iCNT_Y;i) draw_h_line(BEGIN_X, BEGIN_Yi*ONE_W, (CNT_X-1)*ONE_W, BLACK); //绘制竖线 for(int i0;iCNT_X;i) draw_s_line(BEGIN_Xi*ONE_W,BEGIN_Y,(CNT_Y-1)*ONE_W,BLACK); //红色光标方块 draw_square(gb_x-5,gb_y-5,10,RED); //遍历棋盘绘制黑白棋子 for(int i0;iCNT_Y;i) for(int j0;jCNT_X;j) { if(WHITE_CHESSchess_g[i][j]) draw_circle(BEGIN_Xj*ONE_W,BEGIN_Yi*ONE_W,10,WHITE); else if(BLACK_CHESSchess_g[i][j]) draw_circle(BEGIN_Xj*ONE_W,BEGIN_Yi*ONE_W,10,BLACK); } } //游戏主循环键盘WASD移动空格落子 void begin_game() { char choose0; while(1) { choosegetchar(); //上W if(wchoose) if(gb_yBEGIN_Y) gb_y-ONE_W; //下S else if(schoose) if(gb_yBEGIN_Y(CNT_Y-1)*ONE_W) gb_yONE_W; //左A else if(achoose) if(gb_xBEGIN_X) gb_x-ONE_W; //右D else if(dchoose) if(gb_xBEGIN_X(CNT_X-1)*ONE_W) gb_xONE_W; //空格落子 else if( choose) { //当前位置已有棋子禁止落子 if(chess_g[(gb_y-BEGIN_Y)/ONE_W][(gb_x-BEGIN_X)/ONE_W]!0) { show_chess_board(); continue; } //奇偶次数切换黑白棋 if(1cishu%2) { chess_g[(gb_y-BEGIN_Y)/ONE_W][(gb_x-BEGIN_X)/ONE_W]WHITE_CHESS; chess_colWHITE_CHESS; } else { chess_g[(gb_y-BEGIN_Y)/ONE_W][(gb_x-BEGIN_X)/ONE_W]BLACK_CHESS; chess_colBLACK_CHESS; } cishu; } //刷新界面 show_chess_board(); //判断胜利显示对应胜利图片 if(check_win((gb_x-BEGIN_Y)/ONE_W,(gb_y-BEGIN_X)/ONE_W,chess_col)) { if(chess_colBLACK_CHESS) draw_bmp(600,200,black.bmp.bmp); else if(chess_colWHITE_CHESS) draw_bmp(600,200,white.bmp.bmp); } } } //胜负检测检测刚落下的棋子四向是否五子连珠 int check_win(int x,int y,int col) { int num1; //1. 水平左右 for(int i1;i5;i) if(colchess_g[y][xi] (xi)CNT_X) num; else break; for(int i1;i5;i) if(colchess_g[y][x-i] (x-i)0) num; else break; if(num5) return 1; //2. 垂直上下 num1; for(int i1;i5;i) if(colchess_g[yi][x] (yi)CNT_Y) num; else break; for(int i1;i5;i) if(colchess_g[y-i][x] (y-i)0) num; else break; if(num5) return 1; //3. 正对角线 \ num1; for(int i1;i5;i) if(colchess_g[yi][xi] (yi)CNT_Y (xi)CNT_X) num; else break; for(int i1;i5;i) if(colchess_g[y-i][x-i] (y-i)0 (x-i)0) num; else break; if(num5) return 1; //4. 反对角线 / num1; for(int i1;i5;i) if(colchess_g[yi][x-i] (yi)CNT_Y (x-i)0) num; else break; for(int i1;i5;i) if(colchess_g[y-i][xi] (y-i)0 (xi)CNT_X) num; else break; if(num5) return 1; return 0; }模块 3程序入口 main.c#include stdio.h #include framebuffer.h #includegame.h int main(void) { init_fb(/dev/fb0); show_chess_board(); begin_game(); return 0; }模块 4Makefile 编译脚本CC gcc TARGET gobang SRCS main.c game.c framebuffer.c OBJS $(SRCS:.c.o) CFLAGS -Wall -g $(TARGET): $(OBJS) $(CC) $(OBJS) -o $(TARGET) %.o: %.c $(CC) $(CFLAGS) -c $ -o $ clean: rm -rf $(OBJS) $(TARGET) .PHONY: clean四、关键知识点讲解4.1 FrameBuffer 核心原理/dev/fb0是 Linux 显卡抽象设备操作文件等价操作屏幕显存mmap将内核显存映射到用户进程虚拟内存直接写内存即可改像素无系统调用开销RGB88832 位无符号整型存储颜色0x00RRGGBB单通道 0~255千万级色彩RGB56516 位低成本色深R5 G6 B5嵌入式老屏幕常用。4.2 BMP 图片渲染要点BMP 像素存储上下颠倒绘图需要从最后一行往第一行遍历BMP 原生像素顺序为 BGR代码内手动转换成 RGB 格式写入显存使用#pragma pack(1)取消结构体内存对齐防止读取文件头错位。4.3 五子棋胜负算法落子后只检测当前棋子四个方向 水平、垂直、左上 - 右下、右上 - 左下 每个方向分别向两侧统计连续同色棋子总数≥5 判定胜利。五、操作说明W光标上移S光标下移A光标左移D光标右移空格落子六、代码缺陷与优化方向全屏重绘每次按键清空整个屏幕刷新闪烁可增加局部刷新 / 双缓冲无触摸、鼠标支持仅键盘操控无文字绘制接口胜利仅贴图无文字提示无游戏重启逻辑分出胜负后无法新开一局clear_ping参数变量名混乱存在语义 bug未封装通用窗口、按钮等 UI 控件仅适用于单棋盘场景。七、适用场景Linux 嵌入式 FrameBuffer 底层显示驱动学习手写 2D 简易 UI 绘图引擎入门C 语言文件 IO、内存映射、位图解析实战五子棋游戏逻辑、五子连珠算法练习。