前言:说到扫雷游戏,小时候玩着可头疼了,玩的时候就乱点,也不知道啥意思,完全凭感觉,至今回想还挺有意思,今天带着大家小小实现一波简易版扫雷游戏。代码分为三个部分,game.h是存放声明的部分,game.c是游戏具体实现的部分,test.c是我们大概逻辑部分。
目录
❤️总体思路:
总体思路:
扫雷游戏是在一堆格子里去排查,格子的话我们可以定义成二维数组的形式,其中这些格子包括雷,和含有多少雷的信息。如下图第一张所示,左上角就是我含有雷的个数,第二张图片上数字部分是它的周围含有几个雷。
既然有存放雷,和显示雷的信息,不如我们就定义两个数组,一个是存放雷,一个存放含雷的信息,并且这两个数组的大小是一样,为了保证位置一一对应关系,这样一来方便了改坐标的代码。我们知道当我们选择棋盘周围的坐标时,去统计周围雷信息的时候就会造成数组访问越界,所以不如我们定义的数组的时候,就多定义两行两列(左右都要所以定义两列,行是一样的道理,上下部分),但是显示的时候不能出现多出来的两行和两列。两个数组定义完就是初始化,和打印,打印完了,我们在布置雷,布置好雷就开始排雷。总体思路是这样接下来我们在去细化。
1.打印菜单栏
玩游戏都有个菜单栏,菜单栏里有选项,玩游戏或者不玩游戏,我们就来定义玩游戏选输入 1, 不玩游戏,我们输入0。接下来就写一个简易的菜单栏
// test.c 代码部分
void menu()
{
printf("************************\n");
printf("******** 1.play ********\n"); //玩游戏
printf("******** 0.exit ********\n"); //退出游戏
printf("************************\n");
}
菜单栏有了,接下来就是调用这个菜单栏,我们都知道玩游戏我们可以多次玩,并不是只玩以此,所以菜单栏打印的话也要放在循环里,并且一上来就有一个菜单栏,所以用到do....while循环。
补充:do......while循环就是先做一次事,再循环。
调用菜单栏如下:
// test.c 代码部分
int main()
{
do
{
menu(); //调用菜单栏
} while ();
}
调用完菜单栏,接下来就是我们去选择,选择你给输入一个数吧,所以定义一个变量,来作为输入部分,代码如下:
// test.c 代码部分
int main()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
} while (input);
}
输入完之后,你肯定要接收进来,接受的结果来判断我们是否玩游戏,所以我们选择switch...case语句,代码如下:
// test.c 代码部分
int main()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1 :
game();
break;
case 0 :
printf("退出游戏\n");
break;
default :
printf("输入错误,请重新输入\n");
break;
}
} while (input); //如果玩家输入的是0,刚好就退出循环,如果是非0,则一直循环
//因此用input来判断循环是否进行
}
2.数组的定义
接下来才是游戏代码,上面说到我们要定义两个数组,一个存放雷的数组,一个存放雷的信息数组,我们就先来个9*9的数组,定义数组行和列不如用宏来定义,因为方便我们改棋盘大小,代码如下:
// game.h 代码部分
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
// test.c 代码部分
void game()
{
char mine[ROWS][COLS] = { 0 }; //存储雷
char show[ROWS][COLS] = { 0 }; //存储雷的信息
}
3.数组的初始化
我们将雷的数组初始化为字符0,将存放雷信息的部分初始化为字符1,既然初始化为不同的字符,所以函数传参的时候我们就将对应的字符传过去就行。初始化是将数组全部内容初始化,所以传数组行和列的时候是对应的总行和总列。代码如下:
// test.c 代码部分
void game()
{
char mine[ROWS][COLS] = { 0 }; //存储雷
char show[ROWS][COLS] = { 0 }; //存储雷的信息
//初始化
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
}
// game.h 代码部分
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
//初始化
InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
// game.c 代码部分
InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
4.打印数组
前面说了由于怕数组越界访问,因此我们多加了两行两列,但是实际打印的时候我们让玩家并不能看到多出的两行两列,所以数组的行和列传的时候不是总行和总列,我们打印的行和列是从1~9,列也是从1~9,代码如下:
// test.c 代码部分
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); //我们不需要将存放雷的位置打印,所以只打印雷的信息
}
// game.h 代码部分
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
//初始化
InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印格子
DisplayBoard(char board[ROWS][COLS], int row, int col);
// game.c 代码部分
DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
效果图如下:
我们可以看到 上面的图片并不能一眼的看出对应的位置,所以我们给行和列加上序号的话这样就方便多了。代码和效果图如下:
// game.c 代码部分
DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
5.布置雷
雷的布置实在mine数组里布置,随机生成一个坐标布置,既然说到随机数的生成,已经在猜数字小游戏里说过了,这里我就不再过多描述。代码如下:(大家要是测试的时候可以布置后将mine数组打印看看是不是随机)
补充:猜数字游戏https://blog.csdn.net/m0_64221480/article/details/124230089?spm=1001.2014.3001.5501
代码如下:
// test.c 代码部分
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); 看看是不是随机生成
}
// game.h 代码部分
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10 //设置雷的个数
//初始化
InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印格子
DisplayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
SetMine(char board[ROWS][COLS], int row, int col);
// game.c 代码部分
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] == '0')
{
board[x][y] = '1';
count--;
}
}
}
6.排查雷
玩家要排查雷的话,玩家首先要输入一个排查雷的坐标,如果玩家输入的坐标,不在我们的数组范围内那就提示一个非法坐标,并让他重新输入,如果在数组范围内的话,我们还要考虑玩家输入的坐标是否是输入过的,输入过的也要提示一下,以上排除完,玩家输入坐标位置万一有雷的话,直接就输掉游戏,如果没雷才去统计周围的雷的个数并且打印出来。代码如下:
// test.c 代码部分
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);
}
// game.h 代码部分
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10
//初始化
InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印格子
DisplayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
SetMine(char board[ROWS][COLS], int row, int col);
//排查雷
FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
// game.c 代码部分
FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请玩家输入坐标:>");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (show[x][y] != '*')
{
printf("该坐标已被排查过,请勿重复排查\n");
}
else
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
break;
}
else
{
int count = get_mine_count(mine, x, y); //调用函数去统计周围雷的个数
show[x][y] = count + '0';
DisplayBoard(show, row, col);
}
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
统计周围雷的函数,我们知道当周围雷统计的时候是统计的是字符,而让他返回时返回的时数字,我们知道字符0的ASIIC码是48,而字符1的ASIIC码是49。字符1= ASIIC(‘1’) - ASIIC(‘0’),进行转换得来,所以我们每一个减去字符0就能得到,所以如下图和代码:
// game.c 代码部分
//计算坐标周围雷的个数
int get_mine_count(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';
}
以上在代码在排雷得过程我们会发现排雷代码并没有结束的条件,我们知道我们的格子有81个,而雷有10,也就是玩家在排除了71次就会终止游戏并提示游戏结束,玩家获胜,所以我们来看看下面修改后的代码:
// game.c 代码部分
FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
printf("请玩家输入坐标:>");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (show[x][y] != '*')
{
printf("该坐标已被排查过,请勿重复排查\n");
}
else
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
break;
}
else
{
win++;
int count = get_mine_count(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, row, col);
}
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
}
}
以上就是游戏的思路过程。下面我将把game.h , game.c,test.c 的代码分别列出
7.game.h代码部分
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10
//初始化
InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印格子
DisplayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
SetMine(char board[ROWS][COLS], int row, int col);
//排查雷
FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
8.game.c代码部分
#include "game.h"
//实现初始化
InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
//打印格子
DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
//布置雷
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] == '0')
{
board[x][y] = '1';
count--;
}
}
}
//计算坐标周围雷的个数
int get_mine_count(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';
}
//排查雷
FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
printf("请玩家输入坐标:>");
scanf("%d%d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
if (show[x][y] != '*')
{
printf("该坐标已被排查过,请勿重复排查\n");
}
else
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
break;
}
else
{
win++;
int count = get_mine_count(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, row, col);
}
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
}
}
9.test.c代码部分
#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()
{
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);
}