【C语言项目实践】扫雷游戏—课程设计&&附加代码

文章目录

  • 一、扫雷游戏概述
    • 1.1 游戏基本规则
    • 1.2 游戏功能设计
  • 二、游戏数据结构设计
    • 2.1 棋盘表示
    • 2.2 数据初始化
  • 三、核心功能实现
    • 3.1 多文件组织
    • 3.2 游戏菜单实现
    • 3.3 棋盘初始化
    • 3.4 打印棋盘
    • 3.5 随机布置地雷
    • 3.6 统计周围地雷数量
    • 3.7 排查地雷
  • 四、游戏主流程
    • 4.1 游戏主循环
    • 4.2 主函数
  • 五、游戏扩展功能
    • 5.1 多难度级别
    • 5.2 递归展开空白区域
    • 5.3 标记地雷功能
  • 六、完整代码实现
    • 6.1 game.h 头文件
    • 6.2 game.c 游戏逻辑实现
    • 6.3 test.c 主程序

扫雷游戏是计算机编程入门的经典实践项目,它能够很好地锻炼编程思维和C语言基础知识的应用能力。本文将详细介绍如何使用C语言实现一个控制台版的扫雷游戏,涵盖游戏设计思路、核心算法和完整代码实现。

一、扫雷游戏概述

1.1 游戏基本规则

扫雷游戏的核心规则包括:

1.游戏区域由若干方格组成的矩形网格

2.部分方格随机布置有地雷

3.玩家点击方格(输入坐标)来"揭开"方格

4.如果揭开的是地雷,游戏失败

5.如果不是地雷,则显示周围8个方格中的地雷数量

6.玩家需要根据数字提示,推理出所有地雷的位置

【C语言项目实践】扫雷游戏—课程设计&&附加代码_第1张图片
【C语言项目实践】扫雷游戏—课程设计&&附加代码_第2张图片

1.2 游戏功能设计

我们将实现以下功能:

9×9的游戏棋盘,随机布置10个地雷

显示当前游戏状态和剩余未排查的非雷区域

支持多次游戏和退出选项

简单的用户界面和错误处理

【C语言项目实践】扫雷游戏—课程设计&&附加代码_第3张图片

二、游戏数据结构设计

2.1 棋盘表示

为了高效存储和处理游戏数据,我们使用两个二维数组:

#define ROW 9    // 实际游戏行数
#define COL 9    // 实际游戏列数
#define ROWS ROW+2  // 包含边界的行数
#define COLS COL+2  // 包含边界的列数

char mine[ROWS][COLS];  // 存储地雷位置 ('1'表示雷,'0'表示非雷)
char show[ROWS][COLS];  // 存储显示给玩家的信息

为什么使用11×11的数组?

实际游戏区域为9×9

外围增加一圈边界,避免在检查边缘格子时数组越界

2.2 数据初始化

初始化两个棋盘:

mine数组初始化为全'0',然后随机布置'1'表示地雷

show数组初始化为全'*',表示未揭开的状态

三、核心功能实现

3.1 多文件组织

采用模块化设计,将代码分为三个文件:

game.h:包含头文件引用、宏定义和函数声明

game.c:游戏核心功能的实现

test.c:游戏主流程和用户界面

3.2 游戏菜单实现

void menu()
{
    printf("******************************\n");
    printf("********     1. play   *******\n");
    printf("********     0. exit   *******\n");
    printf("******************************\n");
}

3.3 棋盘初始化

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
    for(int i = 0; i < rows; i++) {
        for(int j = 0; j < cols; j++) {
            board[i][j] = set;
        }
    }
}

3.4 打印棋盘

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
    printf("--------扫雷--------\n");
    // 打印列号
    for(int i = 0; i <= col; i++) {
        printf("%d ", i);
    }
    printf("\n");
    
    // 打印行号和棋盘内容
    for(int i = 1; i <= row; i++) {
        printf("%d ", i);
        for(int j = 1; j <= col; j++) {
            printf("%c ", board[i][j]);
        }
        printf("\n");
    }
    printf("--------扫雷--------\n");
}

