用 C 语言编写一个 2048 小游戏🎮
首先,2048游戏的核心在于滑动数字方块,将相同的数字合并,以达到2048这个目标。游戏界面是一个4x4的网格,每个格子可以是空的,或者包含2或4等2的幂的数字。每次移动后,系统会在空格中随机生成一个2或4。
为了用C语言实现这个游戏,我们需要考虑以下几个主要部分:
• 游戏网格的表示:我们需要一个数据结构来表示4x4的网格。可以使用一个二维数组来实现。
• 初始化网格:游戏开始时,网格应该是空的,除了两个随机生成的数字(2或4)。
• 用户输入:玩家需要能够输入移动指令,比如上、下、左、右。
• 移动逻辑:根据玩家的输入,我们需要移动网格中的数字,合并相同的数字,并在移动后生成一个新的数字。
• 游戏结束条件:如果网格满了且无法再进行任何移动,游戏结束。
让我们从定义网格开始。我将创建一个4x4的整数数组来表示网格,并用0表示空格子。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define GRID_SIZE 4
int grid[GRID_SIZE][GRID_SIZE];
void initializeGrid() {
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
grid[i][j] = 0;
}
}
// 随机生成两个数字
srand(time(NULL));
int firstRow = rand() % GRID_SIZE;
int firstCol = rand() % GRID_SIZE;
grid[firstRow][firstCol] = 2;
int secondRow = rand() % GRID_SIZE;
int secondCol = rand() % GRID_SIZE;
while (secondRow == firstRow && secondCol == firstCol) {
secondRow = rand() % GRID_SIZE;
secondCol = rand() % GRID_SIZE;
}
grid[secondRow][secondCol] = 2;
}
void printGrid() {
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
printf("%4d ", grid[i][j]);
}
printf("\n");
}
}
int main() {
initializeGrid();
printGrid();
// 这里需要处理用户输入和游戏逻辑
// 但为了简单起见,我们先在这里结束
return 0;
}
以上代码初始化了一个4x4的网格,并随机放置了两个2。接下来,我们需要处理用户输入和游戏逻辑。这可能需要使用getchar()
或scanf()
来读取玩家的移动指令。为了简化,我们先用getchar()
来获取一个字符输入。
char move;
while (1) {
printf("Enter move (w/a/s/d): ");
scanf(" %c", &move);
getchar(); // 清除缓冲区
switch (move) {
case 'w':
// 向上移动
break;
case 'a':
// 向左移动
break;
case 's':
// 向下移动
break;
case 'd':
// 向右移动
break;
default:
printf("Invalid move.\n");
break;
}
printGrid();
}
接下来,我们需要实现每个方向的移动逻辑。这涉及到遍历网格,合并相同的数字,并在移动后生成一个新的数字。这可能会有些复杂,但我们可以一步一步地实现。
希望这些步骤能帮助您开始用C语言编写2048游戏。如果您需要进一步的帮助或有其他问题,随时告诉我!
处理用户输入:
我打算使用getchar()
来获取用户的移动指令。为此,需要在开头包含<stdio.h>
。让我们修改main
函数,使其能够接收输入:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// ...(其余部分与前面相同)...
int main() {
initializeGrid();
printGrid();
char move;
while(1) {
printf("Enter move (w/a/s/d): ");
move = getchar();
getchar(); // 清除缓冲区
switch(move) {
case 'w':
// 向上移动
break;
case 'a':
// 向左移动
break;
case 's':
// 向下移动
break;
case 'd':
// 向右移动
break;
default:
printf("Invalid move.\n");
break;
}
printGrid();
}
return 0;
}
如此一来,程序会不断提示用户输入一个移动指令。然而,我尚未实现实际的移动逻辑。让我们先从一个方向开始,例如“向上”。
实现向上移动:
当用户按下'w'时,我需要将网格中的数字向上移动。具体步骤如下:
• 从每一列的顶部开始,检查每个数字。
• 如果数字不为零,将其向上移动,直到它碰到另一个数字或到达顶部。
• 如果两个相同的数字碰到一起,将它们合并成一个更大的数字(例如,两个2变成4)。
• 在所有移动完成后,在一个空格中随机生成一个新的数字。
这听起来有些复杂。或许我可以编写一个单独的函数来处理向上移动。
void moveUp() {
for (int col = 0; col < GRID_SIZE; col++) {
// 临时数组,用于存储非零元素
int temp[GRID_SIZE];
int index = 0;
for (int row = 0; row < GRID_SIZE; row++) {
if (grid[row][col] != 0) {
temp[index] = grid[row][col];
index++;
}
}
// 合并相同的数字
for (int i = 0; i < index - 1; i++) {
if (temp[i] == temp[i + 1]) {
temp[i] *= 2;
temp[i + 1] = 0;
}
}
// 更新网格
for (int row = 0; row < GRID_SIZE; row++) {
grid[row][col] = (row < index) ? temp[row] : 0;
}
}
// 在空格中生成一个新的数字
generateNewNumber();
}
稍等,这里似乎存在一个问题。在合并数字时,我应当确保在合并后正确地移动其余的数字。让我再仔细思考一下。
或许我应该遍历临时数组,检查相邻的元素,如果它们相等,就合并它们,并将下一个元素设为零。然后,将合并后的数组重新赋值给网格。
让我再试一次:
void moveUp() {
for (int col = 0; col < GRID_SIZE; col++) {
int temp[GRID_SIZE] = {0};
int index = 0;
for (int row = 0; row < GRID_SIZE; row++) {
if (grid[row][col] != 0) {
temp[index] = grid[row][col];
index++;
}
}
// 合并相同的数字
for (int i = 0; i < index - 1; i++) {
if (temp[i] == temp[i + 1]) {
temp[i] *= 2;
temp[i + 1] = 0;
i++; // 跳过下一个已经合并的数字
}
}
// 更新网格
for (int row = 0; row < GRID_SIZE; row++) {
grid[row][col] = (row < index) ? temp[row] : 0;
}
}
generateNewNumber();
}
这样看起来更加合理了。接下来,我需要实现generateNewNumber
函数,它会在一个随机的空格中生成一个新的2或4。
void generateNewNumber() {
int emptyCells = 0;
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
if (grid[i][j] == 0) {
emptyCells++;
}
}
}
if (emptyCells == 0) {
printf("No empty cells! Game over.\n");
exit(0);
}
int randCell = rand() % emptyCells;
int count = 0;
for (int i = 0; i < GRID_SIZE; i++) {
for (int j = 0; j < GRID_SIZE; j++) {
if (grid[i][j] == 0) {
if (count == randCell) {
grid[i][j] = (rand() % 2 == 0) ? 2 : 4;
return;
}
count++;
}
}
}
}
现在,我已经实现了向上移动和生成新数字的功能。接下来,我需要类似地实现向左、向下和向右移动的功能。这将涉及类似的逻辑,但方向不同。
实现向左移动:
向左移动与向上移动类似,但操作的是行而不是列。
void moveLeft() {
for (int row = 0; row < GRID_SIZE; row++) {
int temp[GRID_SIZE] = {0};
int index = 0;
for (int col = 0; col < GRID_SIZE; col++) {
if (grid[row][col] != 0) {
temp[index] = grid[row][col];
index++;
}
}
// 合并相同的数字
for (int i = 0; i < index - 1; i++) {
if (temp[i] == temp[i + 1]) {
temp[i] *= 2;
temp[i + 1] = 0;
i++; // 跳过下一个已经合并的数字
}
}
// 更新网格
for (int col = 0; col < GRID_SIZE; col++) {
grid[row][col] = (col < index) ? temp[col] : 0;
}
}
generateNewNumber();
}
实现向右移动:
向右移动与向左移动类似,但顺序相反。
void moveRight() {
for (int row = 0; row < GRID_SIZE; row++) {
int temp[GRID_SIZE] = {0};
int index = 0;
for (int col = GRID_SIZE - 1; col >= 0; col--) {
if (grid[row][col] != 0) {
temp[index] = grid[row][col];
index++;
}
}
// 合并相同的数字
for (int i = 0; i < index - 1; i++) {
if (temp[i] == temp[i + 1]) {
temp[i] *= 2;
temp[i + 1] = 0;
i++; // 跳过下一个已经合并的数字
}
}
// 更新网格
for (int col = GRID_SIZE - 1; col >= 0; col--) {
grid[row][col] = (GRID_SIZE - 1 - col < index) ? temp[GRID_SIZE - 1 - col] : 0;
}
}
generateNewNumber();
}
实现向下移动:
向下移动与向上移动类似,但顺序相反。
void moveDown() {
for (int col = 0; col < GRID_SIZE; col++) {
int temp[GRID_SIZE] = {0};
int index = 0;
for (int row = GRID_SIZE - 1; row >= 0; row--) {
if (grid[row][col] != 0) {
temp[index] = grid[row][col];
index++;
}
}
// 合并相同的数字
for (int i = 0; i < index - 1; i++) {
if (temp[i] == temp[i + 1]) {
temp[i] *= 2;
temp[i + 1] = 0;
i++; // 跳过下一个已经合并的数字
}
}
// 更新网格
for (int row = GRID_SIZE - 1; row >= 0; row--) {
grid[row][col] = (GRID_SIZE - 1 - row < index) ? temp[GRID_SIZE - 1 - row] : 0;
}
}
generateNewNumber();
}
现在,我已经实现了所有四个方向的移动。接下来,我需要将这些函数与用户输入关联起来,并处理游戏循环。
完成主函数:
让我们修改main
函数,使其能够根据用户输入调用相应的移动函数。
int main() {
initializeGrid();
printGrid();
char move;
while(1) {
printf("Enter move (w/a/s/d): ");
move = getchar();
getchar(); // 清除缓冲区
switch(move) {
case 'w':
moveUp();
break;
case 'a':
moveLeft();
break;
case 's':
moveDown();
break;
case 'd':
moveRight();
break;
default:
printf("Invalid move.\n");
break;
}
printGrid();
}
return 0;
}
测试游戏:
现在,我应该编译并运行程序,看看是否按预期工作。如果一切顺利,我应该能够输入w
、a
、s
或d
来移动网格中的数字,并在每次移动后看到网格更新。
在测试过程中,可能会遇到一些问题,例如:
• 输入处理:如果getchar()
没有正确读取输入或缓冲区没有正确清除,可能会导致意外行为。我可能需要实现更稳健的输入处理。
• 边界条件:忘记正确处理网格的边界可能会导致数组越界错误。我需要确保所有索引都在0到3之间。
• 合并逻辑:如果合并逻辑没有正确跳过已经合并的数字,可能会导致错误的数字生成。我需要确保合并后正确更新索引。
通过仔细测试和调试,我可以解决这些问题,使游戏更加稳定。
这只是一个2048游戏的基础实现。为了使其更加完整,我可以添加更多功能,如:
• 得分系统:跟踪玩家的得分,每次合并数字时增加得分。
• 游戏结束条件:当没有可能的移动时,显示游戏结束消息。
• 更好的用户界面:使用颜色或更复杂的文本格式使游戏更加美观。
本文系作者 @惊鸿 原创发布在惊鸿博客站点。未经许可,禁止转载。
全部评论 1
惊鸿
Google Chrome Android