C语言小游戏2048数字拼图(含源码)

 一、引言

        C语言作为编程界的经典语言,具有高效、灵活等特点,非常适合进行小游戏开发。本文将带领大家从零开始开发一款经典的2048数字拼图游戏,通过这个项目,你将掌握C语言的基本语法、函数使用、数组操作以及简单的游戏逻辑实现。

二、游戏设计思路

        2048游戏的基本规则是玩家通过方向键移动数字方块,相同数字的方块相撞时会合并成为它们的和,每次移动后会在空白位置随机生成一个新的数字2或4,当界面中出现2048这个数字时游戏胜利,当界面被填满且无法进行有效移动时游戏失败。我们需要设计以下几个核心模块:

- 游戏界面初始化
- 数字方块的移动与合并
- 随机数字生成
- 游戏状态检测
- 游戏状态更新与显示

 三、核心源码实现

下面是2048数字拼图游戏的完整源码实现:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <windows.h>

// 定义游戏区域大小
#define SIZE 4

// 定义方向键值
#define UP 72
#define DOWN 80
#define LEFT 75
#define RIGHT 77
#define ESC 27

// 定义游戏状态
enum Status {
    RUNNING,
    WON,
    LOST,
    EXIT
};

// 游戏的结构体
typedef struct {
    int board[SIZE][SIZE];  // 游戏面板
    int score;              // 分数
    enum Status status;     // 游戏状态
} Game;

// 函数声明
void initGame(Game *game);
void drawGame(Game *game);
void moveBoard(Game *game, int direction);
int canMove(Game *game, int direction);
void generateNewNumber(Game *game);
int checkWin(Game *game);
int checkLose(Game *game);
void setCursorPosition(int x, int y);
void hideCursor();

// 初始化游戏
void initGame(Game *game) {
    // 初始化游戏面板
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            game->board[i][j] = 0;
        }
    }
    
    // 初始化分数
    game->score = 0;
    
    // 初始化游戏状态
    game->status = RUNNING;
    
    // 随机生成两个初始数字
    generateNewNumber(game);
    generateNewNumber(game);
    
    // 隐藏光标
    hideCursor();
}

// 绘制游戏界面
void drawGame(Game *game) {
    system("cls");  // 清屏
    
    // 显示游戏标题
    printf("\n\n\t\t2048 Puzzle Game\n\n");
    
    // 显示分数
    printf("\t\tScore: %d\n\n", game->score);
    
    // 绘制游戏面板边框
    printf("\t\t");
    for (int i = 0; i < SIZE; i++) {
        printf("--------");
    }
    printf("-\n");
    
    // 绘制游戏面板
    for (int i = 0; i < SIZE; i++) {
        printf("\t\t");
        for (int j = 0; j < SIZE; j++) {
            if (game->board[i][j] == 0) {
                printf("|      ");
            } else {
                printf("|%6d", game->board[i][j]);
            }
        }
        printf("|\n");
        
        // 绘制分隔线
        printf("\t\t");
        for (int j = 0; j < SIZE; j++) {
            printf("--------");
        }
        printf("-\n");
    }
    
    // 显示游戏提示
    printf("\n\t\tUse arrow keys to move. Press ESC to exit.\n");
    
    // 显示游戏状态
    if (game->status == WON) {
        printf("\n\t\tCongratulations! You won!\n");
    } else if (game->status == LOST) {
        printf("\n\t\tGame Over! You lost.\n");
    }
}

