【无聊系列】C实现类似2048的游戏
2023-08-03 12:03:34

0. 引子

无聊是第一生产力。实现一下以前玩过的一版修改版的2048,正好测试一下codeium工具的效果。

Just for fun:)

1. 基本思想

2048的定义是这样的:

该游戏使用方向键让方块整体上下左右移动。如果两个带有相同数字的方块在移动中碰撞,则它们会合并为一个方块,且所带数字变为两者之和。每次移动时,会有一个值为2或者4的新方块出现,所出現的數字都是2的冪。

当值为2048的方块出现时,游戏即胜利,该游戏因此得名。

移动

对于移动的实现,我的想法大概是“拆分-合并-补位”,比如让方块向右移动:

  1. 拆分:向右移动的过程中,行与行之间相互独立,只需要实现单个行上的移动操作即可。
  2. 合并:从左往右合并方块。对于2 2 4 8这样的行,原版的2048会合并成0 4 4 8,我的代码将之合并成0 0 0 16,玩起来快一些。
  3. 补位:将方块都紧密地置于一边,即让4 0 8 16变成 0 4 8 16。

移动的代码大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void RowMoveRight(int map[map_size][map_size], int index) {
for(int i = 1; i < map_size; i++) {
if(map[index][i] == map[index][i-1]) {
map[index][i] = 2 * map[index][i];
map[index][i-1] = 0;
} else if(map[index][i] == 0) {
map[index][i] = map[index][i-1];
map[index][i-1] = 0;
}
}

for(int i = 1; i < map_size; i++) {
if(map[index][i] == 0) {
map[index][i] = map[index][i-1];
map[index][i-1] = 0;
}
}
}

void MoveRight(int map[map_size][map_size]) {
for(int i = 0; i < map_size; i++) {
RowMoveRight(map, i);
}
GenerateTwo(map);
}

输赢的判断

如果出现了任意一个2048方块,即为赢。大概的逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
int GameSuccess(int map[map_size][map_size])
{
for(int row = 0; row < map_size; row++) {
for(int col = 0; col < map_size; col++) {
if(map[row][col] == 2048) {
return success;
}
}
}
return not_success;
}

若每个方块的值,都不等于右侧方块和下侧方块的值,并且,地图被占满,就是输。codeium生成的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
int GameFailed(int map[map_size][map_size])
{
for(int i = 0; i < map_size - 1; i++) {
for(int j = 0; j < map_size - 1; j++) {
if(map[i][j] == 0) {
return not_failed;
} else if(map[i][j] == map[i+1][j] || map[i][j] == map[i][j+1]) {
return not_failed;
}
}
}
return failed;
}

挺对的,哈?No!对于最后一行来说,没法判断方块的值是否等于其右侧方块的值。所以要分开来比较。

输出相关

首先要输出的是地图、移动次数和分数,分数的计算规则很简单,2和2拼出4得4分。

接着,彩色输出方块里数字的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#define ANSI_COLOR_RED     "\x1b[31m"
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_YELLOW "\x1b[33m"
#define ANSI_COLOR_BLUE "\x1b[34m"
#define ANSI_COLOR_MAGENTA "\x1b[35m"
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"

void OutputDigit(int digit) {
printf("|");
if(digit == 0) {
printf("%6d", digit);
return;
} else if(digit <= 4) {
printf(ANSI_COLOR_RED);
} else if(digit <= 8) {
printf(ANSI_COLOR_GREEN);
} else if(digit <= 16) {
printf(ANSI_COLOR_MAGENTA);
} else if(digit <= 256) {
printf(ANSI_COLOR_YELLOW);
} else if(digit <= 512) {
printf(ANSI_COLOR_CYAN);
}else {
printf(ANSI_COLOR_BLUE);
}
printf("%6d", digit);

printf(ANSI_COLOR_RESET);
}

输出大概长这样:

在随便整个主菜单:

简陋版2048就做好了。