此制作是在LCD设备上实现,并且还需要在LCD设备上下载相应的图片。
关键点:
随机值生成:
(1)
关键在于利用srandom和random配套组合生成随机值。
用法:
srandom( time(NULL) ); //用于重新打乱随机因子(一般只需要启动一次)
num=random()%n; //用于生成0-n之间的一个随机数。赋值给num
(2)棋盘数组(用于记录每个位置上的数,并利用图片将其显示出来)
int matrix_2048[4][4] = {
0,0,0,0,
2,0,0,0,
0,0,4,0,
0,0,0,0
};
(3)在棋盘的随机位置填充上一个随机数的方法。(注意,必须不能填充到已有数字的位置)
方法1:将所有为0的位置记录下来,并且用一个数组去保存。(容易理解)
int zero_array[16]={0};
int get_matrix_zero_num()
{
int i=0,j=0,k=0;
int zero_num=0;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
{
if(matrix_2048[i][j]==0)
zero_array[zero_num++]=k;
k++;//用于记录0的位置,用于保存
}
//这样就可以得到所有为0的位置和0的数量
return zero_num;
}
int set_matrix_num(int place,int num)//往棋盘上某个没有数值的位置上,填上一个随机值
{
int i=0,j=0,k=0;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
{
if(k==place)//如果找得到位置
matrix_2048[i][j] = num;
k++; //根据计数值,来计算对应位置
}
}
int rand_matrix()
{
int rand_num=0,rand_place=0,i;
int num[4]={2,4,8,2}; //随机值
bzero(zero_array,16);
i=get_matrix_zero_num(); //先获取0的数量
rand_place=random()%i; //利用随机值求出0位置下标
rand_num=random()%3;
set_matrix_num(rand_place,rand_num);//根据求出来的随机位置和值,填充到对应的数组中。
}
方法2:只记录0的数量,然后根据其数量来生成随机值,并且在对应0的位置进行查找,最终填值。
int get_matrix_zero_num()
{
int i=0,j=0;
int zero_num=0;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
{
if(matrix_2048[i][j]==0)
zero_num++;
}
//这样就可以得到所有为0的位置和0的数量
return zero_num;
}
int set_matrix_num(int place,int num)//往棋盘上某个为0的位置上,填上一个随机值
{
int i=0,j=0,k=0;
for(i=0;i<4;i++)
for(j=0;j<4;j++)
{
if(matrix_2048[i][j]==0)//只找0的位置进行计数
{
if(k == place)
matrix_2048[i][j]=num;
k++;
}
}
}
随机函数同上。
注意点:
求余符号(%)和除法符号(/),不能对0操作,否则会出点浮点数错误
完整代码:
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h> //perror ,errno
//#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <math.h>
#include <linux/input.h> //struct input_event
int *plcd = NULL; //指向屏幕内存首地址
#define BORADSIZE 4 // 棋盘矩阵的大小: BORADSIZE * BORADSIZE. 棋子的个数(每一行)
#define PIECE_SIZE 100 //每个棋子的像素宽度
#define BORADBLANK 10 //棋子与棋子之间的空白宽度
#define matrix_X0 0 //整个棋盘左上角顶点的x坐标
#define matrix_Y0 0 //整个棋盘左上角顶点的y坐标
#define LCD_WIDTH 1024
#define LCD_HEIGHT 600
#define ZERO_COLOR 0xffffff
#define MOVE_UP 0
#define MOVE_DOWN 1
#define MOVE_RIGHT 2
#define MOVE_LEFT 3
static int game_over_flag =0;
static int matrix_max=0;
//2048游戏矩阵,整个程序的核心就是这玩意
//
int matrix_2048[BORADSIZE][BORADSIZE] =
{
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0
};
//数字合成的数据图片
const char *bmps[] =
{
NULL, //0
"2.bmp", // 2
"4.bmp", // 4
"8.bmp", // 8
"16.bmp", // 16
"32.bmp", // 32
"64.bmp", // 64
"128.bmp", // 128
"256.bmp", // 256
"512.bmp", // 512
"1024.bmp", // 1024
"2048.bmp", // 2048
// "2048_photo/military_font_7_4096.bmp", // 4096
// "2048_photo/military_font_7_8192.bmp", // 8192
// "2048_photo/military_font_7_16384.bmp", // 16384
// "2048_photo/military_font_7_32768.bmp", // 32768
// "2048_photo/military_font_7_65536.bmp", // 65536
NULL,
};
//计算数字,得到对应的数组图片名
const char * get_bmpname_by_digtal(int num)
{
int index = 0;
switch(num)
{
case 2:
index = 1;
break;
case 4:
index = 2;
break;
case 8:
index = 3;
break;
case 16:
index = 4;
break;
case 32:
index = 5;
break;
case 64:
index = 6;
break;
case 128:
index = 7;
break;
case 256:
index = 8;
break;
case 512:
index = 9;
break;
case 1024:
index = 10;
break;
case 2048:
index = 11;
break;
// case 4096:
// index = 12;
// break;
// case 8192:
// index = 13;
// break;
// case 16384:
// index = 14;
// break;
// case 32768:
// index = 15;
// break;
// case 65536:
// index = 16;
// break;
default:
index = 0;
break;
}
return bmps[index];
}
/*
LCD_Draw_Point:给屏幕坐标为(x, y)这个点一个颜色值color
*/
void LCD_Draw_Point(int x, int y, int color)
{
int *p = plcd;
*(p + y*1024 + x) = color;
}
/*
LCD_Draw_Bmp:给LCD坐标(start_x,start_y)位置,画一张大小为w*h的图片
*/
void LCD_Draw_Bmp(int start_x,int start_y,int w,int h,const char* bmp_path)
{
int fd;
unsigned char pixs[1024*600*3];//图片大小为100*100
int i = 0;
//printf(" --> %s\n", bmpname);
fd = open(bmp_path, O_RDONLY);
if (fd == -1)
{
perror("open failed:");
return ;
}
lseek(fd, 54, SEEK_SET);
read(fd, pixs, w*h*3);
close(fd);
int x, y;
for (y = start_y; y < start_y+h; y++)
{
//读一行
for (x = start_x; x < start_x+w; x++)
{
unsigned char b,g,r;
int color;
b = pixs[i++];
g = pixs[i++];
r = pixs[i++];
color = (r << 16) | (g << 8) | b;
LCD_Draw_Point(start_x + x, start_y + (599 - y), color);
}
}
}
/*
LCD_Clear_Screen:给屏幕上每一个点都显示同一种颜色值color
*/
void LCD_Clear_Screen( int color)
{
int x, y;
for (y = 0; y < 600; y++)
{
for (x = 0; x < 1024; x++)
{
LCD_Draw_Point(x, y, color);
}
}
}
/*
LCD_draw_rect: 在屏幕上填充一个颜色矩阵。
@x0: 颜色矩阵左上角顶点的x坐标
@y0:颜色矩阵左上角顶点的y坐标
@w: 颜色矩阵的长(像素点为单位)
@h: 颜色矩阵的高
@color: 要的颜色值
返回值:
无.
*/
void LCD_draw_rect(int x0, int y0, int w, int h, int color)
{
if (x0 <0 || x0 > LCD_WIDTH || y0 < 0 || y0 > LCD_HEIGHT || w < 0 || h <0)
return ;
if ((x0 + w > LCD_WIDTH) || (y0 + h) > LCD_HEIGHT)
return;
/*
把所在该矩形框里面的像素点()都赋一个
颜色值color
(x, y)
*/
int x, y;
for (y = y0; y < y0 +h; y++)
{
for (x = x0; x < x0 + w; x++)
{
LCD_Draw_Point(x, y, color);
}
}
}
/*
LCD_draw_rect_bmp:在屏幕的顶点(x0, y0)处显示一张
bmp图片文件(由bmpname指定)
*/
void LCD_draw_rect_bmp(int x0, int y0, const char *bmpname)
{
int fd;
unsigned char pixs[100*100*3];//图片大小为100*100
int i = 0;
//printf(" --> %s\n", bmpname);
fd = open(bmpname, O_RDWR);
if (fd == -1)
{
perror("open failed:");
return ;
}
lseek(fd, 54, SEEK_SET);
read(fd, pixs, 100*100*3);
close(fd);
int x, y;
for (y = 0; y < 100; y++)
{
//读一行
for (x = 0; x < 100; x++)
{
unsigned char b,g,r;
int color;
b = pixs[i++];
g = pixs[i++];
r = pixs[i++];
color = (r << 16) | (g << 8) | b;
LCD_Draw_Point(x0 + x, y0 + 99 - y, color);
}
}
}
/*
draw_matrix_2048:在屏幕上画一个棋盘矩阵
@x0: 整个棋盘矩阵左上角顶点的x坐标
@y0: 整个棋盘矩阵左上角顶点的y坐标
@num: 棋盘每一行有多少个棋子
@piece_size:每个棋子宽度
@blank_size: 棋子与棋子之间空白宽度
*/
void draw_matrix(int x0, int y0, int num, int piece_size, int blank_size)
{
int k = 0;
int n = 0;
//int color;
int x, y; //每个棋子左上角顶点的坐标
for (n = 0; n < num; n++)
{
y = y0 + n *(piece_size + blank_size); //每一个y点的起始位置:y0+(宽度+间隙)*棋子数量
/*画一行的棋子*/
for (k = 0; k < num; k++)
{
x = x0 + k*(piece_size + blank_size); //每一个x点的起始位置:x0+(宽度+间隙)*棋子数量
if (matrix_2048[n][k] == 0)
{
LCD_draw_rect(x, y, piece_size, piece_size, ZERO_COLOR); //如果棋盘对应位置没值,就清空
}
else
{
LCD_draw_rect_bmp(x, y,
get_bmpname_by_digtal( matrix_2048[n][k] ) //填充对应数字的图片
);
}
}
}
}
/*
get_max_num:用于获取棋盘中最大的数值,并显示出来
*/
void get_max_num()
{
int i,j;
int max=0;
const char *bmp_name=NULL;
for (i = 0; i < BORADSIZE; ++i)
{
for (j = 0; j < BORADSIZE; ++j)
{
if (matrix_2048[i][j]>max)
{
max=matrix_2048[i][j];
}
}
}
//求出整个棋盘最大数。并且将其显示出来
if(max>matrix_max)
{
matrix_max=max;
bmp_name=get_bmpname_by_digtal(max);
LCD_draw_rect_bmp(600,240,bmp_name);
}
}
/*
get_finger_move_direction:用来获取手指的滑动方向的
MOVE_UP ,
MOVE_DOWN,
MOVE_RIGHT,
MOVE_LEFT,
*/
int get_finger_move_direction()
{
int fd;
int mv = -1;
fd = open("/dev/input/event1", O_RDWR);
if (fd == -1)
{
perror("open4 failed");
return -1;
}
struct input_event ev = {0};
int x1 = -1;
int x2 = -1;
int y1 = -1;
int y2 = -1;
// x1 = -1;
// y1 = -1;
while (1)
{
int r = read(fd, &ev, sizeof(ev));
if (r < sizeof(ev))
{
perror("read failed");
close(fd);
return -1;
}
if (ev.type == EV_ABS && ev.code == ABS_MT_POSITION_X)
{
if (x1 == -1)
{
x1 = ev.value;
}
else
{
x2 = ev.value;
}
}
if (ev.type == EV_ABS && ev.code == ABS_MT_POSITION_Y)
{
if (y1 == -1)
{
y1 = ev.value;
}
else
{
y2 = ev.value;
}
}
//手指弹起
if ( ev.code == ABS_MT_TRACKING_ID && ev.value == -1)
{
int delta_x = abs(x2-x1);
int delta_y = abs(y2-y1);
if (delta_x >2*delta_y)
{
if (x2 > x1)
{
mv = MOVE_RIGHT;
}
else
{
mv = MOVE_LEFT;
}
break;
} else if (delta_y > 2 * delta_x)
{
if (y2 > y1)
{
mv = MOVE_DOWN;
}
else
{
mv = MOVE_UP;
}
break;
}
else //恭喜您,再来一次
{
x1 = -1;
y1 = -1;
}
}
}
close(fd);
return mv;
}
/*
get_ts_xy:用于获取一次坐标值
*/
void get_ts_xy(int *x,int *y)
{
int fd;
fd = open("/dev/input/event1", O_RDWR);
if (fd == -1)
{
return;
}
struct input_event ev;
while (1)
{
read(fd, &ev, sizeof(ev));
if (ev.type == EV_ABS && ev.code == ABS_MT_POSITION_X)
*x = ev.value;
if (ev.type == EV_ABS && ev.code == ABS_MT_POSITION_Y)
*y = 599-ev.value;
//手指弹起
if (ev.code == ABS_MT_TRACKING_ID && ev.value == -1)
break;
}
}
/*
get_zero_num:求棋盘矩阵里面有多少个0
返回值:
返回棋盘矩阵中0的个数
*/
int get_zero_num()
{
int z = 0;//棋盘矩阵中元素为0的个数
int i, j;
for (i = 0; i < BORADSIZE; i++)
{
for (j = 0; j < BORADSIZE; j++)
{
if (matrix_2048[i][j] == 0)
{
z++;
}
}
}
return z;
}
/*
move_judge:判断是否还能移动
return value:
1 game over
0 continue
*/
int move_judge()
{
int i, j;
if(get_zero_num() != 0)
{
return 0;
}
//当没有0时,就进入下面判断
//先判断每一列上面能不能合并
for(i = 0; i < BORADSIZE; i++)
{
for(j = 0; j < BORADSIZE ; j++)
{
if (j != BORADSIZE -1)
{
if (matrix_2048[i][j] == matrix_2048[i][j+1])
{
return 0;
}
}
//再判断每一行上面能不能合并
if (i != BORADSIZE - 1)
{
if (matrix_2048[i][j] == matrix_2048[i+1][j])
{
return 0;
}
}
}
}
return 1;
}
//算法重新研究
void up_fun()
{
int h=0,w=0;
int value=0,save_zero=0;
for(w=0;w<BORADSIZE;w++)
{
value=0;
save_zero=0;
for(h=0;h<BORADSIZE;h++)
{
if(matrix_2048[h][w]==0)//顶格不为空
continue;
if(value==0)
{
value = matrix_2048[h][w];
}
else
{
if(matrix_2048[h][w]==value)//与上次保留的值做比较
{
matrix_2048[save_zero++][w] = value*2;
value = 0;
}
else //上下不相等
{
matrix_2048[save_zero++][w] = value;
value = matrix_2048[h][w];
}
}
matrix_2048[h][w] = 0;//将当前的位置填为0
}
if(value != 0)//将最底层的位置填为当前值
{
matrix_2048[save_zero][w] = value;
}
}
}
void down_fun()
{
int h=0,w=0;
int value;
int save_zero;
for(w = 0; w < BORADSIZE; w++)
{
value = 0;
save_zero = BORADSIZE - 1;
for(h = BORADSIZE - 1; h >= 0 ; h--)
{
if(matrix_2048[h][w] == 0)
{
continue;
}
if(value == 0)
{
value = matrix_2048[h][w];
}
else
{
if(value == matrix_2048[h][w])
{
matrix_2048[save_zero--][w] = 2 * value;
value = 0;
}
else
{
matrix_2048[save_zero--][w] = value;
value = matrix_2048[h][w];
}
}
matrix_2048[h][w] = 0;
}
if(value != 0)
{
matrix_2048[save_zero][w] = value;
}
}
}
void left_fun()
{
int h=0,w=0;
int value, save_zero;
for(h = 0; h < BORADSIZE; h++)
{
value = 0;
save_zero= 0;
for(w = 0; w < BORADSIZE ; w++)
{
if (matrix_2048[h][w] == 0)
continue;
if (value == 0)
value = matrix_2048[h][w];
else
{
if (value == matrix_2048[h][w])
{
matrix_2048[h][save_zero++] = value * 2;
value = 0;
} else {
matrix_2048[h][save_zero++] = value;
value = matrix_2048[h][w];
}
}
matrix_2048[h][w] = 0;
}
if (value != 0)
matrix_2048[h][save_zero] = value;
}
}
void right_fun()
{
int h=0,w=0;
int value;
int save_zero;
for(h = 0; h < BORADSIZE; h++)
{
value = 0;
save_zero = BORADSIZE -1;
for(w = BORADSIZE - 1; w >= 0 ; w--)
{
if(matrix_2048[h][w] == 0)
{
continue;
}
if(value == 0)
{
value = matrix_2048[h][w];
}
else
{
if(value == matrix_2048[h][w])
{
matrix_2048[h][save_zero--] = 2 * value;
value = 0;
}
else
{
matrix_2048[h][save_zero--] = value;
value = matrix_2048[h][w];
}
}
// 移动后原来的位置清空
matrix_2048[h][w] = 0;
}
if(value != 0) // 最右边位置更新值
{
matrix_2048[h][save_zero] = value;
}
}
}
/*
change_matrix:判断手指滑动的方向,对应更改数组里面的值
*/
void change_matrix()
{
//1.判断之后,修改数组的值
int touch_flag;
touch_flag=get_finger_move_direction();
switch(touch_flag)
{
case MOVE_UP:
up_fun();
break;
case MOVE_DOWN:
down_fun();
break;
case MOVE_RIGHT:
right_fun();
break;
case MOVE_LEFT:
left_fun();
break;
}
}
/*
set_matrix_one_num:设置棋盘上的某个位置的数
*/
void set_matrix_one_num(int z, int s)
{
int i, j;
int k = 0 ;//0的个数
for (i = 0; i < BORADSIZE ;i++)
{
for (j = 0; j < BORADSIZE; j++)
{
if (matrix_2048[i][j] == 0)
{
k++;
if (k == z)
{
matrix_2048[i][j] = s;
return;
}
}
}
}
}
/*
init_matrix_2048:利用随机数,在棋盘的随机位置上,
填充上随机的数,作为初始化的第一个数
*/
void init_matrix()
{
//规则x >= 1,x <= 3
int x = (random() % 3) + 1;
int i;
/*
step1:随机产生x个数字,并填充到棋盘矩阵中去
*/
for(i = 0; i < x; i++)
{
int pos = (random() % get_zero_num()) + 1;
//先求出0-15的随机数,用以确定第一个数字的位置
int s[] = {2, 4, 8, 2};
int s_i = (random() % 3);
//随机在数组中,定出一个具体数字,再显示到对应的位置上
set_matrix_one_num(pos, s[s_i]);
}
}
/*
rand_matrix:移动之后随机产生一个数字填充到
任意一个0的位置上
*/
void rand_matrix()
{
//printf("2\n");
int pos=0;
if(get_zero_num()==0) //防止出现Floating point exception
pos=1;
else
pos= (random() % get_zero_num()) + 1;
//printf("3\n");
int s[] = {2, 4, 8, 2};
int s_i = (random() % 4);
set_matrix_one_num(pos, s[s_i]);
//draw_matrix_2048();
}
/*
清空棋盘
*/
void clear_matrix()
{
int i,j;
for (i = 0; i < BORADSIZE; ++i)
for (j = 0; j < BORADSIZE; ++j)
matrix_2048[i][j] =0;
}
int main()
{
int x=0,y=0;
int fd;
/* step 1:
打开屏幕设备
*/
fd = open("/dev/fb0", O_RDWR);
if (fd == -1) //出错啦, -> errno被设置
{
//perror先把后面的字符串打印,然后再
//根据errno的值把相应的出错提示打印出来
perror("open failed:");
return -1;
}
int *p= mmap(NULL, 1024*600*4, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
if (p == MAP_FAILED)
{
perror("mmap failed:");
close(fd);
return -1;
}
plcd = p;
again:
clear_matrix();
//step 2:为显示屏显示一个初始画面
LCD_Draw_Bmp(0,0,1024,600,"2048_menu.bmp");
//step 3:初始化棋盘的初始化数字(什么位置出现2.4.8)
srandom( time(NULL) ); //设置随机数种子,种子一样,产生的
// 初始化棋盘,2482
init_matrix();
//step 4:显示棋盘初始界面(初始已有数字)
draw_matrix(100, 30, BORADSIZE, PIECE_SIZE, BORADBLANK);
//step 5.不断判断手指触摸屏的方向,并且做出动作
while(1) //判断游戏是否还能继续
{
change_matrix();//判断移动
//step 6.根据发生改变的位置,重新刷新对应的图片
rand_matrix();
//step 7.再次生成随机数,填充棋盘上某个空白位置
draw_matrix(100, 30, BORADSIZE, PIECE_SIZE, BORADBLANK);
//拓展:更新最高分
get_max_num();
//step 8.判断是否结束(遍历看看还没有空值,如果没有,再判断是否还能合并)
game_over_flag = move_judge();
if(game_over_flag==1)//游戏结束,判断是否继续
{
LCD_Draw_Bmp(0,0,1024,600,"game_over.bmp");
sleep(2);
LCD_Draw_Bmp(0,0,1024,600,"continue.bmp");
get_ts_xy(&x,&y);
if(x>0&&x<512)
goto again;
else
break;
}
//出现bug
}
printf("Game Over~~\n");
LCD_Draw_Bmp(0,0,1024,600,"game_over.bmp");
/*
关闭设备
*/
munmap(p, 1024*600*4);
close(fd);
}