C语言实现2048小游戏
规则就是 2084 游戏的规则
j - 左
k - 下
l - 右
i - 上
相同数字移动过程中会合并
这里我把按键修改成了方向键,并加了些注释帮助理解
#include<stdio.h> #include<stdlib.h> #include<conio.h> #define GAME_SIZE 4 static void left(int *data) { int i, j, f;//f标志位判断是否经过合并,每一行(列)有且只能合并一次 int row = GAME_SIZE; while (row--) { for (f=0,j=0,i=0; i<GAME_SIZE; i++) { if (data[i]) { if (!f && j>0 && data[j-1] == data[i]) { data[j-1]+= data[i]; f = 1;//j左边有非0格,且与i格相等才合并 } else { data[j++] = data[i]; f = 0;//j记录0格下标 (j在0格和相等合并时不变,否则+1) } } } while (j < GAME_SIZE) data[j++] = 0;//填充剩余格 data += GAME_SIZE;//地址偏移4格 } } static void right(int *data) { int i, j, f; int row = GAME_SIZE; while (row--) { for (j=GAME_SIZE-1,f=0,i=GAME_SIZE-1; i>=0; i--) { if (data[i]) { if (!f && j<GAME_SIZE-1 && data[j+1] == data[i]) { data[j+1]+= data[i]; f = 1; } else { data[j--] = data[i]; f = 0; } } } while (j >= 0) data[j--] = 0; data += GAME_SIZE; } } static void up(int *data) { int i, j, f; int col = GAME_SIZE; while (col--) { for (j=0,f=0,i=0; i<GAME_SIZE; i++) { if (data[i*GAME_SIZE]) { if (!f && j>0 && data[(j-1)*GAME_SIZE] == data[i*GAME_SIZE]) { data[(j-1)*GAME_SIZE]+= data[i*GAME_SIZE]; f = 1; } else { data[(j++)*GAME_SIZE] = data[i*GAME_SIZE]; f = 0; } } } while (j < GAME_SIZE) data[(j++)*GAME_SIZE] = 0; data++; } } static void down(int *data) { int i, j, f; int col = GAME_SIZE; while (col--) { for (j=GAME_SIZE-1,f=0,i=GAME_SIZE-1; i>=0; i--) { if (data[i*GAME_SIZE]) { if (!f && j<GAME_SIZE-1 && data[(j+1)*GAME_SIZE] == data[i*GAME_SIZE]) { data[(j+1)*GAME_SIZE]+= data[i*GAME_SIZE]; f = 1; } else { data[(j--)*GAME_SIZE] = data[i*GAME_SIZE]; f = 0; } } } while (j >= 0) data[(j--)*GAME_SIZE] = 0; data++; } } static int next(int *data){ int empidx[GAME_SIZE*GAME_SIZE]; int empnum = 0; int max = 0; int i,j = 0; for(j=0,i=0; i<GAME_SIZE*GAME_SIZE; i++){ if(!data[i]){ empidx[j++]=i;//记录0格的偏移地址 empnum++;//判断有几格为0 } max = max > data[i] ? max : data[i];//保留最大值判断是否游戏已结束 } if(empnum){ data[empidx[rand()%empnum]] = 1; return max == 2048 ? 1 : 0; } else return -1; } static void output(int *data){//通过判断i是否被4整除来确定是否换行 int i, sum = 0; printf("+--------------------+\n\n"); for (i=1; i<=GAME_SIZE*GAME_SIZE; i++) { if(data[i-1]){ printf("%4d %s", data[i-1], i%GAME_SIZE ? "" : "\n\n"); }else{ printf("%s %s", " .", i%GAME_SIZE ? "" : "\n\n"); } sum+=data[i-1]; } printf("+--------------------+\n\n"); printf("分数:%d\n\n", sum); } int main(){ int data[GAME_SIZE * GAME_SIZE] = {0}; int ret = 0; next(data); next(data); output(data); while(1){ int c; if(c=getch()){ if(c==72||c==75||c==77||c==80) continue;//功能键是两个字节,输入错误则跳过一次 c=getch(); if(c==75) left (data); else if(c==77) right(data); else if(c==72) up (data); else if(c==80) down (data); else if(c=='q') return 0;//q也需要按两次才退出 else continue; } ret = next(data); output(data); if (ret == -1) printf("game over !\n"); if (ret == 1) printf("you win !\n"); } }
我的测试环境为 Dev-C++ 5.11,亲测有效,如果使用linux 下 gcc 编译,需要解决 getch 问题,具体参考原博客
算法主要就是 left right up down 四个函数,这里给出 left 函数的前两位图解
准确的说,i 代表当前遍历格地址, j代表当前填充格地址(如果合并填充j-1格),f为合并标志位(每一行(列)只能合并一次)
如果用getch()捕获上、下、左、右键
上、下、左、右键是二个字节的,getch()只读一个字节,ASC码
想要用getch()得到上、下、左、右键的话,要调用二次getch():
可参考博客:C语言使用getch()读取方向键
getch函数在读取一个功能键或者箭头(方向)键盘时,函数会返回两次,第一次调用返回0或者0xE0,第二次调用返回实际的键值,所以使用两次getch()即可。