前期准备:
使用软件:vscode
使用函数库:ncurses
安装ncurses 绘图库
sudo apt-get install libncurses5-dev
设计思路
要实现 2048 游戏目前有两个关键点:
- 在满足条件情况下消除方块
- 允许在游戏主界面(16 宫格)中任意一格输出数据
其中第二点借助 ncurses 库可以较容易实现,但是第一点要稍微麻烦些。
第一点的实现思路是,我们创建一个与游戏地图相同维数的数组矩阵,通过数组矩阵来维护 2048 游戏中每个格子的数据与状态,从而玩家的移动操作都可以映射为对数组矩阵的操作。 
绘制窗格
添加需要的头文件和全局变量
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <unistd.h>
#include <time.h>
void init(void);
void draw_grid(void);
void draw_one(int x, int y);
void play(void);
int cnt_one(int x, int y);
void cnt_value(int* new_x,int* new_y);
int game_over(void);
int board[4][4] = {0};
int empty;
int old_x, old_y;
绘制5行横线,每行由21个- 组成;绘制5列竖线,每列由7个| 组成;
void draw_grid(void){
int m, n, x, y;
clear();
for(m = 0; m < 9; m += 2){
for(n = 0; n < 21; n++){
move(m,n);
addch('-');
refresh();
}
}
for (n = 0; n < 22; n += 5){
for(m = 1; m < 8; m++){
move(m,n);
addch('|');
refresh();
}
}
for (x = 0; x < 4; x++){
for(y = 0; y < 4; y++)
draw_one(x, y);
}
}