3.5 随机布置地雷

void SetMine(char board[ROWS][COLS], int row, int col)
{
    int count = EASY_COUNT;  // 10个雷
    while(count) {
        int x = rand() % row + 1;  // 1-9
        int y = rand() % col + 1;  // 1-9
        if(board[x][y] != '1') {
            board[x][y] = '1';
            count--;
        }
    }
}

3.6 统计周围地雷数量

int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
    // 周围8个格子的值相加后减去8个'0',得到雷的数量
    return (mine[x-1][y] + mine[x-1][y-1] + mine[x][y-1] + 
            mine[x+1][y-1] + mine[x+1][y] + mine[x+1][y+1] + 
            mine[x][y+1] + mine[x-1][y+1] - 8 * '0');
}

3.7 排查地雷

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    int x, y;
    int win = 0;  // 已排查的安全格子数
    
    while(win < row * col - EASY_COUNT) {
        printf("请输入要排查的坐标:");
        scanf("%d %d", &x, &y);
        
        if(x >= 1 && x <= row && y >= 1 && y <= col) {
            if(mine[x][y] == '1') {
                printf("很遗憾,此处是雷,被炸死了\n");
                DisplayBoard(mine, ROW, COL);
                break;
            }
            else {
                if(show[x][y] != '*') {
                    printf("该位置已排查过\n");
                    continue;
                }
                
                win++;
                int count = GetMineCount(mine, x, y);
                show[x][y] = count + '0';  // 转换为字符数字
                DisplayBoard(show, ROW, COL);
            }
        }
        else {
            printf("输入的坐标有误,重新输入\n");
        }
    }
    
    if(win == row * col - EASY_COUNT) {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, ROW, COL);
    }
}

四、游戏主流程

4.1 游戏主循环

void game()
{
    char mine[ROWS][COLS] = {0};  // 存储地雷信息
    char show[ROWS][COLS] = {0};  // 存储显示信息
    
    // 初始化棋盘
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    
    // 布置地雷
    SetMine(mine, ROW, COL);
    
    // 显示初始棋盘
    DisplayBoard(show, ROW, COL);
    
    // 开始排查地雷
    FindMine(mine, show, ROW, COL);
}

4.2 主函数

int main()
{
    int input = 0;
    srand((unsigned int)time(NULL));  // 设置随机种子
    
    do {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        
        switch(input) {
            case 1:
                game();
                break;
            case 0:
                printf("退出游戏\n");
                break;
            default:
                printf("选择错误,重新选择!\n");
                break;
        }
    } while(input);
    
    return 0;
}

五、游戏扩展功能

5.1 多难度级别

可以扩展为支持不同难度级别:

#define EASY_ROW 9
#define EASY_COL 9
#define EASY_COUNT 10

#define MEDIUM_ROW 16
#define MEDIUM_COL 16
#define MEDIUM_COUNT 40

#define HARD_ROW 16
#define HARD_COL 30
#define HARD_COUNT 99

5.2 递归展开空白区域

实现点击空白区域时自动展开周围区域:

void ExpandBlank(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win)
{
    // 检查边界
    if(x < 1 || x > ROW || y < 1 || y > COL || show[x][y] != '*') {
        return;
    }
    
    int count = GetMineCount(mine, x, y);
    if(count > 0) {
        show[x][y] = count + '0';
        (*win)++;
        return;
    }
    
    // 如果是空白区域,递归展开周围8个格子
    show[x][y] = ' ';
    (*win)++;
    
    for(int i = x-1; i <= x+1; i++) {
        for(int j = y-1; j <= y+1; j++) {
            if(i != x || j != y) {
                ExpandBlank(mine, show, i, j, win);
            }
        }
    }
}

5.3 标记地雷功能

添加标记功能,让玩家可以标记怀疑有雷的位置:

