C语言代码实现简单的扫雷小游戏

C语言代码实现简单的扫雷小游戏

一、游戏规则

扫雷是一款经典的单人益智小游戏,游戏场景是一个区块是由许多个格子组成的矩形网格,有一部分格子下面隐藏着地雷,玩家通过揭露不带雷的部分,最终找到所有地雷的位置。

具体游戏规则:

  1. 鼠标左键点开或标记可疑格子。
  2. 若点击的是地雷,则游戏结束,显示所有地雷的位置。
  3. 若点击的是数字,则显示周边8个格子中地雷的数量。
  4. 若点击的是空格,则显示周边8个格子是否带雷,若为空格则可以扩散一定范围。
  5. 当所有空格(不带雷的格子)都被打开时,成功过关。

二、代码设计

1. 生成游戏区块

使用二维数组来生成游戏区块,-1表示该位置存在雷,其他数字表示该位置周边8个格子中雷的数量。

int block[ROW][COLUMN]; // 游戏区块
int show[ROW][COLUMN];  // 展示区块

void init_block()
{
    int i, j, k;
    memset(block, 0, sizeof(block));
    memset(show, 0, sizeof(show));
    srand((unsigned)time(NULL));
    for (k = 0; k < MINE_NUM; k++) { // MINE_NUM 表示雷的数量
        i = rand() % ROW;
        j = rand() % COLUMN;
        if (block[i][j] == -1) {
            k--;
            continue;
        }
        block[i][j] = -1;
        for (int h = -1; h <= 1; h++) {
            for (int w = -1; w <= 1; w++) {
                if (i + h >= 0 && i + h < ROW && j + w >= 0 && j + w < COLUMN && block[i+h][j+w] != -1) {
                    block[i+h][j+w]++;
                }
            }
        }
    }
}

2. 游戏的初始化

void init_game() {
    int x, y;
    init_block(); // 初始化游戏区块
    for (y = 0; y < ROW; y++) { // ROW 表示行数
        for (x = 0; x < COLUMN; x++) { // COLUMN 表示列数
            // 设置每个格子的默认状态为未点击(idx=0),未标记(idx=1)
            opened[y][x] = 0;
            marked[y][x] = 0;
        }
    }
}

3. 游戏的主循环

该部分使用SDL库实现,包含初始化、事件循环、图像渲染等多个小模块,需要结合完整代码查看。

while (1) {
    // 事件循环
    while (SDL_PollEvent(&event)) {
        // 处理鼠标事件
        switch (event.type) {
            case SDL_MOUSEBUTTONDOWN:
                if (event.button.button == SDL_BUTTON_LEFT) {
                    click_left_down(event);
                }
                else if (event.button.button == SDL_BUTTON_RIGHT) {
                    click_right_down(event);
                }
                break;
            case SDL_MOUSEBUTTONUP:
                if (event.button.button == SDL_BUTTON_LEFT) {
                    click_left_up(event);
                }
                else if (event.button.button == SDL_BUTTON_RIGHT) {
                    click_right_up(event);
                }
                break;
            case SDL_QUIT:
                quit();
            default:
                break;
        }
    }

    // 渲染游戏区块
    draw_game();
}

4. 点击事件的处理

void click_left_down(SDL_Event event)
{
    int x = event.button.x / CSIZE;
    int y = event.button.y / CSIZE;
    if (opened[y][x] == 0 && marked[y][x] == 0) {
        if (block[y][x] == -1) {
            show_all_mines();
            printf("Game Over!\n");
            game_over = 1;
        }
        else if (block[y][x] > 0) {
            show[y][x] = 1;
            opened[y][x] = 1;
        }
        else {
            open_area(y, x);
        }
    }
}

void click_left_up(SDL_Event event)
{
    // do nothing
}

void click_right_down(SDL_Event event)
{
    int x = event.button.x / CSIZE;
    int y = event.button.y / CSIZE;
    if (opened[y][x] == 0) {
        marked[y][x] = !marked[y][x];
    }
}

void click_right_up(SDL_Event event)
{
    // do nothing
}

5. 扩散函数的实现

使用递归实现,对于空白格子,向周围的8个格子扩散。若周围的格子没有地雷,则再次扩散。

void open_area(int y, int x)
{
    if (opened[y][x]) {
        return;
    }
    show[y][x] = 1;
    opened[y][x] = 1;
    if (block[y][x] > 0) {
        return;
    }
    // 扩散
    for (int h = -1; h <= 1; h++) {
        for (int w = -1; w <= 1; w++) {
            if (y + h >= 0 && y + h < ROW && x + w >= 0 && x + w < COLUMN) {
                if (block[y+h][x+w] == 0 && !opened[y+h][x+w]) {
                    open_area(y+h, x+w);
                }
                else {
                    show[y+h][x+w] = 1;
                    opened[y+h][x+w] = 1;
                }
            }
        }
    }
}

三、示例说明

示例一

void init_block()
{
    int i, j;
    memset(block, 0, sizeof(block));
    srand((unsigned)time(NULL));
    for (i = 0; i < ROW; i++) {
        for (j = 0; j < COLUMN; j++) {
            // 设置每个格子的默认状态为未点击(idx=0),未标记(idx=1)
            opened[y][x] = 0;
            marked[y][x] = 0;
            if (rand() % (ROW*COLUMN) <= MINE_NUM) { // MINE_NUM 表示雷的数量
                block[i][j] = -1;
            }
            else {
                block[i][j] = 0;
            }
        }
    }
}

