扫雷游戏的功能说明 :
• 使⽤控制台实现经典的扫雷游戏
• 游戏可以通过菜单实现继续玩或者退出游戏
• 扫雷的棋盘是9*9的格⼦
• 默认随机布置10个雷
• 可以排查雷:
◦ 如果位置不是雷,就显⽰周围有⼏个雷
◦ 如果位置是雷,就炸死游戏结束
◦ 把除10个雷之外的所有⾮雷都找出来,排雷成功,游戏结束
1test.c //⽂件中写游戏的测试逻辑 2game.c //⽂件中写游戏中函数的实现等 3game.h //⽂件中写游戏需要的数据类型和函数声明等 4
逻辑开始:
一、菜单
- 输入1进入游戏,输入0退出游戏,输入其他数字显示输入错误,并且重新输入
1test.c 2#include "game.h" 3 4int main() 5{ 6 menu(); 7 { 8 regain: 9 printf("请输入你的选择:"); 10 int input1; 11 scanf("%d", &input1); 12 switch (input1) 13 { 14 case 1: 15 { 16 printf("进入游戏\n"); 17 game(); 18 break; 19 } 20 case 0: 21 { 22 printf("退出游戏\n"); 23 break; 24 25 } 26 default: 27 { 28 printf("输入错误,请重新输入:"); 29 goto regain; 30 } 31 } 32 } 33 return 0; 34} 35
1game.c 2#include "game.h" 3 4void menu() 5{ 6 printf("****************\n"); 7 printf("**** 1.Play ****\n"); 8 printf("**** 0.Quit ****\n"); 9 printf("****************\n"); 10 11} 12
1game.h 2#pragma once 3#include <stdio.h> 4#include "game.h" 5 6//菜单 7void menu(); 8
二、生成 9X9 的游戏界面
- 使用二维数组实现
- 运用两个棋盘,一个用于展示,一个用于设置雷,写出初始化棋盘的函数
- 将展示的棋盘
char show全部初始化为'*',将布置雷的棋盘char mine全部初始化为0 - 为方便后边测试,可以先把打印棋盘的函数写出
test.c文件增加了以下代码
1test.c 2#include "game.h" 3 4void game() 5{ 6 //用于布置雷的二维数组 7 char mine[ROWS][COLS] = { 0 }; 8 9 //用于游戏界面的的二维数组 10 char show[ROWS][COLS] = { 0 }; 11 12 //用于游戏界面的的二维数组全部初始为 '*' 13 set_keyboard(show, ROWS, COLS, '*'); 14 15 //用于布置雷的二维数组全部初始化为 '0' 16 set_keyboard(mine, ROWS, COLS, '0'); 17 18 //打印函数 19 printf_keyboard(show, ROW, COL); 20 printf_keyboard(mine, ROW, COL); 21} 22
game.c文件增加了以下代码
1game.c 2#include "game.h" 3 4//初始化棋盘 5void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set) 6{ 7 for (int i = 0; i < rows; i++) 8 { 9 for (int j = 0; j < cols; j++) 10 { 11 board[i][j] = set; 12 } 13 } 14} 15 16//展示棋盘 17void printf_keyboard(char board[ROWS][COLS], int row, int col) 18{ 19 printf("-------扫雷--------\n"); 20 for (int r = 0; r <= row; r++) 21 { 22 printf("%d ", r); 23 } 24 25 printf("\n"); 26 27 for (int i = 1; i <= row; i++) 28 { 29 printf("%d ", i); 30 for (int j = 1; j <= col; j++) 31 { 32 printf("%c ", board[i][j]); 33 } 34 printf("\n"); 35 } 36} 37
game.c文件增加了以下代码
1#pragma once 2#include <stdio.h> 3#include "game.h" 4 5#define ROW 9 6#define COL 9 7#define ROWS ROW+3 8#define COLS COL+2 9 10 11//菜单 12void menu(); 13 14//初始化棋盘 15void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set); 16 17//展示棋盘 18void printf_keyboard(char board[ROWS][COLS], int row, int col); 19 20
三、随机布置雷
- 使用
srand((unsigned int) time(NULL))和rand() - 将雷设置为
1,雷只能布置在char mine[x][y] == '0'的地方
game.h文件增加了
#include <time.h>
#include "stdlib.h"
#define MINE 10
void set_mine(char board[ROWS][COLS], int row, int col, int mine);
1game.h 2 3#pragma once 4#include <stdio.h> 5#include "game.h" 6#include <time.h> 7#include "stdlib.h" 8 9#define ROW 9 10#define COL 9 11#define ROWS ROW+3 12#define COLS COL+2 13#define MINE 10 14 15 16//菜单 17void menu(); 18 19//初始化棋盘 20void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set); 21 22//展示棋盘 23void printf_keyboard(char board[ROWS][COLS], int row, int col); 24 25//随机布置雷 26void set_mine(char board[ROWS][COLS], int row, int col, int mine); 27
1 2game.c文件增加以下代码 3game.c 4 5//随机布置雷 6void set_mine(char board[ROWS][COLS], int row, int col, int mine) 7{ 8 srand((unsigned int) time(NULL)); 9 10 while (mine) 11 { 12 int x = (rand() % row) + 1; 13 int y = (rand() % col) + 1; 14 if (board[x][y] == '0'); 15 { 16 board[x][y] = '1'; 17 mine--; 18 } 19 } 20 21} 22 23
test.c文件增加以下代码
1test.c 2 3//随机布置雷 4set_mine(mine, ROW, COL, MINE); 5
四、排雷
- 注意输入的坐标,横纵坐标都只能是
0~9,出现其他数字报错,并重新输入 - 所排的坐标要显示周围雷的个数,如果为
0,展开周围的棋盘(运用到递归) - 如果所排的坐标是雷,显示
游戏结束 - 如果输入的坐标是已经输入过的坐标,显示
该坐标已经排除 - 判断游戏胜利,
排除的坐标个数与减掉雷后的格子数相等
test.c文件布局改为以下情况
1test.c 2void game() 3{ 4 //用于布置雷的二维数组 5 char mine[ROWS][COLS] = { 0 }; 6 7 //用于游戏界面的的二维数组 8 char show[ROWS][COLS] = { 0 }; 9 10 //用于游戏界面的的二维数组全部初始为 '*' 11 set_keyboard(show, ROWS, COLS, '*'); 12 13 //用于布置雷的二维数组全部初始化为 '0' 14 set_keyboard(mine, ROWS, COLS, '0'); 15 16 //随机布置雷 17 set_mine(mine, ROW, COL, MINE); 18 19 //打印函数 20 printf_keyboard(show, ROW, COL); 21 //printf_keyboard(mine, ROW, COL); 22 23 //排雷 24 move_mine(show, mine, ROW, COL); 25} 26
game.c文件增加了以下代码
1game.c 2//计算周围雷的个数 3int Count_mine(char mine[ROWS][COLS], int x, int y) 4{ 5 return mine[x][y] - '0'; 6} 7 8//展开棋盘----递归 9void Open_keyboard(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y) 10{ 11 //写递归首先写结束条件 12 13 //越界时,返回 14 if ((x > (ROWS - 2)) || (x < 1) || (y > (COLS - 2)) || (y < 1)) 15 { 16 return; 17 } 18 19 //遇到以及排过雷的坐标返回 20 if (show[x][y] != '*') 21 { 22 return; 23 } 24 25 //计算雷的个数 26 int count = 0; 27 for (int i = -1; i <= 1; i++) 28 { 29 for (int j = -1; j <= 1; j++) 30 { 31 count += Count_mine(mine, x + i, y + j); 32 } 33 } 34 show[x][y] = count + '0'; 35 36 //按照游戏规则,如果坐标显示雷的数目不为零,则返回 37 if (show[x][y] != '0') 38 { 39 return; 40 } 41 42 //展开雷 43 for (int i = -1; i <= 1; i++) 44 { 45 for (int j = -1; j <= 1; j++) 46 { 47 Open_keyboard(show, mine, x + i, y + j); 48 } 49 } 50 51} 52 53//排雷 54void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) 55{ 56 int x, y; 57 while (1) 58 { 59 printf("请输入要排查的坐标:"); 60 regain2: 61 scanf("%d %d", &x, &y); 62 if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9)) 63 { 64 if (show[x][y] == '*') 65 { 66 if (mine[x][y] == '0') 67 { 68 Open_keyboard(show,mine,x,y); 69 printf_keyboard(show, ROW, COL); 70 } 71 else 72 { 73 printf("很遗憾,踩到雷了,游戏结束\n以下是雷的位置:"); 74 printf_keyboard(mine, ROW, COL); 75 break; 76 } 77 } 78 else 79 { 80 printf("该坐标已经排查过了,请输入别的坐标:"); 81 goto regain2; 82 } 83 } 84 else 85 { 86 printf("输入错误,重新输入:"); 87 goto regain2; 88 } 89 90 //判断赢 91 int Remove_mine_count = 0; 92 for (int i = 1; i <= row; i++) 93 { 94 for (int j = 1; j <= col; j++) 95 { 96 if (show[i][j] != '*') 97 { 98 Remove_mine_count++; 99 } 100 } 101 } 102 if (Remove_mine_count == ((ROW * COL) - MINE)) 103 { 104 printf("恭喜你,排除所有的雷,游戏胜利\n"); 105 printf_keyboard(mine, ROW, COL); 106 break; 107 } 108 } 109} 110
game.h文件的代码不变
1game.h 2#pragma once 3#include <stdio.h> 4#include "game.h" 5#include <time.h> 6#include "stdlib.h" 7 8#define ROW 9 9#define COL 9 10#define ROWS ROW+3 11#define COLS COL+2 12#define MINE 10 13 14 15//菜单 16void menu(); 17 18//初始化棋盘 19void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set); 20 21//展示棋盘 22void printf_keyboard(char board[ROWS][COLS], int row, int col); 23 24//随机布置雷 25void set_mine(char board[ROWS][COLS], int row, int col, int mine); 26 27//排雷 28void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col); 29 30
五、扫雷游戏完整代码
test.c
1#include "game.h" // 包含扫雷游戏所需的头文件(声明函数、宏定义等) 2 3// 游戏核心逻辑函数,负责初始化游戏数据、布置雷、处理排雷过程 4void game() 5{ 6 // 定义二维数组mine,用于存储雷的位置信息('1'表示有雷,'0'表示无雷) 7 // ROWS和COLS是宏定义,通常比实际游戏区域大2(用于处理边界判断,避免越界) 8 char mine[ROWS][COLS] = { 0 }; 9 10 // 定义二维数组show,用于展示给玩家的界面(初始为'*',排雷后显示周围雷数或雷) 11 char show[ROWS][COLS] = { 0 }; 12 13 // 初始化show数组,全部元素设为'*'(表示未探索的格子) 14 // set_keyboard是自定义函数,用于批量初始化二维数组 15 set_keyboard(show, ROWS, COLS, '*'); 16 17 // 初始化mine数组,全部元素设为'0'(先默认所有格子无雷,后续再随机布置雷) 18 set_keyboard(mine, ROWS, COLS, '0'); 19 20 // 在mine数组中随机布置雷,MINE是宏定义(雷的总数) 21 // ROW和COL是宏定义,代表实际游戏区域的行数和列数(比ROWS、COLS小2) 22 set_mine(mine, ROW, COL, MINE); 23 24 // 打印玩家界面(show数组),展示当前未探索的格子(全为'*') 25 printf_keyboard(show, ROW, COL); 26 // 调试用:打印雷的位置(mine数组),实际游戏中不会显示给玩家 27 // printf_keyboard(mine, ROW, COL); 28 29 // 进入排雷逻辑,玩家输入坐标,处理排雷结果(显示周围雷数、踩雷结束等) 30 move_mine(show, mine, ROW, COL); 31} 32 33// 主函数,程序入口,负责展示菜单和处理用户选择 34int main() 35{ 36 menu(); // 调用menu函数,打印游戏菜单(如"1. 开始游戏 0. 退出游戏") 37 38 { // 局部作用域,隔离内部变量 39 regain1: // 跳转标签,用于输入错误时重新回到输入步骤 40 printf("请输入你的选择:"); // 提示用户输入选项(1或0) 41 int input1; // 存储用户输入的选项 42 scanf("%d", &input1); // 读取用户输入 43 44 // 根据用户输入的选项执行对应操作 45 switch (input1) 46 { 47 case 1: // 用户选择"开始游戏" 48 { 49 printf("进入游戏\n"); // 提示进入游戏 50 game(); // 调用game函数,启动扫雷游戏逻辑 51 break; // 退出switch分支 52 } 53 case 0: // 用户选择"退出游戏" 54 { 55 printf("退出游戏\n"); // 提示退出游戏 56 break; // 退出switch分支 57 } 58 default: // 用户输入了无效选项(非1和0) 59 { 60 printf("输入错误,请重新输入:"); // 提示输入错误 61 goto regain1; // 跳转到regain1标签,重新等待用户输入 62 } 63 } 64 } 65 66 return 0; // 程序正常结束 67} 68
game.c
1#include "game.h" // 包含扫雷游戏所需的头文件(宏定义、函数声明等) 2 3// 打印游戏菜单 4void menu() 5{ 6 printf("****************\n"); 7 printf("**** 1.Play ****\n"); // 1表示开始游戏 8 printf("**** 0.Quit ****\n"); // 0表示退出游戏 9 printf("****************\n"); 10} 11 12// 初始化棋盘(二维数组) 13// board:要初始化的二维数组 14// rows、cols:数组的行数和列数 15// set:初始化填充的字符(如'*'或'0') 16void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set) 17{ 18 // 遍历数组的每个元素,设置为指定字符set 19 for (int i = 0; i < rows; i++) 20 { 21 for (int j = 0; j < cols; j++) 22 { 23 board[i][j] = set; 24 } 25 } 26} 27 28// 展示棋盘(打印到控制台) 29// board:要展示的二维数组(玩家界面或雷区) 30// row、col:实际游戏区域的行数和列数(不包含边界) 31void printf_keyboard(char board[ROWS][COLS], int row, int col) 32{ 33 printf("-------扫雷--------\n"); 34 // 打印列号(0到col),方便玩家定位坐标 35 for (int r = 0; r <= row; r++) 36 { 37 printf("%d ", r); 38 } 39 printf("\n"); 40 41 // 打印每行内容(包含行号和对应格子的字符) 42 for (int i = 1; i <= row; i++) 43 { 44 printf("%d ", i); // 打印行号(1到row) 45 // 打印当前行的每个格子(从第1列到第col列) 46 for (int j = 1; j <= col; j++) 47 { 48 printf("%c ", board[i][j]); 49 } 50 printf("\n"); // 每行结束后换行 51 } 52} 53 54// 随机布置雷到雷区数组 55// board:存储雷区信息的二维数组('1'表示有雷,'0'表示无雷) 56// row、col:实际游戏区域的行数和列数 57// mine:要布置的雷的总数 58void set_mine(char board[ROWS][COLS], int row, int col, int mine) 59{ 60 srand((unsigned int)time(NULL)); // 初始化随机数种子,确保每次雷的位置不同 61 62 // 循环布置雷,直到雷的数量为0 63 while (mine) 64 { 65 // 生成1到row范围内的随机行坐标x 66 int x = (rand() % row) + 1; 67 // 生成1到col范围内的随机列坐标y 68 int y = (rand() % col) + 1; 69 70 // 如果当前位置没有雷(为'0'),则布置雷(设为'1') 71 if (board[x][y] == '0') // 注意:原代码此处多了一个分号,可能是笔误,实际应去掉 72 { 73 board[x][y] = '1'; 74 mine--; // 雷的数量减1 75 } 76 } 77} 78 79// 计算指定坐标周围8个方向的雷的总数 80// mine:雷区数组 81// x、y:要检查的坐标 82int Count_mine(char mine[ROWS][COLS], int x, int y) 83{ 84// 计算坐标(x,y)周围8个相邻格子中雷的总数 85// 原理:将周围8个格子的字符值('0'表示无雷,'1'表示有雷)转换为数字后求和 86return ( 87 mine[x - 1][y] + // 上方格子 88 mine[x - 1][y - 1] + // 左上方格子 89 mine[x][y - 1] + // 左方格子 90 mine[x + 1][y - 1] + // 左下方格子 91 mine[x + 1][y] + // 下方格子 92 mine[x + 1][y + 1] + // 右下方格子 93 mine[x][y + 1] + // 右方格子 94 mine[x - 1][y + 1] - // 右上方格子 95 8 * '0' // 减去8个'0'的ASCII值(将字符转为数字:'0'→0,'1'→1) 96); 97} 98 99// 递归展开无雷区域(当某个格子周围无雷时,自动展开周围所有无雷格子) 100// show:玩家界面数组 101// mine:雷区数组 102// x、y:当前要展开的坐标 103void Open_keyboard(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y) 104{ 105 // 递归结束条件1:坐标越界(超出实际游戏区域) 106 if ((x > (ROWS - 2)) || (x < 1) || (y > (COLS - 2)) || (y < 1)) 107 { 108 return; 109 } 110 111 // 递归结束条件2:该坐标已被探索过(非'*') 112 if (show[x][y] != '*') 113 { 114 return; 115 } 116 117 // 计算当前坐标周围8个方向的雷的总数 118 int count = 0; 119 for (int i = -1; i <= 1; i++) // 行方向:-1(上)、0(当前)、1(下) 120 { 121 for (int j = -1; j <= 1; j++) // 列方向:-1(左)、0(当前)、1(右) 122 { 123 count += Count_mine(mine, x + i, y + j); // 累加周围每个格子的雷数 124 } 125 } 126 // 在玩家界面显示当前格子周围的雷数(字符形式,如'0'表示无雷) 127 show[x][y] = count + '0'; 128 129 // 递归结束条件3:如果周围有雷(count≠0),则停止展开 130 if (show[x][y] != '0') 131 { 132 return; 133 } 134 135 // 如果周围无雷(count=0),递归展开周围8个方向的格子 136 for (int i = -1; i <= 1; i++) 137 { 138 for (int j = -1; j <= 1; j++) 139 { 140 Open_keyboard(show, mine, x + i, y + j); 141 } 142 } 143} 144 145// 排雷核心逻辑 146// show:玩家界面数组 147// mine:雷区数组 148// row、col:实际游戏区域的行数和列数 149void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) 150{ 151 int x, y; // 存储玩家输入的排查坐标 152 while (1) // 循环处理排雷操作,直到游戏结束 153 { 154 printf("请输入要排查的坐标:"); 155 regain2: // 跳转标签,用于输入错误时重新输入坐标 156 scanf("%d %d", &x, &y); // 读取玩家输入的坐标(x为行,y为列) 157 158 // 检查坐标是否在有效范围内(1到row行,1到col列) 159 if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9)) // 假设row=col=9,可改为x<=row && y<=col 160 { 161 // 检查该坐标是否未被排查过(仍为'*') 162 if (show[x][y] == '*') 163 { 164 // 如果该位置无雷(mine[x][y]为'0') 165 if (mine[x][y] == '0') 166 { 167 Open_keyboard(show, mine, x, y); // 展开周围无雷区域 168 printf_keyboard(show, ROW, COL); // 刷新并显示玩家界面 169 } 170 // 如果该位置有雷(mine[x][y]为'1') 171 else 172 { 173 printf("很遗憾,踩到雷了,游戏结束\n以下是雷的位置:"); 174 printf_keyboard(mine, ROW, COL); // 显示所有雷的位置 175 break; // 退出循环,游戏结束 176 } 177 } 178 // 该坐标已被排查过 179 else 180 { 181 printf("该坐标已经排查过了,请输入别的坐标:"); 182 goto regain2; // 跳转到regain2,重新输入坐标 183 } 184 } 185 // 坐标输入无效(超出范围) 186 else 187 { 188 printf("输入错误,重新输入:"); 189 goto regain2; // 跳转到regain2,重新输入坐标 190 } 191 192 // 判断玩家是否获胜(已排查所有非雷格子) 193 int Remove_mine_count = 0; // 记录已排查的非雷格子数量 194 for (int i = 1; i <= row; i++) 195 { 196 for (int j = 1; j <= col; j++) 197 { 198 if (show[i][j] != '*') // 非'*'表示已排查 199 { 200 Remove_mine_count++; 201 } 202 } 203 } 204 // 获胜条件:已排查的格子数 = 总格子数 - 雷的总数 205 if (Remove_mine_count == ((ROW * COL) - MINE)) 206 { 207 printf("恭喜你,排除所有的雷,游戏胜利\n"); 208 printf_keyboard(mine, ROW, COL); // 显示所有雷的位置 209 break; // 退出循环,游戏结束 210 } 211 } 212} 213
game.h
1#pragma once // 防止头文件被重复包含(只编译一次) 2 3// 包含所需的标准库头文件 4#include <stdio.h> // 提供输入输出函数(如printf、scanf) 5#include "game.h" // 包含游戏相关的其他声明(注意:此处可能存在循环包含,实际应避免) 6#include <time.h> // 提供时间相关函数(如time,用于初始化随机数种子) 7#include "stdlib.h" // 提供标准库函数(如rand、srand,用于生成随机数) 8 9// 宏定义:游戏核心参数 10#define ROW 9 // 实际游戏区域的行数(9行) 11#define COL 9 // 实际游戏区域的列数(9列) 12#define ROWS ROW+3 // 雷区数组的总行数(比实际行数多3,用于处理边界,避免越界访问) 13#define COLS COL+2 // 雷区数组的总列数(比实际列数多2,用于处理边界) 14#define MINE 10 // 游戏中雷的总数(10个) 15 16 17// 函数声明:声明游戏中用到的所有函数(供其他文件调用) 18 19// 打印游戏菜单(如开始/退出选项) 20void menu(); 21 22// 初始化棋盘数组 23// 参数:board-要初始化的二维数组,rows-数组行数,cols-数组列数,set-初始化填充的字符 24void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set); 25 26// 打印展示棋盘(玩家界面或雷区) 27// 参数:board-要展示的二维数组,row-实际游戏区域行数,col-实际游戏区域列数 28void printf_keyboard(char board[ROWS][COLS], int row, int col); 29 30// 在雷区数组中随机布置雷 31// 参数:board-雷区数组,row-实际游戏区域行数,col-实际游戏区域列数,mine-要布置的雷数 32void set_mine(char board[ROWS][COLS], int row, int col, int mine); 33 34// 处理玩家的排雷操作(核心游戏逻辑) 35// 参数:show-玩家界面数组,mine-雷区数组,row-实际游戏区域行数,col-实际游戏区域列数 36void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col); 37
《C语言-----扫雷游戏》 是转载文章,点击查看原文。