void MarkMine(char show[ROWS][COLS], int row, int col)
{
    int x, y;
    printf("请输入要标记的坐标:");
    scanf("%d %d", &x, &y);
    
    if(x >= 1 && x <= row && y >= 1 && y <= col) {
        if(show[x][y] == '*') {
            show[x][y] = '!';  // 用!表示标记的雷
        }
        else if(show[x][y] == '!') {
            show[x][y] = '*';  // 取消标记
        }
        else {
            printf("该位置不能标记\n");
        }
        DisplayBoard(show, ROW, COL);
    }
    else {
        printf("坐标非法\n");
    }
}

六、完整代码实现

6.1 game.h 头文件

#pragma once

#include 
#include 
#include 

#define EASY_COUNT 10

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

// 初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

// 打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);

// 布置雷
void SetMine(char board[ROWS][COLS], int row, int col);

// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

6.2 game.c 游戏逻辑实现

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
    for(int i = 0; i < rows; i++) {
        for(int j = 0; j < cols; j++) {
            board[i][j] = set;
        }
    }
}

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
    printf("--------扫雷--------\n");
    for(int i = 0; i <= col; i++) {
        printf("%d ", i);
    }
    printf("\n");
    
    for(int i = 1; i <= row; i++) {
        printf("%d ", i);
        for(int j = 1; j <= col; j++) {
            printf("%c ", board[i][j]);
        }
        printf("\n");
    }
    printf("--------扫雷--------\n");
}

void SetMine(char board[ROWS][COLS], int row, int col)
{
    int count = EASY_COUNT;
    while(count) {
        int x = rand() % row + 1;
        int y = rand() % col + 1;
        if(board[x][y] != '1') {
            board[x][y] = '1';
            count--;
        }
    }
}

int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
    return (mine[x-1][y] + mine[x-1][y-1] + mine[x][y-1] + 
            mine[x+1][y-1] + mine[x+1][y] + mine[x+1][y+1] + 
            mine[x][y+1] + mine[x-1][y+1] - 8 * '0');
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    int x, y;
    int win = 0;
    
    while(win < row * col - EASY_COUNT) {
        printf("请输入要排查的坐标(输入0 0标记雷):");
        scanf("%d %d", &x, &y);
        
        if(x == 0 && y == 0) {
            MarkMine(show, row, col);
            continue;
        }
        
        if(x >= 1 && x <= row && y >= 1 && y <= col) {
            if(show[x][y] != '*' && show[x][y] != '!') {
                printf("该位置已排查过\n");
                continue;
            }
            
            if(mine[x][y] == '1') {
                printf("很遗憾,此处是雷,被炸死了\n");
                DisplayBoard(mine, ROW, COL);
                break;
            }
            else {
                int count = GetMineCount(mine, x, y);
                if(count == 0) {
                    ExpandBlank(mine, show, x, y, &win);
                }
                else {
                    show[x][y] = count + '0';
                    win++;
                }
                DisplayBoard(show, ROW, COL);
                printf("还需排查%d个安全位置\n", row*col - EASY_COUNT - win);
            }
        }
        else {
            printf("输入的坐标有误,重新输入\n");
        }
    }
    
    if(win == row * col - EASY_COUNT) {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, ROW, COL);
    }
}

6.3 test.c 主程序

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void menu()
{
    printf("******************************\n");
    printf("********     1. play   *******\n");
    printf("********     0. exit   *******\n");
    printf("******************************\n");
}

void game()
{
    char mine[ROWS][COLS] = {0};
    char show[ROWS][COLS] = {0};
    
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    
    SetMine(mine, ROW, COL);
    DisplayBoard(show, ROW, COL);
    
    FindMine(mine, show, ROW, COL);
}

int main()
{
    int input = 0;
    srand((unsigned int)time(NULL));
    
    do {
        menu();
        printf("请选择:");
        scanf("%d", &input);
        
        switch(input) {
            case 1:
                game();
                break;
            case 0:
                printf("退出游戏\n");
                break;
            default:
                printf("选择错误,重新选择!\n");
                break;
        }
    } while(input);
    
    return 0;
}

你可能感兴趣的:(C/C++项目实战,c语言,游戏,开发语言,算法,数据结构,c++,课程设计)