该部分的修改增加了一定的随机性,不再是预先固定的雷的位置。随着雷的数量的增加,用户的运气也将会对游戏过程产生影响。

示例二

void open_area(int y, int x)
{
    if (opened[y][x] || marked[y][x]) {
        return;
    }
    show[y][x] = 1;
    opened[y][x] = 1;
    if (block[y][x] > 0) {
        return;
    }
    // 扩散
    for (int h = -1; h <= 1; h++) {
        for (int w = -1; w <= 1; w++) {
            if (y + h >= 0 && y + h < ROW && x + w >= 0 && x + w < COLUMN) {
                if (block[y+h][x+w] == 0) {
                    open_area(y+h, x+w);
                }
                else if (block[y+h][x+w] > 0) {
                    show[y+h][x+w] = 1;
                    opened[y+h][x+w] = 1;
                }
            }
        }
    }
}

该部分的修改是调整了代码的逻辑结构,将相似的else if语句合并,提高了代码的可读性并减少了重复代码的编写。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言代码实现简单的扫雷小游戏 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • C++ 类this及返回自身对象的引用方式

    C++ 类this及返回自身对象的引用方式 this指针 每个非静态成员函数都有一个隐含的形参,即指向该类对象的指针。这个指针就是this指针。通过this指针,我们可以访问到类的所有成员变量和成员函数。 在C++中,关键字this用来指向当前对象。this指针是一个隐式参数,它在成员函数内部使用。 返回自身对象的引用 在C++中,返回自身对象的引用是一种常…

    C 2023年5月22日
    00
  • C++类结构体与json相互转换

    当我们面对需要将C++类与结构体转换成json数据的需求时,最常见的方法是使用第三方库,例如jsoncpp、rapidjson等。在这里,我将为大家介绍一种简单易用的方法。它结合了C++11的特性和STL容器,使得代码简洁易读,同时具备高效性能。 步骤一:定义C++类结构体 首先,我们需要定义一个C++类或者结构体,来描述我们要转换为json数据的对象。例如…

    C 2023年5月22日
    00
  • C语言实现分治法实例

    C语言实现分治法实例 分治法(Divide and Conquer)是一种处理问题的思想,它的基本思路是:将一个复杂的问题分成两个或更多的子问题,对每一个子问题进行解决,然后将子问题的解合并得到原问题的解。 在C语言中,实现分治法可以通过使用递归函数来实现。 分治法基本思路 分治法基本思路如下: 分解(Divide): 将问题划分成一些子问题,子问题的形式与…

    C 2023年5月23日
    00
  • C++中临时对象的常见产生情况及其解决的方案

    C++中的临时对象,通常表示一些临时生成的对象,这些对象没有名字,在表达式的计算中会被创建和销毁。临时对象经常出现在以下情况中: 函数返回局部对象 函数参数以值传递方式传递 使用运算符等生成的新对象 下面分别对这三种情况进行详细介绍: 函数返回局部对象 如果在函数中定义了一个对象并将其作为返回值返回,则该对象就是一个局部对象。由于该对象是由函数定义的,因此它…

    C 2023年5月22日
    00
  • 邻接表无向图的Java语言实现完整源码

    如果要实现一个邻接表无向图的Java程序,需要进行以下几个步骤: 1. 定义节点类 首先定义一个节点类来存储图中的每个节点以及它们之间的关系(边): class Node { int label; // 节点编号 List<Node> edges = new LinkedList<>(); // 存储与该节点相连的边 Node(int…

    C 2023年5月22日
    00
  • C#多线程异步执行和跨线程访问控件Helper

    关于C#多线程异步执行和跨线程访问控件Helper,我会分为以下几个部分进行讲解: 什么是多线程异步执行和跨线程访问控件 为什么需要多线程异步执行和跨线程访问控件 实现多线程异步执行和跨线程访问控件的方法 示例说明:多线程异步执行 示例说明:跨线程访问控件Helper 什么是多线程异步执行和跨线程访问控件 多线程异步执行是指在执行过程中,可以有多个线程同时进…

    C 2023年5月22日
    00
  • C语言中如何进行代码保护?

    在C语言中,代码保护是指采取一系列措施,防止不合法的访问和修改程序,以提高程序的安全性和可靠性。下面是C语言中进行代码保护的一些常用方法: 1.使用共享库 将程序中一些常见的函数封装成共享库,能够有效地避免代码被恶意篡改的问题,同时还可以使程序占用更少的内存空间。在Linux下,使用共享库非常方便,只需要将程序中用到的函数的原型声明在头文件中,然后将共享库链…

    C 2023年4月27日
    00
  • Linux环境使用g++编译C++方法总结

    关于“Linux环境使用g++编译C++方法总结”的攻略,我们可以按照以下步骤进行: 一、安装g++ 首先需要在Linux环境中安装g++,g++是GNU C++编译器的套件,也是GNU Compiler Collection(GCC)的一部分。安装方法如下: 1. 使用apt-get安装 运行以下命令安装g++: sudo apt-get update s…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部