// 移动游戏面板
void moveBoard(Game *game, int direction) {
    int moved = 0;
    int merged[SIZE][SIZE] = {0};  // 记录每个位置是否已经合并过
    
    // 根据方向移动
    switch (direction) {
        case UP:
            for (int j = 0; j < SIZE; j++) {
                for (int i = 1; i < SIZE; i++) {
                    if (game->board[i][j] != 0) {
                        int newPos = i;
                        // 找到可以移动到的位置
                        while (newPos > 0 && game->board[newPos-1][j] == 0) {
                            newPos--;
                        }
                        // 检查是否可以合并
                        if (newPos > 0 && game->board[newPos-1][j] == game->board[i][j] && !merged[newPos-1][j]) {
                            game->board[newPos-1][j] *= 2;
                            game->score += game->board[newPos-1][j];
                            game->board[i][j] = 0;
                            merged[newPos-1][j] = 1;
                            moved = 1;
                        } 
                        // 移动到新位置
                        else if (newPos != i) {
                            game->board[newPos][j] = game->board[i][j];
                            game->board[i][j] = 0;
                            moved = 1;
                        }
                    }
                }
            }
            break;
            
        case DOWN:
            for (int j = 0; j < SIZE; j++) {
                for (int i = SIZE-2; i >= 0; i--) {
                    if (game->board[i][j] != 0) {
                        int newPos = i;
                        // 找到可以移动到的位置
                        while (newPos < SIZE-1 && game->board[newPos+1][j] == 0) {
                            newPos++;
                        }
                        // 检查是否可以合并
                        if (newPos < SIZE-1 && game->board[newPos+1][j] == game->board[i][j] && !merged[newPos+1][j]) {
                            game->board[newPos+1][j] *= 2;
                            game->score += game->board[newPos+1][j];
                            game->board[i][j] = 0;
                            merged[newPos+1][j] = 1;
                            moved = 1;
                        } 
                        // 移动到新位置
                        else if (newPos != i) {
                            game->board[newPos][j] = game->board[i][j];
                            game->board[i][j] = 0;
                            moved = 1;
                        }
                    }
                }
            }
            break;
            
        case LEFT:
            for (int i = 0; i < SIZE; i++) {
                for (int j = 1; j < SIZE; j++) {
                    if (game->board[i][j] != 0) {
                        int newPos = j;
                        // 找到可以移动到的位置
                        while (newPos > 0 && game->board[i][newPos-1] == 0) {
                            newPos--;
                        }
                        // 检查是否可以合并
                        if (newPos > 0 && game->board[i][newPos-1] == game->board[i][j] && !merged[i][newPos-1]) {
                            game->board[i][newPos-1] *= 2;
                            game->score += game->board[i][newPos-1];
                            game->board[i][j] = 0;
                            merged[i][newPos-1] = 1;
                            moved = 1;
                        } 
                        // 移动到新位置
                        else if (newPos != j) {
                            game->board[i][newPos] = game->board[i][j];
                            game->board[i][j] = 0;
                            moved = 1;
                        }
                    }
                }
            }
            break;
            
        case RIGHT:
            for (int i = 0; i < SIZE; i++) {
                for (int j = SIZE-2; j >= 0; j--) {
                    if (game->board[i][j] != 0) {
                        int newPos = j;
                        // 找到可以移动到的位置
                        while (newPos < SIZE-1 && game->board[i][newPos+1] == 0) {
                            newPos++;
                        }
                        // 检查是否可以合并
                        if (newPos < SIZE-1 && game->board[i][newPos+1] == game->board[i][j] && !merged[i][newPos+1]) {
                            game->board[i][newPos+1] *= 2;
                            game->score += game->board[i][newPos+1];
                            game->board[i][j] = 0;
                            merged[i][newPos+1] = 1;
                            moved = 1;
                        } 
                        // 移动到新位置
                        else if (newPos != j) {
                            game->board[i][newPos] = game->board[i][j];
                            game->board[i][j] = 0;
                            moved = 1;
                        }
                    }
                }
            }
            break;
    }
    
    // 如果有移动,生成新数字并检查游戏状态
    if (moved) {
        generateNewNumber(game);
        
        if (checkWin(game)) {
            game->status = WON;
        } else if (checkLose(game)) {
            game->status = LOST;
        }
    }
}

// 检查是否可以向某个方向移动
int canMove(Game *game, int direction) {
    // 创建游戏面板副本
    int copy[SIZE][SIZE];
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            copy[i][j] = game->board[i][j];
        }
    }
    
    // 模拟移动
    moveBoard(game, direction);
    
    // 检查是否有变化
    int changed = 0;
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (copy[i][j] != game->board[i][j]) {
                changed = 1;
            }
            // 恢复原游戏面板
            game->board[i][j] = copy[i][j];
        }
    }
    
    return changed;
}

