基本的五子棋游戏,有UI界面,能悔棋,需要预装驱动和文件才能运行。
设计思路流程图
以下是代码
main.c
#include <stdio.h>
#include "bmp.h"
#include "game.h"
#include "lcd.h"
#include <stdlib.h> // abs 的头文件
int main()
{
//屏幕初始化
unsigned int *plcd = lcd_init();
game:
//开始操作屏幕
Display_homepage("gobang.bmp",plcd); //封面图片
//播放背景音乐
system("killall -KIll madplay");
system("madplay -Q 2.mp3 -a -20 &");
//
Game_play( plcd);
goto game;
//关闭屏幕
lcd_end( plcd);
return 0 ;
}
lcd.c 操作屏幕函数的封装
#include <stdio.h>
#include <sys/types.h> // open 头文件
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> // read、write、close 头文件
#include <sys/mman.h> // mmap 的头文件
#include "lcd.h" // 用到哪个函数就得 加上 对应的头文件
int lcd_fd = -1; // 全局变量
/*
lcd_init: 屏幕初始化 打开 + 映射
返回值: 第一个像素点的地址
*/
unsigned int *lcd_init()
{
// 1、打开屏幕文件
lcd_fd = open("/dev/fb0",O_RDWR); // 读写打开 显示屏
if(lcd_fd == -1)
{
perror("open error"); // 显示错误原因
return NULL;
}
// 2、屏幕映射
unsigned int *plcd = mmap(NULL,800*480*4,PROT_READ | PROT_WRITE,MAP_SHARED,lcd_fd,0);
if(plcd == MAP_FAILED)
{
perror("mmap error"); // 显示错误原因
return NULL;
}
return plcd;
}
/*
lcd_end: 屏幕扫尾 解映射 + 关闭
返回值: 第一个像素点的地址
*/
void lcd_end(unsigned int *plcd)
{
// 4、解除映射
munmap(plcd,800*480*4);
// 5、关闭屏幕文件
close(lcd_fd);
}
lcd.h
#ifndef __LCD_H__ // 固定格式
#define __LCD_H__ // 固定格式
/*
lcd_init: 屏幕初始化 打开 + 映射
返回值: 第一个像素点的地址
*/
unsigned int *lcd_init();
/*
lcd_end: 屏幕扫尾 解映射 + 关闭
返回值: 第一个像素点的地址
*/
void lcd_end(unsigned int *plcd);
#endif // 固定格式
bmp.c UI界面及屏幕输入的封装
#include "bmp.h"
#include "game.h"
#include <stdlib.h> // abs 的头文件
// 居中显示图片
void Display_homepage(char *bmp_name,unsigned int * plcd)
{
// 1、打开图片文件
int fd = open(bmp_name,O_RDWR); // 读写打开 图片
if(fd == -1)
{
perror("open error"); // 显示错误原因
return ;
}
// 2、判断图片是不是 BMP
char buf[2];
read(fd,buf,2);
if(!(buf[0] == 'B' && buf[1] == 'M'))
{
printf("你是不是憨批,转换不会\n");
return ;
}
// 3、解析 位宽 位高 色深
int width;
lseek(fd,0x12,SEEK_SET); // 定位到指定的位置
read(fd,&width,4); // 读取 4 字节的宽度
int height;
lseek(fd,0x16,SEEK_SET); // 定位到指定的位置
read(fd,&height,4); // 读取 4 字节的高度
short depth;
lseek(fd,0x1c,SEEK_SET); // 定位到指定的位置
read(fd,&depth,2); // 读取 2 字节的高度
printf("%s:%d,%d,%d\n",bmp_name,width,height,depth);
// 4、获取像素数组
// 一行实际大小:
int line_bytes = abs(width) * depth/8;
int laizi = 0; // 可能需要补充的字节数目
if(line_bytes % 4)
{
laizi = 4 - line_bytes % 4;
}
// 一行保存总大小:
int linebytes = line_bytes + laizi;
// 像素数组总大小:
int bytes = linebytes * abs(height);
// 读取像素数组:
unsigned char array[bytes];
lseek(fd,0x36,SEEK_SET); // 定位到指定的位置
read(fd,array,bytes);
// 5、显示图片
unsigned char a,r,g,b; // 保存每个像素点颜色值分量
int i,j,k = 0;
for(i = 0;i < abs(height);i++) // 一行一行的显示
{
for(j = 0;j < abs(width);j++) // 一行每个像素点依次显示
{
b = array[k++]; // 获取当前像素点的 蓝色分量
g = array[k++]; // 获取当前像素点的 绿色分量
r = array[k++]; // 获取当前像素点的 红色分量
if(depth == 32)
{
a = array[k++]; // 获取当前像素点的 透明分量
}
else
{
a = 0;
}
unsigned int color = a << 24 | r << 16 | g << 8 | b; // 合成当前像素点的颜色
int x = width > 0 ? j : (abs(width) - 1 - j); // width > 0 从左到右,反之从右到左 0开始的
int y = height > 0 ? (abs(height) - 1 - i) : i; // height > 0 从下到上,反之从上到下 0开始的
int m=(800-abs(width))/2;
int n=(480-abs(height))/2;
display_point(x+m,y+n,color,plcd);
}
k += laizi; // 跳过加上的癞子
}
// 6、关闭图片文件
close(fd);
}
void Display_qipan() //生成棋盘页面
{ //打开屏幕文件
int fd = open("/dev/fb0",O_RDWR);
unsigned int *plcd = mmap(NULL,800*480*4,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
display_pic("duolah.bmp",plcd,0,0);//黑方
display_pic("duolaw.bmp",plcd,600,0);//白方
//生成棋盘
for(int i = 0;i <= 480;i++)
{
for(int j = 120;j <= 600;j++)
{
if((i%30 -15 == 0||j%30 - 15 == 0)&& i >= 15&&i <= 465&&j >= 135&&j <= 585)
display_point(j, i,0x00, plcd);
else
{
display_point(j, i,0xCDAA7D,plcd);
}
}
}
munmap(plcd,800*480*4);
close(fd);
}
//设定位置显示图片
void display_pic(char *bmp_name,unsigned int * plcd,int m ,int n)
{
// 1、打开图片文件
int fd = open(bmp_name,O_RDWR); // 读写打开 图片
if(fd == -1)
{
perror("open error"); // 显示错误原因
return ;
}
// 2、判断图片是不是 BMP
char buf[2];
read(fd,buf,2);
if(!(buf[0] == 'B' && buf[1] == 'M'))
{
printf("你是不是憨批,转换不会\n");
return ;
}
// 3、解析 位宽 位高 色深
int width;
lseek(fd,0x12,SEEK_SET); // 定位到指定的位置
read(fd,&width,4); // 读取 4 字节的宽度
int height;
lseek(fd,0x16,SEEK_SET); // 定位到指定的位置
read(fd,&height,4); // 读取 4 字节的高度
short depth;
lseek(fd,0x1c,SEEK_SET); // 定位到指定的位置
read(fd,&depth,2); // 读取 2 字节的高度
/*printf("%s:%d,%d,%d\n",bmp_name,width,height,depth);*/
// 4、获取像素数组
// 一行实际大小:
int line_bytes = abs(width) * depth/8;
int laizi = 0; // 可能需要补充的字节数目
if(line_bytes % 4)
{
laizi = 4 - line_bytes % 4;
}
// 一行保存总大小:
int linebytes = line_bytes + laizi;
// 像素数组总大小:
int bytes = linebytes * abs(height);
// 读取像素数组:
unsigned char array[bytes];
lseek(fd,0x36,SEEK_SET); // 定位到指定的位置
read(fd,array,bytes);
// 5、显示图片
unsigned char a,r,g,b; // 保存每个像素点颜色值分量
int i,j,k = 0;
/*printf("请输入起始位置: ");
scanf("%d,%d",&m,&n);*/
for(i = 0;i < abs(height);i++) // 一行一行的显示
{
for(j = 0;j < abs(width);j++) // 一行每个像素点依次显示
{
b = array[k++]; // 获取当前像素点的 蓝色分量
g = array[k++]; // 获取当前像素点的 绿色分量
r = array[k++]; // 获取当前像素点的 红色分量
if(depth == 32)
{
a = array[k++]; // 获取当前像素点的 透明分量
}
else
{
a = 0;
}
unsigned int color = a << 24 | r << 16 | g << 8 | b; // 合成当前像素点的颜色
int x = width > 0 ? j : (abs(width) - 1 - j); // width > 0 从左到右,反之从右到左 0开始的
int y = height > 0 ? (abs(height) - 1 - i) : i; // height > 0 从下到上,反之从上到下 0开始的
display_point(x+m,y+n,color,plcd);
}
k += laizi; // 跳过加上的癞子
}
// 6、关闭图片文件
close(fd);
}
/*
display_point: 在(x,y)显示一个颜色为 color 的点
@x: x 坐标
@y: y 坐标
@color: 要显示颜色
@plcd: 第一个像素点的地址(mmap 的返回值)
*/
void display_point(int x,int y,unsigned int color,unsigned int *plcd)
{
if(x >= 0 && x < 800 && y >= 0 && y < 480) // 防止憨批
{
*(plcd + y * 800 + x) = color;
}
}
//获取触屏坐标
struct point get_point()
{
struct point xy = {-1,-1}; // 保存实时坐标
// 1、打开文件
int fd = open("/dev/input/event0",O_RDWR); // 读写打开 触摸屏
if(fd == -1)
{
perror("open error"); // 显示错误原因
return xy;
}
// 2、操作文件 write
struct input_event ev; // 保存读取到的输入信息
while(1)
{
read(fd,&ev,sizeof(ev));
if(ev.type == EV_ABS && ev.code == ABS_X)
{
xy.x = ev.value; // 保存 x 坐标
}
else if(ev.type == EV_ABS && ev.code == ABS_Y)
{
xy.y = ev.value; // 保存 y 坐标
}
else if(ev.type == EV_KEY && ev.code == BTN_TOUCH && ev.value == 0) // 手离开屏幕
{
break;
}
}
// 3、关闭文件
close(fd);
return xy;
}
bmp.h
#ifndef __BMP_H__
#define __BMP_H__
#include <stdio.h>
#include "game.h"
struct point // 保存触摸屏的坐标 point 对应两个值 point.x point.y
{
int x;
int y;
};
void Display_qipan();
void Display_homepage(char *bmp_name,unsigned int * plcd);
void display_point(int x,int y,unsigned int color,unsigned int *plcd);
void display_pic(char *bmp_name,unsigned int * plcd,int m ,int n) ;
struct point get_point();
#endif
gsme.c 游戏的算法
#include "bmp.h"
#include "game.h"
#include <stdlib.h> // abs 的头文件
int turn = 0; //回合数
unsigned int Game_buf[16][17]={0};//存储16*16每个点
/*答辩结束后发现此处存在bug*/
unsigned int Game_buf2[10][16][17]={0};//存储10局16*16每个点
int Game_sta(int x,int y,unsigned int *plcd) //游戏开始 像素点坐标
{
//得到棋盘坐标位置
int X = abs(x - 120) / 30; // 120 = 135-(30/2)
int Y = abs(y - 0) / 30; // 0=15-(30/2);
printf("落子位置为%d,%d \n",X,Y);
// 将落棋状态写入数组 0/1/2 分别代表 空/黑/白
if(Game_buf[X][Y]==0) //避免重复落子
{
//轮数加1
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 16; j++)
{
Game_buf2[turn][i][j] = Game_buf[i][j]; //保存 落子前所有的棋子位置
}
}
turn++;
if(turn % 2)
{
Game_buf[X][Y]=1;
Luozi(X * 30 + 135,Y * 30 + 15,turn,plcd);
}
else
{
Game_buf[X][Y]=2;
Luozi(X * 30 + 135,Y * 30 + 15,turn,plcd);
}
}
//提醒下棋
if(turn & 1)//黑方下完提示白方
{
display_pic("baiturn.bmp", plcd,600,175);
}
else
{
display_pic("heiturn.bmp", plcd,600,175);
}
//游戏结束判定
turn = Game_Over(X,Y,plcd,turn);
return turn ;
}
//显示落子
int Luozi(int x,int y,int turn,unsigned int *plcd)
{
int i,j;
if (turn & 1)//执黑方先落子
{
for (i = 0; i < 800; i++)
{
for (j = 0; j < 480; j++)
{
if ((i-x)*(i-x)+(j-y)*(j-y)<=100)//生成半径为10的棋子
{
display_point(i, j, 0X000000,plcd);
}
}
}
printf("执白者已落子 \n");
}
else
{
for(i=0;i<800;i++)
{
for(j=0;j<480;j++)
{
if((i-x)*(i-x)+(j-y)*(j-y)<=100)
{
display_point(i,j,0xffffff,plcd);
}
}
}
printf("执黑者已落子 \n");
}
}
//悔棋
int regret(int x,int y,unsigned int *plcd)
{
if(turn)
{
turn--; //回合数减1
//存储 复位悔棋前的棋子布局
for(int i = 0;i < 16;i++)
{
for(int j = 0;j < 16;j++)
{
Game_buf[i][j]=Game_buf2[turn][i][j]; //棋盘复位
}
}
// 显示 复位悔棋前的棋子布局
Display_qipan();
for(int i = 0;i < 16;i++)
{
for(int j = 0;j < 16;j++)
{
int X = abs(x - 120) / 30; // 120 = 135-(30/2)
int Y = abs(y - 0) / 30; // 0=15-(30/2);
if(Game_buf[i][j] == 1)
{
Luozi(i * 30 + 135,j * 30 + 15,1,plcd);
}
if(Game_buf[i][j] == 2)
{
Luozi(i * 30 + 135,j * 30 + 15,2,plcd);
}
}
}
}
else
{
printf("棋盘中已无棋子 \n");
}
}
//清空棋盘
int clear()
{
for(int a=0;a<16;a++)
{
for(int b=0;b <16;b++)
{
Game_buf[a][b]=0;
for (int c= 0; c < 10; c++)
{
Game_buf2[c][a][b]=0;
}
}
}
turn = 0 ;
printf("已清盘 \n");
}
//游戏结束条件
int Game_Over(int x,int y,unsigned int * plcd,int turn) //棋盘落子坐标
{
int x1 = 0,y1 = 0,xy1 = 0,xy2 = 0;//四个方向的累计数
for(int i = -4;i < 5;i ++)
{
//水平方向判定
if(Game_buf[x][y] == Game_buf[x + i][y] )
{
x1++;
printf("x1值为 %d\n",x1);
if(x1 == 5)
{
break;
}
}
else
{
x1 = 0;
}
//竖直方向判定
if(Game_buf[x][y] == Game_buf[x][y + i])
{
y1++;
printf("y1值为 %d\n",y1);
if(y1 == 5)
{
break;
}
}
else
{
y1 = 0;
}
//Y=X方向判定
if(Game_buf[x][y] == Game_buf[x + i][y + i])
{
xy1++;
printf("xy1值为 %d\n",xy1);
if(xy1 == 5)
{
break;
}
}
else
{
xy1 = 0;
}
//Y=-X方向判定
if(Game_buf[x][y] == Game_buf[x - i][y+i])
{
xy2++;
printf("xy2值为 %d\n",xy2);
if(xy2 == 5)
{
break;
}
}
else
{
xy2 = 0;
}
}
if( x1 == 5||y1 == 5||xy1 == 5||xy2 == 5)//结束条件
{
if(turn & 1)
{
Display_homepage("heiwin.bmp", plcd);
/*system("killall -KIll madplay");
system("madplay -Q 3.mp3 &");*/
}
else
{
Display_homepage("baiwin.bmp", plcd);
/*system("killall -KIll madplay");
system("madplay -Q 4.mp3 &");*/
}
clear(); //清理棋盘
sleep(3); //复盘时间
Display_qipan();
//播放背景音乐
system("killall -KIll madplay");
system("madplay -Q 2.mp3 -a -20 &");
return 0;
}
else
{
return turn;
}
}
int Game_play(unsigned int *plcd)
{
while(1)
{
struct point p = get_point();
printf("%d,%d\n",p.x,p.y);
if (p.x>370&&p.x<625&&p.y>520&&p.y<600) //进入游戏,生成棋盘
{
Display_qipan();
clear();
system("killall -KIll madplay");
system("madplay -Q 2.mp3 -a -20 &");
printf("开始下棋 \n");
break;
}
else
{
printf("请点击指定区域 \n");
}
}
while(1)
{
struct point p = get_point();
printf("%d,%d\n",p.x,p.y);
int x1 = p.x * 800 / 1024,y1 = p.y * 480 / 600; //触屏坐标转化为像素点坐标
if (x1>120&&x1<600&&y1>0&&y1<480) // 在范围内落子
{
//开始游戏
Game_sta(x1,y1,plcd);
}
else if(x1>600&&x1<800&&y1>0&&y1<75||x1>0&&x1<120&&y1>0&&y1<80)// 悔棋
{
regret(x1,y1,plcd);
}
else if (y1>400&&y1<480&&x1>600&&x1<800)
{
printf("返回主界面 \n");
break ;
}
}
}
game.h
#ifndef __GAME_H__
#define __GAME_H__
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <math.h>
#include <linux/input.h>
struct point get_point();
int Game_sta(int x,int y,unsigned int *plcd);
int save(int turn);
int Luozi(int x,int y,int turn,unsigned int *plcd);
int clear();
int regret(int x,int y,unsigned int *plcd);
int Game_Over(int x,int y,unsigned int * plcd,int turn);
int Game_play(unsigned int *plcd);
#endif
项目展示
项目讲解