前言
1. 扫雷规则信息
此次的扫雷游戏是最简单的等级——【9*9雷盘埋10个雷】。玩家在玩扫雷游戏的时候,会根据输入的坐标进行扫雷。雷用字符 ‘1’ 表示,不是雷的用字符 ‘0’ 表示。当输入下标,扫到雷之后会被提示踩雷,结束游戏,并展示所有雷的信息。若没踩到雷,会在棋盘上打印出该位置周围8个位置有几个雷,就会继续输入坐标,直到把雷全部找出,才赢。接下来,我来简单说说扫雷的整个框架👇👇👇👇👇。
2. 扫雷整体框架
扫雷嘛,最主要就是用什么存储和怎样生成雷以及如何排查雷。所以,在整个扫雷游戏中,下图四个板块是最主要的。往后会讲到具体代码结构👀👀👀:
3. 扫雷代码结构
扫雷游戏,用的多文件编写。test_game.c源文件代码,主要是对游戏的测试。game.c是对扫雷游戏的函数定义【可以说是功能的具体实现】。game.h头文件就是对数据的宏定义和函数的声明。
4. 代码分块解析
- 代码块主要分为三大模块:菜单模块、数据存储模块、布置雷模块、排查雷模块。我们娓娓道来👌👌👌👌👇:
4.1 菜单模块
首先,不管玩什么游戏都不是一次就结束了,所以此处一定得用到循环结构,对于菜单,需要玩家选择,所以需要分支结构,再加上打印信息就可以,直接上代码:
void menu()
{
printf("\t\t\t\t\t***************************\n");
printf("\t\t\t\t\t******** 扫雷游戏 *******\n");
printf("\t\t\t\t\t***************************\n");
printf("\t\t\t\t\t******** 1 : play *******\n");
printf("\t\t\t\t\t******** 0 : exit *******\n");
printf("\t\t\t\t\t***************************\n");
}
int main()
{
int input = 0;
do{
menu();
printf("请玩家选择—>:\n");
scanf_s("%d", &input);
switch (input)
{
case 1:
//具体游戏实现代码
break;
case 0:
printf("欢迎下次再玩!\n");
break;
default:
printf("输入错误,请重新输入—>:\n");
break;
}
} while (input);
return 0;
}
运行结果如下👇👇👇👇:
4.2 数据存储模块
在上面菜单写好后,我们看到输入1之后就还没有信息出来,接下来就是对数据进行存储。对于数据存储模块,就是要考虑该游戏是用什么样的数据结构类型。首先我们先看一下成品扫雷游戏的界面。从下面的图中可以看出,除开棋盘上的数据外,就是一个一个的空格组成的,是9*9的棋盘。我们通过C语言知识的所学,会很快联想到数组,并且是二维数组这样的结构。所以,这个棋盘可以看作是9行9列的二维数组。
- 既然知道了是用9*9的二维数组来实现的扫雷的,那我们就要对棋盘进行定义了。所以,我们就定义一个二维数组的棋盘,在这里我们为了考虑代码的灵活性,我们对数组定义的时候,不要把数组大小写死,用到宏定义,这样能更灵活。然而,还需要考虑一个问题,就是我们用一个数组到底可不可以???👀👀👀👀。代码如下:
布置雷的棋盘用字符 ‘0’ 初始化,显示雷的信息的棋盘用字符 ’*‘ 表示
#define Row 9
#define Col 9
#define Rows Row+2
#define Cols Col+2
//初始化棋盘的函数
void Init_Board(char Board_Mine[Rows][Cols], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Board_Mine[i][j] = set;
}
}
}
//打印棋盘信息的函数
void Print_Mine_Board(char Board_Mine[Rows][Cols], int row, int col)
{
printf("***** 扫雷游戏 *****\n");
for (int i = 0; i <= row; 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_Mine[i][j]);
}
printf("\n");
}
}
结果如下【布置雷的棋盘打印出来是看一下初始化成功没有,正式扫雷的时候是不打印出来的】👇👇👇:
4.3 布置雷模块
我们在上面已经把布置雷的棋盘打印出来后,现在就是需要把雷放在上面。在游戏信息里面说了,雷用字符 ‘1’ 表示,不是雷用字符 ‘0’ 表示。所以,我们需要在雷棋盘的二维数组中加一些字符 ‘1’ ,为了让雷的位置是随机的,需要用到随机函数来把雷的字符放入二维数组中,接下来直接上代码👉👉👉:
#define Mines 10
//布置雷的函数
void Set_Mines(char Board_Mine[Rows][Cols], int row, int col)
{
//布置雷的前提就是需要坐标,所以先定义坐标变量。
int x = 0;
int y = 0;
//现在需要雷的数量,为了代码灵活性,就用宏定义来规定雷的数量
int i = 0;
while (i < Mines)
{
//此处生成坐标就需要用到随机函数,需要时间戳函数,在主函数定义一次就可以了
x = rand() % row + 1;
y = rand() % col + 1;
//此处需要判断,就是看布置的位置是否是0,如果是0就布置在这位置,如果不是0,就说明随机函数生成的坐标把这个位置布置了
if (Board_Mine[x][y] == '0')
{
Board_Mine[x][y] = '1';
i++;//把i变量的调整放在后面,是为了防止雷的坐标重复,如果重复了就会导致雷的数量减少。
}
}
}
4.4 排查雷模块
既然我们把雷埋好以后,就该玩家排雷了吧!💪💪💪🧒。接下来我们需要在显示雷的数量的棋盘上排查,找出排查点周围8个点的雷的数量【下图是计算雷的个数的方法】。所以我们需要用到两个二维数组,把他们关联起来,先看代码👉:
//计算雷的数量
int GetMinesNumber(char Board_Mine[Rows][Cols], int x, int y)
{
return Board_Mine[x - 1][y - 1] + Board_Mine[x][y - 1] + Board_Mine[x + 1][y - 1] +
Board_Mine[x + 1][y] + Board_Mine[x + 1][y + 1] + Board_Mine[x][y + 1] + Board_Mine[x - 1][y + 1] +
Board_Mine[x - 1][y] - 8 * '0';
}
//排查雷的函数
void Find_Mines(char Board_Mine[Rows][Cols], char Show_Mine[Rows][Cols], int row, int col)
{
//怎么排查呢?当然是输入坐标了,所以得先定义坐标变量
int x = 0;
int y = 0;
//输入坐标吧,由于排雷不可能一次就成功,所以得用循环
int win = 0;
while (win<Row*Col-Mines)
{
printf("请输入排查坐标—>:\n");
scanf_s("%d %d", &x, &y);
//输入过后立马判断合法性
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//坐标合法后,就得看此坐标位置是否是雷,如果不是雷则计算周围8个位置有多少个雷
if (Board_Mine[x][y] != '1')
{
//判断是否重复输入同一个坐标
if (Show_Mine[x][y] == '*')
{
//计算雷的数量
int count = GetMinesNumber(Board_Mine,x,y);
//显示在另外一个棋盘上,先把返回来的数量赋值给另外个数组,由于返回的是整型,需要转为字符型,直接加上个字符0
Show_Mine[x][y] = count + '0';
//打印出来
Print_Mine_Board(Show_Mine, Row, Col);
win++;
}
else
{
printf("已经排查过了\n");
}
}
else
{
printf("很遗憾!你被炸死了!\n");
Print_Mine_Board(Board_Mine, Row, Col);
break;
}
}
else
{
printf("输入坐标不合法!请重新输入->:\n");
}
}
if (win == row * col - Mines)
{
printf("恭喜你!排雷成功!\n");
Print_Mine_Board(Board_Mine, Row, Col);
}
}
注意这种情况:
就这下面是正确的。写成下面这种会出现乱码:
写错后会出现下面错误结果👇👇👇:
5. 整体代码如下:
1.主函数文件 test_game.c
#include"game.h"
void menu()
{
printf("\t\t\t\t\t***************************\n");
printf("\t\t\t\t\t******** 扫雷游戏 *******\n");
printf("\t\t\t\t\t***************************\n");
printf("\t\t\t\t\t******** 1 : play *******\n");
printf("\t\t\t\t\t******** 0 : exit *******\n");
printf("\t\t\t\t\t***************************\n");
}
void game()
{
char Mine[Rows][Cols] = { 0 };//布置雷的棋盘。
char Show[Rows][Cols] = { 0 };//显示雷的信息。
//初始化棋盘
Init_Board(Mine, Rows, Cols, '0');
Init_Board(Show, Rows, Cols, '*');
//打印棋盘
//Print_Mine_Board(Mine,Row,Col);
//printf("\n");
Print_Mine_Board(Show,Row,Col);
//布置雷
Set_Mines(Mine, Row, Col);
//布置好雷后,打印看看是否成功
//Print_Mine_Board(Mine, Row, Col);
//排查雷
Find_Mines(Mine,Show, Row, Col);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do{
menu();
printf("请玩家选择—>:\n");
scanf_s("%d", &input);
switch (input)
{
case 1:
//具体游戏实现代码
game();
break;
case 0:
printf("欢迎下次再玩!\n");
break;
default:
printf("输入错误,请重新输入—>:\n");
break;
}
} while (input);
return 0;
}
2.game.c 函数具体实现文件
#include"game.h"
//初始化棋盘函数
void Init_Board(char Board_Mine[Rows][Cols], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
Board_Mine[i][j] = set;
}
}
}
//打印棋盘函数
void Print_Mine_Board(char Board_Mine[Rows][Cols], int row, int col)
{
printf("***** 扫雷游戏 *****\n");
for (int i = 0; i <= row; 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_Mine[i][j]);
}
printf("\n");
}
}
//布置雷的函数
void Set_Mines(char Board_Mine[Rows][Cols], int row, int col)
{
//布置雷的前提就是需要坐标,所以先定义坐标变量。
int x = 0;
int y = 0;
//现在需要雷的数量,为了代码灵活性,就用宏定义来规定雷的数量
int i = 0;
while (i < Mines)
{
//此处生成坐标就需要用到随机函数,需要时间戳函数,在主函数定义一次就可以了
x = rand() % row + 1;
y = rand() % col + 1;
//此处需要判断,就是看布置的位置是否是0,如果是0就布置在这位置,如果不是0,就说明随机函数生成的坐标把这个位置布置了
if (Board_Mine[x][y] == '0')
{
Board_Mine[x][y] = '1';
i++;//把i变量的调整放在后面,是为了防止雷的坐标重复,如果重复了就会导致雷的数量减少。
}
}
}
//计算雷的数量
int GetMinesNumber(char Board_Mine[Rows][Cols], int x, int y)
{
return Board_Mine[x - 1][y - 1] + Board_Mine[x][y - 1] + Board_Mine[x + 1][y - 1] +
Board_Mine[x + 1][y] + Board_Mine[x + 1][y + 1] + Board_Mine[x][y + 1] + Board_Mine[x - 1][y + 1] +
Board_Mine[x - 1][y] - 8 * '0';
}
//排查雷的函数
void Find_Mines(char Board_Mine[Rows][Cols], char Show_Mine[Rows][Cols], int row, int col)
{
//怎么排查呢?当然是输入坐标了,所以得先定义坐标变量
int x = 0;
int y = 0;
//输入坐标吧,由于排雷不可能一次就成功,所以得用循环
int win = 0;
while (win<Row*Col-Mines)
{
printf("请输入排查坐标—>:\n");
scanf_s("%d %d", &x, &y);
//输入过后立马判断合法性
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//坐标合法后,就得看此坐标位置是否是雷,如果不是雷则计算周围8个位置有多少个雷
if (Board_Mine[x][y] != '1')
{
//判断是否重复输入同一个坐标
if (Show_Mine[x][y] == '*')
{
//计算雷的数量
int count = GetMinesNumber(Board_Mine,x,y);
//显示在另外一个棋盘上,先把返回来的数量赋值给另外个数组,由于返回的是整型,需要转为字符型,直接加上个字符0
Show_Mine[x][y] = count + '0';
//打印出来
Print_Mine_Board(Show_Mine, Row, Col);
win++;
}
else
{
printf("已经排查过了\n");
}
}
else
{
printf("很遗憾!你被炸死了!\n");
Print_Mine_Board(Board_Mine, Row, Col);
break;
}
}
else
{
printf("输入坐标不合法!请重新输入->:\n");
}
}
if (win == row * col - Mines)
{
printf("恭喜你!排雷成功!\n");
Print_Mine_Board(Board_Mine, Row, Col);
}
}
- game.h 函数定义文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define Row 9
#define Col 9
#define Rows Row+2
#define Cols Col+2
#define Mines 10
//初始化棋盘函数
void Init_Board(char Board_Mine[Rows][Cols], int row, int col, char set);
//打印棋盘函数
void Print_Mine_Board(char Board_Mine[Rows][Cols], int row, int col);
//布置雷的函数
void Set_Mines(char Board_Mine[Rows][Cols], int row, int col);
//计算雷的数量
int GetMinesNumber(char Board_Mine[Rows][Cols], int row, int col);
//排查雷的函数
void Find_Mines(char Board_Mine[Rows][Cols], char Show_Mine[Rows][Cols], int row, int col);
6. 总结
以上是整个游戏代码,通过简单的扫雷游戏,可以加深对数组、函数、游戏设计思路的理解和使用。如果代码和描述有误的地方,请各位大佬指正。谢谢。