// 随机生成一个新数字(2或4)
void generateNewNumber(Game *game) {
    // 找出所有空白位置
    int emptyCells[SIZE*SIZE][2];
    int count = 0;
    
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (game->board[i][j] == 0) {
                emptyCells[count][0] = i;
                emptyCells[count][1] = j;
                count++;
            }
        }
    }
    
    if (count > 0) {
        // 随机选择一个空白位置
        srand(time(NULL));
        int index = rand() % count;
        int x = emptyCells[index][0];
        int y = emptyCells[index][1];
        
        // 90%概率生成2,10%概率生成4
        int value = (rand() % 10 < 9) ? 2 : 4;
        game->board[x][y] = value;
    }
}

// 检查是否获胜
int checkWin(Game *game) {
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (game->board[i][j] >= 2048) {
                return 1;
            }
        }
    }
    return 0;
}

// 检查是否失败
int checkLose(Game *game) {
    // 检查是否还有空白位置
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            if (game->board[i][j] == 0) {
                return 0;
            }
        }
    }
    
    // 检查是否还能向任何方向移动
    if (canMove(game, UP) || canMove(game, DOWN) || canMove(game, LEFT) || canMove(game, RIGHT)) {
        return 0;
    }
    
    return 1;
}

// 设置光标位置
void setCursorPosition(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

// 隐藏光标
void hideCursor() {
    HANDLE hOut;
    CONSOLE_CURSOR_INFO ConCurInf;
    
    hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    
    ConCurInf.dwSize = 10;
    ConCurInf.bVisible = FALSE;
    
    SetConsoleCursorInfo(hOut, &ConCurInf);
}

int main() {
    Game game;
    initGame(&game);
    
    // 游戏主循环
    while (game.status == RUNNING) {
        drawGame(&game);
        
        // 获取用户输入
        int key = _getch();
        
        // 处理方向键
        switch (key) {
            case UP:
            case DOWN:
            case LEFT:
            case RIGHT:
                moveBoard(&game, key);
                break;
            case ESC:
                game.status = EXIT;
                break;
        }
    }
    
    // 游戏结束后显示最终状态
    drawGame(&game);
    
    // 等待用户按键退出
    _getch();
    
    return 0;
}


四、源码解析

1. 数据结构设计

        我们使用一个结构体来组织游戏数据:

        - `Game` 结构体:存储游戏面板、分数和游戏状态

        这种设计使代码结构清晰,便于维护和扩展。

2. 核心功能模块

初始化模块

        `initGame` 函数负责游戏的初始化工作,包括设置游戏面板、初始化分数、游戏状态等,并生成两个初始数字。

绘制模块

        `drawGame` 函数负责绘制整个游戏界面,包括边框、数字方块、分数信息和游戏状态。使用 `system("cls")` 清屏实现动态效果。

移动与合并模块

        `moveBoard` 函数实现数字方块的移动和合并逻辑,根据不同方向键处理不同的移动逻辑,并处理合并情况。

随机数字生成模块

        `generateNewNumber` 函数随机生成数字2或4,并将其放置在空白位置上。

游戏状态检测模块

        `checkWin` 和 `checkLose` 函数分别检查游戏是否获胜或失败。

3. 游戏控制

        在 `main` 函数的游戏主循环中,使用 `_getch()` 函数检测用户按键输入,实现对游戏的控制。

五、游戏优化与扩展建议

1. **增加撤销功能**:允许玩家撤销上一步操作。
2. **添加颜色显示**:根据数字大小显示不同颜色,提升视觉体验。
3. **保存最高分**:使用文件操作保存历史最高分。
4. **添加动画效果**:在数字移动和合并时添加动画效果。

六、总结

        通过这个2048数字拼图游戏项目,我们学习了C语言的基本语法、函数使用、结构体和数组操作等知识。游戏开发不仅能够帮助我们巩固编程基础,还能培养逻辑思维和问题解决能力。希望本文能为你打开游戏开发的大门,激发你更多的编程创意!

如果你对代码有任何疑问或建议,欢迎在评论区留言讨论!
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洛希极限(25届)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值