目录
- 游戏总体思路
- ⚪文件分工
- ⚪游戏构思
- 游戏函数代码
- ⚪主函数
- ⚪菜单打印
- ⚪游戏函数
- ⚪初始化棋盘
- ⚪打印棋盘
- ⚪设置雷
- ⚪排查雷
- ⚪计算周围雷数
- ⚪递归展开
- ⚪防止第一步踩雷
- 完整代码
- ⚪game.h
- ⚪test.c
- ⚪game.c
游戏总体思路
同三子棋一样,做稍微复杂的程序时尽量多文件分工______________________________________________________________________________________
文件分工
test.c —— 测试游戏的逻辑
game.c —— 游戏相关函数的实现
game.h —— 进行游戏相关函数、符号的声明与头文件的包含
______________________________________________________________________________________
游戏构思
扫雷的实现思路、方法有很多,博主也只是提供一种思路方法
以9*9的扫雷棋盘为例
扫雷的本质是对于一个未知的棋盘,把它内部的内容显现出来的过程;
而对于编程实现,就需要设置两个棋盘(棋盘由二维数组表示)
一个作为内棋盘,用来存放雷,一个作为外棋盘,为玩家初始看到的棋盘
对于内棋盘mine[][],我们以‘0’代表空,以‘1’代表雷;
对于外棋盘show[][],以‘*’作为未被打开的格子;
game.h
这里我们提前把game.h放出来,便于分析
#pragma once
#include <stdio.h>
#define ROW 9 //行
#define COL 9 //列
#define ROWS ROW+2
#define COLS COL+2
#define QUANTITY 9 //雷数
//初始化棋盘
void InitBoard(char Board[ROWS][COLS], int rows, int cols);
//打印棋盘
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);
ROW代表棋盘行数,COL代表列数
扫雷过程中,每个格子会把周围一圈的格子遍历,然后显现数字表示雷数
当选中边缘的格子时,周围没有格子怎么办?
初始化时行数列数多加2,这样让每个格子都能遍历周围的8个格子
ROWS和COLS就是数组实际的行列数
上图并非实际效果,只是用于理解
______________________________________________________________________________________
游戏函数代码
test.c
主函数
int main()
{
srand((unsigned int)time(NULL));//随机数种子(电脑随机布雷)
int input = 0;
do {
menu();
scanf("%d", &input);
switch (input) {
case 1:
printf("扫雷游戏\n");
game();
break;
case 0:
printf("退出成功\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
主函数没有太多介绍的,
do_while循环让游戏持续进行,switch语句实现多分枝
注意这里用dowhile为了让游戏先执行再进行输入
______________________________________________________________________________________
菜单打印
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, '*');
//打印棋盘
//DisplayBoard(mine, ROW, COL);//用于观察内棋盘
DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
game.c
初始化棋盘
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char ret)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Board[i][j] = ret;
}
}
}
打印棋盘
void DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
printf("------------扫雷游戏-------------\n");
//打印列号
for (int j = 0; j <= col; j++)
{
printf("%d ", j);
}
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");
}
打印棋盘只需要打印ROW,COL,并不需要打印初始化的多出的外圈
外圈只是为了防止越界
棋盘有多种打印方式/风格,这里选择比较简便的一种方式
设置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = QUANTITY;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
设置雷使用rand()随机布置雷的坐标位置
这里简单提一下rand()生成随机数范围的规则
排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0, y = 0, step = 0;//step用来判断是否第一次踩雷
int isWin = 0;//判断游戏胜利的条件
while (isWin < row * col - QUANTITY)
{
step++;
printf("请输入要排查的坐标:(行/列)\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 & y <= col)//坐标是否合法
{
FirstStep(mine, show, step, x, y);//第一次踩雷保护
if (mine[x][y] == '1')
{
printf("精准踩雷,游戏失败\n");
DisplayBoard(mine, row, col);
break;
}
else
{
//未踩雷,进入递归
openNull(mine, show, x, y);
DisplayBoard(show, row, col);
isWin++;
}
}
else
{
printf("坐标不合法,请重新输入\n");
break;
}
}
if (isWin == row * col - QUANTITY)
{
printf("排雷成功,游戏胜利\n");
DisplayBoard(mine, row, col);
}
}
这里是引用
计算周围雷数
static int calculate_mine(char mine[ROWS][COLS], int x, int y)
{
return mine[x + 1][y] +
mine[x - 1][y] +
mine[x][y + 1] +
mine[x][y - 1] +
mine[x - 1][y - 1] +
mine[x + 1][y + 1] +
mine[x + 1][y - 1] +
mine[x - 1][y + 1] - 8 * '0';
}
static int calculate_mine(char mine[ROWS][COLS], int x, int y)
{
int sum = 0;
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
sum += mine[x + i][y + j] - '0';
}
}
return sum;
}
这里介绍两种方法
第一种直接把坐标相加,第二种通过for循环得到当前坐标的周围八个,相加起来最后进行返回
注意每个二维数组的坐标减‘0’(字符零得到本身字符型
递归展开
void openNull(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
//遍历9宫格,炸开以空白为中心的九宫格的空白
int count = calculate_mine(mine, x, y);//计算该坐标周围雷的个数
if (count == 0)//周围没雷,进入
{
show[x][y] = '0';
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*' && (mine[i][j] != '1' || show[i][j] == '?'))
//当位置未打开且不为雷或者未被标记时递归
{
openNull(mine, show, i, j);//递归,多次检测空白部分的九宫格
}
}
}
}
else
{
show[x][y] = count + '0';//加上字符0得到本身的字符型
}
}
递归展开中for循环的实现和计算周围雷数中的for循环思路一样
遍历过程中每到一个坐标并满足条件就进行递归
从而实现递归展开
防止第一步踩雷
void FirstStep(char mine[ROWS][COLS],char show[ROWS][COLS], int step, int x, int y)
{
if (step == 1 && show[x][y] == '*' && mine[x][y] == '1')
{
mine[x][y] = '0';
while (1)
{
//重新设置坐标
int a = rand() % 9 + 1;
int b = rand() % 9 + 1;
if ((a != x || b != y) && mine[a][b] == '0')//把雷放到空白处
{
mine[a][b] = '1';
break;
}
}
}
}
防止踩雷的原理其实是当第一次就是雷,把雷移到一个空白的位置
______________________________________________________________________________________
完整代码
game.h
#pragma once
#include <stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define QUANTITY 9
//初始化棋盘
void InitBoard(char Board[ROWS][COLS], int rows, int cols);
//打印棋盘
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);
test.c
#define _CRT_SECURE_NO_DEPRECATE 1
#pragma warning(disable:4996)
#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, '*');
//打印棋盘
DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
DisplayBoard(mine, ROW, COL);//用于观察内棋盘
//排查雷
FindMine(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));//随机数种子(电脑随机布雷)
int input = 0;
do {
menu();
scanf("%d", &input);
switch (input) {
case 1:
printf("扫雷游戏\n");
game();
break;
case 0:
printf("退出成功\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
return 0;
}
game.c
#define _CRT_SECURE_NO_DEPRECATE 1
#pragma warning(disable:4996)
#include "game.h"
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char ret)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Board[i][j] = ret;
}
}
}
void DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
printf("------------扫雷游戏-------------\n");
//打印列号
for (int j = 0; j <= col; j++)
{
printf("%d ", j);
}
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 mine[ROWS][COLS], int row, int col)
{
int count = QUANTITY;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
static int calculate_mine(char mine[ROWS][COLS], int x, int y)
{
/*return mine[x + 1][y] +
mine[x - 1][y] +
mine[x][y + 1] +
mine[x][y - 1] +
mine[x - 1][y - 1] +
mine[x + 1][y + 1] +
mine[x + 1][y - 1] +
mine[x - 1][y + 1] - 8 * '0';*/
int sum = 0;
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
sum += mine[x + i][y + j] - '0';
}
}
return sum;
}
void openNull(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
//遍历9宫格,炸开以空白为中心的九宫格的空白
int count = calculate_mine(mine, x, y);//计算该坐标周围雷的个数
if (count == 0)//周围没雷,进入
{
show[x][y] = '0';
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if (show[i][j] == '*' && (mine[i][j] != '1' || show[i][j] == '?'))
//当位置未打开且不为雷或者未被标记时递归
{
openNull(mine, show, i, j);//递归,多次检测空白部分的九宫格
}
}
}
}
else
{
show[x][y] = count + '0';//加上字符0得到本身的字符型
}
}
void FirstStep(char mine[ROWS][COLS],char show[ROWS][COLS], int step, int x, int y)
{
if (step == 1 && show[x][y] == '*' && mine[x][y] == '1')
{
mine[x][y] = '0';
while (1)
{
//重新设置坐标
int a = rand() % 9 + 1;
int b = rand() % 9 + 1;
if ((a != x || b != y) && mine[a][b] == '0')//把雷放到空白处
{
mine[a][b] = '1';
break;
}
}
}
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0, y = 0, step = 0;
int isWin = 0;//判断游戏胜利的条件
while (isWin < row * col - QUANTITY)
{
step++;
printf("请输入要排查的坐标:(行/列)\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 & y <= col)//坐标是否合法
{
FirstStep(mine, show, step, x, y);//第一次踩雷保护
if (mine[x][y] == '1')
{
printf("精准踩雷,游戏失败\n");
DisplayBoard(mine, row, col);
break;
}
else
{
//未踩雷,进入递归
openNull(mine, show, x, y);
DisplayBoard(show, row, col);
isWin++;
}
}
else
{
printf("坐标不合法,请重新输入\n");
break;
}
}
if (isWin == row * col - QUANTITY)
{
printf("排雷成功,游戏胜利\n");
DisplayBoard(mine, row, col);
}
}
最后扫雷中有标记等功能,有机会用图形库easyx来实现,更加方便一些(挖坑),希望对你有帮助!