扫雷游戏的思维导图
以上是扫雷游戏的思维导图,接下来我会详细说一下思维步骤。
第一步,设置三个文件
分别是
minesweeper.c//测试文件(函数的大体都放在这里)
game.c//实现文件(主要是实现函数的逻辑)
game.h//头文件(函数的头文件一般放在这里面)
第二步,布置菜单
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*****************************\n");
printf("*******输入对应的数字********\n");
printf("*********1.开始游戏**********\n");
printf("*********0.退出游戏**********\n");
printf("*****************************\n");
}
int main()
{
menu(); //这里直接调用函数,下面一样 不过多赘述
return 0;
}
第三步,进入菜单(1 是开始 ,0 是退出)
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*****************************\n");
printf("*******输入对应的数字********\n");
printf("*********1.开始游戏**********\n");
printf("*********0.退出游戏**********\n");
printf("*****************************\n");
}
void test()
{
int input = 0;
scanf("%d", &input);
switch (input)
{
case 1: //这里的意思是,输入一个数字 如果是1 则进入game()函数。
game();
break;
case 0: //如果是0则打印退出函数,并且跳出函数
printf("退出游戏。");
break;
default: //如果输入其他数字,则打印输入错误,请重新输入,并且跳出函数
printf("输入错误,请重新输入");
break;
}
}
int main()
{
menu();
test();
return 0;
}
当然你也可以写成这样,但是要注意的,这样写需要把main里面的menu()去掉,免得多此一举。
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("游戏结束,退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
第四步,构建逻辑,game()函数的构建;
1.定义两个二维数组,并且初始化数组(同时在在头文件里面,定义数组的真实棋盘大小,和玩家操作票大小)
这里定义两个二维数组的目的是,如图,
这里情况清楚的看出来,如果只是一个棋盘无法做到排雷的时候,如何准确分辨,1是地雷,还是计算出来的以点击为中心的周围的地雷个数。
A;一个数组棋盘是给自己看的,全是字符'0000'的棋盘
全是字符'0',地雷是字符'1',方便布置地雷,称为mine
千万要记得,这里了不是1和0,这里是字符1和字符0,区别就在于加了''这个引号
(下面会详细用实例介绍)
B;一个全是******的棋盘,不显示地雷,命名为show
这个棋盘的目的
1,是给玩家看的棋盘。
2,是排查地雷。
为什么要数组,也就是为什么要布置两个棋盘的根本原因是,我们在
(下面会详细用实例介绍)
关于ASCII数值的计算
为什么是字符0和字符1 ,因为字符1和字符0的ASCII数值方便计算,在思维导图里面有一个ASCII数值计算的逻辑
简单说,这个逻辑就是,任意的字符ASCII数值减去字符0 可以获得这个字符的数字 ,当然加上字符0,也可以得到相应的字符
(下面会详细介绍ASCII码值)
2.尝试打印棋盘。
打印棋盘理论上是先布置地雷,后打印棋盘
这里先打印棋盘,后布置地雷
(这里打印棋盘的目的是方便布置地雷的时候观察到,是否布置成功,是否布置的是随机地雷。但是需要注意的是,在最后打印的时候,还是需要把展示给玩家的棋盘放在布置地雷后面)
(下面会详细用实例介绍)
3.布置地雷
这里布置地雷是吧地雷布置到mine里面,mine的棋盘是不展现给玩家的,只有show是展现给玩家的。
我们在mine里面布置地雷的时候,只需要用到时间戳,随机布置地雷,这里就不过多赘述关于什么是时间戳,在我的猜数字小游戏里面有说明,不懂的可以去看看。
srand((unsigned int)time(NULL));
int x = rand() % row + 1;
int y = rand() % col + 1;
这样就可以随机布置地雷
(下面会详细用实例介绍)
4.获得布置的地雷的个数
布置下去的地雷是利用时间戳布置的地雷,简单的说就是随机布置下去的,所以要获得以某一个坐标为中心,周围九个格子的地雷个数,此时就用到ASCII码值。
当然在布置地雷时候需要在头文件里面定义一个地雷 10 ,目的是方便修改代码,如果你采取的是数字,那样的情况下,如果代码多的情况下,就要修改很多数字。当然这里记得 定义的后面不加分号,这里涉及到define的使用,
define的使用就是的
#define +名字+数字
#define EASY_COUNT 10
在game()里面
int count = EASY_COUNT;
(下面会详细用实例介绍)
5.排查布置下去的地雷
这里是game()函数里面关键的一步,在整个函数里面game()是实现逻辑的关键,而排查地雷是函数实现的关键。
之所以排查布置下去的地雷,是因为,此时我们已经获得了mine里面随机点击一坐标,坐标周围九宫格地雷的个数,此时我们需要把mine里面的数值传递到show里面。简单的说,就是如下
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
令一个数值等于获得mine里面地雷的个数
GetMineCount这个函数里面实现的是获得布置的地雷的个数,然后利用ASCII码值的计算方法,反馈给show的二维数组,所以此时show二维数组棋盘就会显示周围几个地雷。
(下面会详细用实例介绍)
6.补充排查地雷的步骤
排查地雷肯定是一个循环,
那么进入循环输入的坐标,是不能超过9*9的棋盘,也不能小于1
而且需要知道此时还无法跳出游戏,也就是结束游戏,当所有的结果都被排查完毕的时候,可以如图
int win = 0;
while (win<Row*Col- EASY_COUNT)
{
}
Row*Col是定义的行和宽-地雷的个数,也就是说当win大于这个数值的时候,意味着不循环
那我可以给另外一个if语句,
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功!\n");
}
此时地雷全部排查完毕,显示游戏胜利
第五步,对game函数的详细解释
0.game函数里面,关于ASCII码值的介绍
为什么是字符0和字符1 ,因为字符1和字符0的ASCII数值方便计算,在思维导图里面有一个ASCII数值计算的逻辑
简单说,这个逻辑就是,任意的字符ASCII数值减去字符0 可以获得这个字符的数字 ,当然加上字符0,也可以得到相应的字符
在编程中,特别是在处理字符或字符串时,'0’通常被用来代表数字0。在扫雷游戏中,show数组可能用于显示雷区中的每个单元格的状态。如果count变量用来记录某个状态的数量,它可能是一个整数值。在某些编程语境中,为了将整型数值转换为字符型以显示或存储,需要将它与字符’0’进行运算,以便将其转换为相应的ASCII字符。
例如,在C语言中,整型数据和字符型数据之间的转换是非常常见的。如果你有一个整型变量count,并且你想要在控制台上打印出这个整数的字符表示形式,你需要将它转换为字符类型。在ASCII码表中,'0’的十进制值是48,所以如果你想要打印出数字0到9之间的任何一个数字,你可以通过将整型数字与’0’相加来得到相应的字符。例如,count + '0'会将整型变量count的值转换为相应的字符。
假设count为3,表达式count + '0'将得到字符’3’,因为3加上48('0’的ASCII值)等于51,也就是字符’3’的ASCII值。
在不同的编程语言和不同的上下文中,这种转换方式可能会有所不同,但基本原理是一致的:将整型数值转换为字符型以便在用户界面中显示。如果你是在特定的编程语言或特定的代码片段中遇到这个问题,提供具体的语言和代码样本将有助于更精确地解释为什么需要加上字符’0’。
1.game函数里面,关于定义二维数组,并且初始化数组(同时在在头文件里面,定义数组的真实棋盘大小,和玩家操作票大小)详细+代码
如下是第一步骤的三个文件
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*****************************\n");
printf("*******输入对应的数字********\n");
printf("*********1.开始游戏**********\n");
printf("*********0.退出游戏**********\n");
printf("*****************************\n");
}
void game()
{
//定义两个棋盘,一个是mine不打印出的棋盘,一个是show打印出来了的棋盘
char mine[Row][Col] = { 0 };
char show[Row][Col] = { 0 };
}
void test()
{
int input = 0;
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏。");
break;
default:
printf("输入错误,请重新输入");
break;
}
}
int main()
{
menu();
test();
return 0;
}
//game.h
//头文件
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//操作的棋盘长,宽
#define Row 9 //长
#define Col 9 //宽
//实际的的棋盘长,宽 这里是11*11 的棋盘,也就是实际操作的棋盘
#define Rows Row+2
#define Cols Col+2
//game.c
//定义
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
这里需要说明一下,为什么需要11*11的棋盘,而不是只是9*9的棋盘,原因在下面的图片,如果是9*9的棋盘,当需要计算周围几个地雷的时候,那就比较困难,不如给11*11的棋盘,但是布置地雷,和玩家玩儿的棋盘都在9*9里面,
但是传参的时候,是11*11,这样的话,计算周棋盘相加,等于几,不就是等于几个地雷了嘛!
#define Rows Row+2
#define Cols Col+2
char[Rows ][Cols ]={0};
初始化棋盘,
//初始化棋盘,这里防止冗余的情况下,给一个不是固定的参数set,这样的话,只需要一个循环,就可以实现两个棋盘的初始化,也可以同时实现两个棋盘的打印。
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*****************************\n");
printf("*******输入对应的数字********\n");
printf("*********1.开始游戏**********\n");
printf("*********0.退出游戏**********\n");
printf("*****************************\n");
}
void game()
{
//定义两个棋盘,一个是mine不打印出的棋盘,一个是show打印出来了的棋盘
char mine[Rows][Cols] = { 0 };
char show[Rows][Cols] = { 0 };
//初始化棋盘
InitBoard(mine, Rows, Cols, '0');
InitBoard(show, Rows, Cols, '*');
}
void test()
{
int input = 0;
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏。");
break;
default:
printf("输入错误,请重新输入");
break;
}
}
int main()
{
menu();
test();
return 0;
}
定义
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化数组
void InitBoard(char arr[Rows][Cols], int rows, int cols, int set)
{
for (int i = 0; i < rows; i++) //行
{
for (int j = 0; j < cols; j++) //列
{
arr[i][j] = set; //初始化成传参的 '0' '*' 也就是set
}
}
}
头文件
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//操作的棋盘长,宽
#define Row 9
#define Col 9
//实际的的棋盘长,宽
#define Rows Row+2
#define Cols Col+2
//初始化棋盘,这里防止冗余的情况下,给一个不是固定的参数set,这样的话,只需要一个循环,就可以实现两个棋盘的打印。
void InitBoard(char arr[Rows][Cols], int rows,int cols,int set);
这里需要强调一下,这里面已经引用了头文件#include"game.h",这个引用的目的是,可以调用game.h里面的头文件,
在定义里面因为是二维数组,所以要循环初始化数组,首先是初始化成行,在初始化成列,
//初始化成传参的 '0' '*' 也就是set,set是接收的形参。在源文件里面,''*' 0',传递的参数是实参,所以需要一个参数接收实参,也就是set,要接收传递的实参。而且set不是一个固定的字符,所以就给了很大的可变动性。
2.game函数里面,关于尝试打印棋盘。详细+代码
关于打印棋盘,需要知道的一点是,打印棋盘理应是放在布置地雷后面。
因为函数是从上往下进行的,如果吧打印棋盘放在布置地雷前面,就会导致棋盘打印出来了,地雷还没有布置上去。
但是这里先打印棋盘的目的是方便之后布置地雷的时候,观察到布置地雷是否成功布置上去,
同时需要知道,这里打印棋盘,最后的结果是只显示show的棋盘,不显示mine的棋盘,因为mine的棋盘里面,是放置地雷的地方。而show里面是显示周围有几颗地雷的地方。
关于打印棋盘接下来这一段代码是写的思路顺序,其次下面才是三个文件的总结,因为其实思路的顺序代表框架,这个往往是更重要的。
下来这一段代码是写的思路顺序
源函数
//打印棋盘 令这个函数为打印棋盘的函数DisplayBoard
DisplayBoard(mine, Row, Col);
DisplayBoard(show, Row, Col);
//放在game里面
void game()
{
//定义两个棋盘,一个是mine不打印出的棋盘,一个是show打印出来了的棋盘
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);
}
关于定义DisplayBoard
第一步这里是先把整个棋盘循环的打印出来,但是此时只是两个棋盘0 和*,并没有坐标,因为这个扫雷游戏是输入坐标排查地雷的,所以这里并没有坐标的显示,所以是不方便观察的,此时需要进行下一步
//第一步
//打印棋盘
void DisplayBoard(char arr[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", arr[i][j]);
}
}
}
第二步完善打印的棋盘
//打印棋盘
void DisplayBoard(char arr[Rows][Cols], int row, int col)
{
int i = 0;
int j = 0;
printf("---------扫雷---------\n");
for ( i = 0; i <= col; i++) //这里是打印棋盘的列 也就是横着打印的第一行0123456789
{
printf("%2d", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%2d", i); //这里是打印棋盘数字的行 也就是竖着的数字
for (j = 1; j <= col; j++)
{
printf("%2c", arr[i][j]);
}
printf("\n");
}
}
for ( i = 0; i <= col; i++) //这里是打印棋盘的列 也就是横着打印的第一行0123456789
至于这里为什么是先打印0 下面的循环是先打印1 那是因为如果这里是从1打印则导致,缺一个数字大家可以试着把这个for循环换成1就知道什么意思了。
这里补充的知识是,在二维数组里面 ,for的循环是先打印行,然后一行一行的打印,一行的数组打印满了,再换行打印,
第三步,关闭打印的棋盘
//打印棋盘
//DisplayBoard(mine, Row, Col);
//DisplayBoard(show, Row, Col);
总结这一段的代码
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*****************************\n");
printf("*******输入对应的数字********\n");
printf("*********1.开始游戏**********\n");
printf("*********0.退出游戏**********\n");
printf("*****************************\n");
}
void game()
{
//定义两个棋盘,一个是mine不打印出的棋盘,一个是show打印出来了的棋盘
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); //注意看这里
}
void test()
{
int input = 0;
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏。");
break;
default:
printf("输入错误,请重新输入");
break;
}
}
int main()
{
menu();
printf("请输入数字:");
test();
return 0;
}
定义文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘,
void InitBoard(char arr[Rows][Cols], int rows, int cols, int set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = set;
}
}
}
//打印棋盘
void DisplayBoard(char arr[Rows][Cols], int row, int col)
{
int i = 0;
int j = 0;
printf("---------扫雷---------\n");
for ( i = 0; i <= col; i++)
{
printf("%2d", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%2d", i);
for (j = 1; j <= col; j++)
{
printf("%2c", arr[i][j]);
}
printf("\n");
}
}
头文件
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//操作的棋盘长,宽
#define Row 9
#define Col 9
//实际的的棋盘长,宽
#define Rows Row+2
#define Cols Col+2
//初始化棋盘,这里防止冗余的情况下,给一个不是固定的参数set,这样的话,只需要一个循环,就可以实现两个棋盘的打印。
void InitBoard(char arr[Rows][Cols], int rows,int cols,int set);
//打印棋盘
void DisplayBoard(char arr[Rows][Cols], int row, int col);
这里面主要是新增了打印一个函数。
3.game函数里面,关于布置地雷。详细+代码
布置地雷这里比较简单,这里直接给代码,然后做出解释,在此之前,先再次讲述一下
时间戳。
rand
是随机数字的生成需要,原因是rand函数是对应一个叫做种子的基准,如果你想改变每次随机的数字,而不是只是第一次的数字,你就要改变种子的基准。
srand
在C语言里面又提供一个函数,叫做srand,是用来初始化随机数的生成器的也就是初始化rand
time
什么是time,简单说的就是时间,也就是srand的变化数值和时间有关系。time的意思就是,函数会返回当前的日历时间,简单说就是,返回的是1970年1⽉1⽇0时0分0秒到现在程序运⾏时间之间的差值,单位是秒。返回类型是time_t, time_t函数类型本质上就是32或者64位的整数类型。
而如果time是NULL,则就只是返回当前时间的差值。time返回的这个时间差也被成为;时间戳。time需要包含的头文件是time.h
所以组合起来就变成了
#include<stdio.h>//srand的头文件
#include<stdlib.h>//rand头文件
#include<time.h>//time的头文件
int main()
{
srand((unsigned int)time(NULL))
//这里面是time(null)也就是之前的时间到当前的时间,
时间每秒都会变化,所以srand也是每次都会变化,为什么
要前面加上unsigned int 因为有些编译器会显示又=有危险,
这时候我们强制转换就可以了。
printf("%d\n",rand());// 此时的数字是41
printf("%d\n",rand());//此时数字是18467
return 0;
}
所以下面上代码
第一步
//布置地雷
void SetMine(char arr[Rows][Cols], int row, int col)
{
int count = EASY_COUNT;//EASY_COUNT;这个是地雷的个数 10 在头文件里面定义了
srand((unsigned int)time(NULL));
int x = rand() % row + 1;
int y = rand() % col + 1; //这里就是随机布置地雷
if (arr[x][y]=='0') //这里是如果mine的棋盘是字符0,则考研课进入循环 随机布置一个地雷字符1
{
arr[x][y] = '1';
count--; //布置完成后,地雷个数减1
}
}
第二步
因为是十个地雷肯定是循环输入 所以完善循环
这里是只要while不等于0的时候一直循环 当while为0的时候 自动跳出循环
//布置地雷
void SetMine(char arr[Rows][Cols], int row, int col)
{
int count = EASY_COUNT;
srand((unsigned int)time(NULL));
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
总结3的代码
源代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*****************************\n");
printf("*******输入对应的数字********\n");
printf("*********1.开始游戏**********\n");
printf("*********0.退出游戏**********\n");
printf("*****************************\n");
}
void game()
{
//定义两个棋盘,一个是mine不打印出的棋盘,一个是show打印出来了的棋盘
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);
DisplayBoard(mine, Row, Col); //这里是打印出来让你自己看是否布置成功,此时布置成功
}
void test()
{
int input = 0;
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏。");
break;
default:
printf("输入错误,请重新输入");
break;
}
}
int main()
{
menu();
printf("请输入数字:");
test();
return 0;
}
定义文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘,
void InitBoard(char arr[Rows][Cols], int rows, int cols, int set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = set;
}
}
}
//打印棋盘
void DisplayBoard(char arr[Rows][Cols], int row, int col)
{
int i = 0;
int j = 0;
printf("---------扫雷---------\n");
for ( i = 0; i <= col; i++)
{
printf("%2d", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%2d", i);
for (j = 1; j <= col; j++)
{
printf("%2c", arr[i][j]);
}
printf("\n");
}
}
//布置地雷
void SetMine(char arr[Rows][Cols], int row, int col)
{
int count = EASY_COUNT;
srand((unsigned int)time(NULL));
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
头文件
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//操作的棋盘长,宽
#define Row 9
#define Col 9
//实际的的棋盘长,宽
#define Rows Row+2
#define Cols Col+2
//地雷的个数
#define EASY_COUNT 10
//初始化棋盘,这里防止冗余的情况下,给一个不是固定的参数set,这样的话,只需要一个循环,就可以实现两个棋盘的打印。
void InitBoard(char arr[Rows][Cols], int rows,int cols,int set);
//打印棋盘
void DisplayBoard(char arr[Rows][Cols], int row, int col);
//布置地雷
void SetMine(char arr[Rows][Cols], int row, int col);
这里就是地雷布置成功
4.game函数里面,关于获得布置的地雷的个数。详细+代码
获得地雷的个数,这里给讲解一下获得地雷的个数为什么要用到ASCII码值。因为首先我们需要计算输入一个坐标,作坐标中心九宫格有几个地雷,那么在此情况下,如果我们要计算周围地雷的个数,此时要计算九宫格的地雷个数,而计算需要知道ASCII码值,需要了解到,
等式:字符1-字符0=那个数字1
同理:字符=那个数字加上字符0
这里面,我们要计算周围的数组,只需要计算这些数字相应的字符加起来再减去8*‘0’,就可以得出相应的数字。
下面是代码
需要知道的是,排查地雷有两种写法,一种是循环的写法。一种是相加等于零的写法,这里主要描述第二种写法
第一种写法
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int count = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
count+=mine[i][j] - '0';
}
}
return count;
}
第二种写法
这里为什么要获得地雷的个数,原因是布置地雷的时候是随机布置的,所以我们不可能去精确布置地雷从而找到地雷,所以我们只能去循环的计算,或者返回值是计算出来周围的地雷的个数
同时我们需要知道,这里获得地雷的个数,包括每一次函数的实现,都可以纸在定义里面实现,不暴露在源文件里面。这都是可以的。
//获得地雷的个数
int GetMineCount(char mine[Rows][Cols], int x, int y)
{
return
mine[x - 1][y - 1] +
mine[x - 1][y] +
mine[x - 1][y + 1] +
mine[x][y - 1] +
mine[x][y + 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] - 8 * '0';
}
总结代码
只需要在布置地雷上面加上一个函数就可以,来回传参会很麻烦,这个函数不如直接给个函数,直接在定义文件里面使用。
定义文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘,
void InitBoard(char arr[Rows][Cols], int rows, int cols, int set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = set;
}
}
}
//获得地雷的个数
int GetMineCount(char mine[Rows][Cols], int x, int y)
{
return
mine[x - 1][y - 1] +
mine[x - 1][y] +
mine[x - 1][y + 1] +
mine[x][y - 1] +
mine[x][y + 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] - 8 * '0';
}
//打印棋盘
void DisplayBoard(char arr[Rows][Cols], int row, int col)
{
int i = 0;
int j = 0;
printf("---------扫雷---------\n");
for ( i = 0; i <= col; i++)
{
printf("%2d", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%2d", i);
for (j = 1; j <= col; j++)
{
printf("%2c", arr[i][j]);
}
printf("\n");
}
}
//布置地雷
void SetMine(char arr[Rows][Cols], int row, int col)
{
int count = EASY_COUNT;
srand((unsigned int)time(NULL));
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
这里需要知道的是,其他文件不需要有其他操作,所以其他的文件不需要改变。所以就没有写在上面。
5.game函数里面,关于排查布置下去的地雷。详细+代码
排查布置下去的地雷是最关键的一步,这里是扫雷的关键。
这里首先我们给出一个条件就是如果输入,x,y的坐标是大于1,小于等于9的情况下,才能进入循环,否则是不能进入循环。如果输入是超出的坐标的,打印输输入错误,请重新输入。
其次进入循环条件之后,我们需要知道只有当show【x】【y】不等于地雷 也就是字符*,的时候,才会继续如果等于字符1,也就是等于地雷,则被炸死。
之后我们需要认识到这个是一个循环,但是什么时候跳出循环,那就是要么地雷等于0的时候,要么被炸死。或者,定义一个数字,这个数字++,循环的次数大于减去地雷的个数的时候,则跳出循环。
此时需要一个条件承接跳出循环 的条件,那就是恭喜你完成游戏,并且取得胜利。
最后承接这个条件之后,在里面打印出棋盘。
第一步
void FineMine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col)
{
int x = 0;
int y = 0;
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
}
else
{
printf("输入的坐标有误,请重新输入。");
}
}
第二步
此时还没有进入循环
此时是当点击的坐标是1 也就是地雷的时候,炸死
如果不是这种情况则
1.获得mine里面的地雷的个数
2.show的棋盘上面显示出来获得的地雷的个数(这里只有加上字符0,才能显示的是地雷的个数,因为计算的时候是按照数计算的,所以显示的时候需要减去字符0,上面已经解释过)
3.显示棋盘
/排查地雷
void FineMine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col)
{
int x = 0;
int y = 0;
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y]=='1')
{
printf("恭喜你被炸死了。");
DisplayBoard(mine, Row, Col);
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, Row, Col);
}
}
else
{
printf("输入的坐标有误,请重新输入。");
}
}
第三步
补充代码,并且明确,这里是一个循环
//排查地雷
void FineMine(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 <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("恭喜你被炸死了。");
DisplayBoard(mine, Row, Col);
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, Row, Col);
}
}
else
{
printf("输入的坐标有误,请重新输入。");
}
}
}
此时的效果是这样的,也就是完成了基本的扫雷模型基本完成,但是还需要完善
6.game函数里面,关于补充排查地雷的步骤。详细+代码
怎么跳出循环
//排查地雷
void FineMine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
printf("请输入一个坐标,开始排查地雷:");
scanf("%d %d", &x, &y);
while (win<row*col- EASY_COUNT)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("恭喜你被炸死了。");
DisplayBoard(mine, Row, Col);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, Row, Col);
}
}
else
{
printf("输入的坐标有误,请重新输入。");
break;
}
}
if (win == row * col - EASY_COUNT) //如果等于的时候说明已经全部排雷成功
{
printf("恭喜你,排雷成功!\n");
DisplayBoard(mine, Row, Col); //打印棋盘
}
}
这里面补充如果输入重复坐标,怎么处理
//排查地雷
void FineMine(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 (show[x][y]=='*')
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("恭喜你被炸死了。");
DisplayBoard(mine, Row, Col);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, Row, Col);
win++;
}
}
else
{
printf("输入的坐标重复,请重新输入。");
break;
}
}
else
{
printf("坐标错误,请重新输入。");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功!\n");
DisplayBoard(mine, Row, Col);
}
}
printf("请输入一个坐标,开始排查地雷:");
scanf("%d %d", &x, &y);
这里需要知道的是,关于这一步,输入,需要在循环里面,不能在循环外面,因为在循环外面代表输入一次一直循环,但是在里面代表一次结束需要下一次的输入才能进行下一次的输入才能继续循环。
到这里就是总结代码,但是因为已经是最后一步了,就不进行二次总结了,直接看最下面。
第六步,总结代码
这里总结代码,一个是总结到一个文件里面,一个是总结到三个文件里面,这里因为我直接是最后把三个文件直接合并的,所以可能会有瑕疵。当然要是学习的话,最好还是看这个三个文件的代码
放到一个文件里面的总结
一个文件,直接复印出结果
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define Row 9
#define Col 9
#define Rows Row + 2
#define Cols Col + 2
#define EASY_COUNT 10
char mine[Rows][Cols] = { 0 };
char show[Rows][Cols] = { 0 };
void initBoard(char arr[Rows][Cols], int rows, int cols, int set) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
arr[i][j] = set;
}
}
}
void displayBoard(char arr[Rows][Cols], int row, int col) {
int i = 0;
int j = 0;
printf("---------扫雷---------\n");
for (i = 0; i <= col; i++) {
printf("%2d", i);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%2d", i);
for (j = 1; j <= col; j++) {
printf("%2c", arr[i][j]);
}
printf("\n");
}
}
void setMine(char arr[Rows][Cols], int row, int col) {
int count = EASY_COUNT;
srand((unsigned int)time(NULL));
while (count) {
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0') {
arr[x][y] = '1';
count--;
}
}
}
int getMineCount(char mine[Rows][Cols], int x, int y) {
return
mine[x - 1][y - 1] +
mine[x - 1][y] +
mine[x - 1][y + 1] +
mine[x][y - 1] +
mine[x][y + 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] - 8 * '0';
}
void fineMine(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 (show[x][y] == '*') {
if (x >= 1 && x <= row && y >= 1 && y <= col) {
if (mine[x][y] == '1') {
printf("恭喜你被炸死了。\n");
displayBoard(mine, Row, Col);
break;
}
else {
int count = getMineCount(mine, x, y);
show[x][y] = count + '0';
displayBoard(show, Row, Col);
win++;
}
}
else {
printf("输入的坐标重复,请重新输入。\n");
break;
}
}
else {
printf("坐标错误,请重新输入。\n");
}
}
if (win == row * col - EASY_COUNT) {
printf("恭喜你,排雷成功!\n");
displayBoard(mine, Row, Col);
}
}
void menu() {
printf("*****************************\n");
printf("*******输入对应的数字********\n");
printf("*********1.开始游戏**********\n");
printf("*********0.退出游戏**********\n");
printf("*****************************\n");
}
int main() {
int input = 0;
while (1) {
menu();
printf("请输入数字:");
scanf("%d", &input);
switch (input) {
case 1:
initBoard(mine, Rows, Cols, '0');
initBoard(show, Rows, Cols, '*');
setMine(mine, Row, Col);
displayBoard(show, Row, Col);
fineMine(mine, show, Row, Col);
break;
case 0:
printf("退出游戏。\n");
return 0;
default:
printf("输入错误,请重新输入。\n");
break;
}
}
}
放到三个文件里面的总结
源文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
printf("*****************************\n");
printf("*******输入对应的数字********\n");
printf("*********1.开始游戏**********\n");
printf("*********0.退出游戏**********\n");
printf("*****************************\n");
}
void game()
{
//定义两个棋盘,一个是mine不打印出的棋盘,一个是show打印出来了的棋盘
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);
//DisplayBoard(mine, Row, Col);
DisplayBoard(show, Row, Col);
//排查地雷
FineMine(mine, show, Row, Col);
}
void test()
{
int input = 0;
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏。");
break;
default:
printf("输入错误,请重新输入");
break;
}
}
int main()
{
menu();
printf("请输入数字:");
test();
return 0;
}
定义文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化棋盘,
void InitBoard(char arr[Rows][Cols], int rows, int cols, int set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = set;
}
}
}
//打印棋盘
void DisplayBoard(char arr[Rows][Cols], int row, int col)
{
int i = 0;
int j = 0;
printf("---------扫雷---------\n");
for ( i = 0; i <= col; i++)
{
printf("%2d", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%2d", i);
for (j = 1; j <= col; j++)
{
printf("%2c", arr[i][j]);
}
printf("\n");
}
}
//布置地雷
void SetMine(char arr[Rows][Cols], int row, int col)
{
int count = EASY_COUNT;
srand((unsigned int)time(NULL));
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
//获得地雷的个数
int GetMineCount(char mine[Rows][Cols], int x, int y)
{
return
mine[x - 1][y - 1] +
mine[x - 1][y] +
mine[x - 1][y + 1] +
mine[x][y - 1] +
mine[x][y + 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] - 8 * '0';
}
//排查地雷
void FineMine(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 (show[x][y]=='*')
{
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("恭喜你被炸死了。");
DisplayBoard(mine, Row, Col);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, Row, Col);
win++;
}
}
else
{
printf("输入的坐标重复,请重新输入。");
break;
}
}
else
{
printf("坐标错误,请重新输入。");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功!\n");
DisplayBoard(mine, Row, Col);
}
}
头文件
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//操作的棋盘长,宽
#define Row 9
#define Col 9
//实际的的棋盘长,宽
#define Rows Row+2
#define Cols Col+2
//地雷的个数
#define EASY_COUNT 10
//初始化棋盘,这里防止冗余的情况下,给一个不是固定的参数set,这样的话,只需要一个循环,就可以实现两个棋盘的打印。
void InitBoard(char arr[Rows][Cols], int rows,int cols,int set);
//打印棋盘
void DisplayBoard(char arr[Rows][Cols], int row, int col);
//布置地雷
void SetMine(char arr[Rows][Cols], int row, int col);
//排查地雷
void FineMine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col);