void draw_one(int x, int y){
int i, k, j, m=0;
char c[5] = {0x00};
i = board[x][y];
while(i > 0){
j = i % 10;
c[m++] = j + '0';
i = i / 10;
}
m=0;
k=(y + 1) * 5 - 1;
while(c[m] != 0x00){
move(2*x+1, k);
addch(c[m++]);
k--;
}
}
void init(void){
int x, y;
initscr();
cbreak();
noecho();
curs_set(0);
empty = 15;
srand(time(0));
x = rand() % 4;
y = rand() % 4;
board[x][y] = 2;
draw_grid();
}
效果图如下: 
核心函数
然后来看看游戏运行的核心 play 函数。通过 W, S, A, D 和方向键来分别控制上、下、左、右方向的移动,按 Q 键可以退出界面。(先分析出一个方向的逻辑,其余的类似)
void play(void){
int x, y, i, new_x, new_y,temp;
int old_empty, move;
while(1){
move = 0;
old_empty = empty;
switch(getch()){
case 'a':
case 68://左移方向键
for(x = 0; x < 4; x++){
for(y = 0; y < 4;){
if(board[x][y] == 0){
y++;
continue;
}
else{
for(i = y+1; i < 4; i++){
if(board[x][i] == 0)
continue;
else{
if(board[x][y] == board[x][i]){
board[x][y] += board[x][i];
board[x][i] = 0;
empty++;
break;
}
else
break;
}
}
y = i;
}
}
}
for(x = 0; x < 4; x++){
for(y = 0; y < 4; y++){
if(board[x][y] == 0)
continue;
else
for(i = y; (i > 0) && (board[x][i-1] == 0); i--){
board[x][i-1] = board[x][i];
board[x][i] = 0;
move = 1;
}
}
}
break;
case 'd':
case 67://右移方向键
for(x = 0; x < 4; x++){
for(y = 3; y >= 0; ){
if(board[x][y] == 0){
y--;
continue;
}
else{
for(i = y - 1; i >= 0; i--){
if(board[x][i] == 0)
continue;
else if(board[x][y] == board[x][i]){
board[x][y] += board[x][i];
board[x][i] = 0;
empty++;
break;
}
else
break;
}
y = i;
}
}
}
for(x = 0; x < 4; x++){
for(y = 3; y >= 0; y--){
if(board[x][y] == 0)
continue;
else
for(i = y; (i < 3) && (board[x][i+1] == 0); i++){
board[x][i+1] = board[x][i];
board[x][i] = 0;
move = 1;
}
}
}
break;
case 'w':
case 65://上移方向键
for(y = 0; y < 4; y++){
for(x = 0; x < 4; ){
if(board[x][y] == 0){
x++;
continue;
}
else{
for(i = x + 1; i < 4; i++){
if(board[i][y] == 0)
continue;
else if(board[x][y] == board[i][y]){
board[x][y] += board[i][y];
board[i][y] = 0;
empty++;
break;
}
else
break;
}
x = i;
}
}
}
for(y = 0; y < 4; y++){
for(x = 0; x < 4; x++){
if(board[x][y] == 0)
continue;
else
for(i = x; (i > 0) && (board[i-1][y] == 0); i--){
board[i-1][y] = board[i][y];
board[i][y] = 0;
move = 1;
}
}
}
break;
case 's':
case 66://下移方向键
for(y = 0; y < 4; y++){
for(x = 3; x >= 0; ){
if(board[x][y] == 0){
x--;
continue;
}
else{
for(i = x - 1; i >= 0; i--){
if(board[i][y] == 0)
continue;
else if(board[x][y] == board[i][y]){
board[x][y] += board[i][y];
board[i][y] = 0;
empty++;
break;
}
else
break;
}
x = i;
}
}
}
for(y = 0; y < 4; y++){
for(x = 3; x >= 0; x--){
if(board[x][y] == 0)
continue;
else
for(i = x; (i < 3) && (board[i+1][y] == 0); i++){
board[i+1][y] = board[i][y];
board[i][y] = 0;
move = 1;
}
}
}
break;
case 'Q':
case 'q':
game_over();
break;
default:
continue;
break;
}
if(empty <= 0)
game_over();
if((empty != old_empty) || (move == 1)){
do{
new_x = rand() % 4;
new_y = rand() % 4;
}while(board[new_x][new_y] != 0);
cnt_value(&new_x, &new_y);
do {
temp = rand() % 4;
}while(temp == 0 || temp == 2);
board[new_x][new_y] = temp + 1;
empty--;
}
draw_grid();
}
}
剩余部分
int cnt_one(int x, int y){
int value = 0;
if(x - 1 >= 0)
board[x - 1][y] ? 0 : value++;
if(x + 1 < 4)
board[x + 1][y] ? 0 : value++;
if(y- 1 >= 0)
board[x][y - 1] ? 0 : value++;
if(y + 1 < 4)
board[x][y + 1] ? 0 : value++;
if(x - 1 >= 0 && y - 1 >= 0)
board[x - 1][y - 1] ? 0 : value++;
if(x - 1 >= 0 && y + 1 < 4)
board[x - 1][y +1] ? 0 : value++;
if(x + 1 < 4 && y - 1 >= 0)
board[x + 1][y - 1] ? 0 : value++;
if(x + 1 < 4 && y + 1 < 4)
board[x + 1][y + 1] ? 0 : value++;
return value;
}
void cnt_value(int* new_x,int* new_y){
int max_x, max_y, x, y, value;
int max = 0;
max = cnt_one(*new_x, *new_y);
for(x = 0; x < 4; x++){
for(y = 0; y < 4; y++){
if(!board[x][y]){
value = cnt_one(x, y);
if(value > max && old_x != x && old_y != y){
*new_x = x;
*new_y = y;
old_x = x;
old_y = y;
}
}
}
}
int game_over(void){
move(10, 5);
addstr("game over!");
refresh();
sleep(3);
endwin();
exit(0);
}
main函数
int main(){
init();
play();
endwin();
return 0;
}
编译
#编译 gcc 2048.c -o 2048 -lcurses
#执行 ./2048
ncurses 库使用参考文章:
Linux curses库使用
Linux下curses函数库
代码参考:
C 语言实现 2048 游戏
|