这里写目录标题
- 前言
- 1.初始化棋盘
- 2.展示棋盘
- 3.布置雷
- 4.开始扫雷
- 4.1判断输赢
- 4.2扫雷时连续性展开
- 4.3展示玩法
- 5.整体代码展示
- 5.1 game.h头文件展示
- 5.2 game.c源文件展示
- 5.3 text.c源文件展示
所属专栏:C语言
博主首页:初阳785
代码托管:chuyang785
感谢大家的支持,您的点赞和关注是对我最大的支持!!!
博主也会更加的努力,创作出更优质的博文!!
关注我,关注我,关注我,重要的事情说三遍!!!!!!!!
前言
- 我们的扫雷和三子棋也是有一点形同之处的。
- 就比如我们写的菜单其实是形同的,这里就不多讲了。
- 直接上菜单:
void menu()
{
printf("***********************\n");
printf("******** 1.play *******\n");
printf("******** 0.exit *******\n");
printf("***********************\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
printf("选择错误,请重新选择:>");
default:
break;
}
} while (input);
return 0;
}
-
这个可以说是一个模板了,我们之前写的猜数字游戏和三子棋游戏都是用到了这个模板的,现在我们的扫了游戏也用到了,可以说是固定搭配了,小伙伴们可以慢慢品味。
-
再看我我们的引用的头文件:
#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 MIN_NUM 10
1.初始化棋盘
- 要先写出扫雷游戏,我们必须要先知道扫雷游戏的规则,这里相信大家都有过了解,所以这里就不做过多的解释。‘
- 如图这是一个简单的9×9的扫雷游戏,同样的要设计出一个二维数组。
- 而以上图片的格子下面可能存放着雷,但是如果只有有一个二维数组的话,我们不但要存放我们用来遮挡雷的格子还有在各自下面存放我们的雷,似乎一个数组是不够用的。
- 所以我们要定义两个二维数组,一个数组用来存放挡住雷的格子,另一个数组用来存放雷的个数。
- 而我们就是通过两个数组之间的关联,通过布置雷数组来给布置格子的数组信息,从而达到我们想要的效果。
- 再看上面的图片我们看到了当我们点击不是雷的地方的时候,在有雷的地方的周围会有与之对应雷个数的数字,而这个范围是在以这个数字为中心,判读这个数周围8个格子有多少个雷。
- 有了这个思路我们就可以一一遍历一个个格子的周围的8个格子,来判盘断雷的个数。
- 但是是不是每个格子都可以判断上判断8次呢?
- 如果是子在角上或者边边子上呢?还会判断8次吗。显然并不会,所以这里我们就想出了一个好办法,那就是在这个9×9的棋盘外边在加上一圈,让他每个格子都能遍历上8次。
于是我们就要定义两个数组:
//存放雷的棋盘
Initboard(min, ROWS, COLS,'0');
//展示的棋盘
Initboard(shar, ROWS, COLS,'*');
这里我们用字符’*'来当作我我们遮挡雷的格子,而我们布置了的棋盘用字符’0’初始化。
void Initboard(char board[ROWS][COLS], int rows, int cols, char ret)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = ret;
}
}
}
2.展示棋盘
- 这里要注意的就是,我们要的是9×9的棋盘,但是我们创建的时候是创建的11×11的,原因是我们在原来的期盼的周围有多加了两行两列,使其在吧遍历雷的个数的时候更方便。
- 而我们创建出来的期盼的下标是0-10,而我们真正要的下标是1-9,所以我们在打印处期盼的时候得控制一下范围。
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("--------扫雷游戏-------\n");
int i = 0;
for (i = 0; i <= row; i++)
{
printf("%d ", i);
if (i == 0)
printf("|");
}
printf("\n");
for (i = 0; i <= row; i++)
{
printf("--");
if (i == 0)
printf("|");
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%-2d|", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
- 因为我们玩的时候是输入下标的,所以我们可以适当的给我们的棋盘添加一下行数和列数:
- 展示效果
3.布置雷
- 我们布置雷的是放入字符’1’的,而我放置雷的坐标是随机的,这个时候又要用到我们的随机函数srand和rand()。
- 同时对生成的随机数也是有要求的,就是不能越界。
void SetMin(char min[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = MIN_NUM;
while (count)
{
x = rand() % row + 1;
y = rand() % col + 1;
//放置生成的随机数越界了。
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (min[x][y] == '0')
{
min[x][y] = '1';
count--;
}
}
}
//这个是不用打印出来的,这里打印出来是观察一下我们布置雷有没有成功。
//DisplayBoard(min, ROW, COL);
}
4.开始扫雷
4.1判断输赢
- 我们赢的方式只有一种就是吧除雷以外的格子都排除了就赢了。
- 也就是说我们要排除ROW*COL-MIN_NUM个格子才算赢了。
void PlayGame(char min[ROWS][COLS], char shar[ROWS][COLS], int row, int col)
{
int win = 0;
while (win<ROW*COL-MIN_NUM)
{
int x = 0;
int y = 0;
printf("请输入坐标,中间用空格隔开>:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (min[x][y] == '1')
{
printf("抱歉,你被炸死了\n");
//被炸死了,打印除布置雷的情况
DisplayBoard(min, ROW, COL);
break;
}
if (shar[x][y] != '*')
{
printf("输入坐标重已显示,请重新输入\n");
break;
}
//x,y坐标不是雷
spand(min, shar,x, y,ROW,COL, &win);
DisplayBoard(shar, ROW, COL);
}
else
{
printf("输入坐标错误,请重新输入\n");
}
if (win == ROW * COL - MIN_NUM)
printf("恭喜你,成功过关\n");
}
}
4.2扫雷时连续性展开
- 你有没有发现在我们玩扫雷的时候,有时候我们点了一个它就会给你蹦出来好多的没有雷的格子。
- 而它的原理就是一个格子不是雷,而且这个格子周围的格子也没有雷就展开。
- 所以这里我们就可以用到递归的思想。
- 而递归的条件又三点
- 这个坐标不是雷。
- 这个坐标周围的坐标不是雷
- (重点)用过了坐标不需要再递归。
其中第三点最为重要,如果处理不当的会很容易陷入死递归。
及比如:
我们刚开始遍历了黄色的圈,以这个圈展开再以这个圈周围不是雷的坐标开始递归,我们拿出一个来讲,就比如上面的红色的圈。这个红色的圈不是雷我们又开始以这个红色的圈为中心,判断周围的坐标是不是雷,但是我们要知道的是我们的黄色的圈也是再红色圈的范围的,如果再次进行递归,这样就形成了死递归了。所以我们得外加一条判断语句。
void spand(char min[ROWS][COLS],char shar[ROWS][COLS], int x, int y,int row,int col, int* count)
{
//防止越界
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
int ret = TheMieCount(min, x, y);
if (ret == 0)
{
shar[x][y] = ' ';
(*count)++;
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (shar[i][j] == '*')//避免已经递归过的再次递归,以放置死递归
{
spand(min, shar, i, j, ROW, COL,count);
}
}
}
}
else
{
shar[x][y] = ret + '0';
(*count)++;
}
}
}
4.3展示玩法
5.整体代码展示
5.1 game.h头文件展示
#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 MIN_NUM 10
//初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char ret);
//展示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
void SetMin(char min[ROWS][COLS],int row,int col);
//开始扫雷
void PlayGame(char min[ROWS][COLS],char shar[ROWS][COLS],int rows,int cols);
5.2 game.c源文件展示
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void Initboard(char board[ROWS][COLS], int rows, int cols, char ret)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = ret;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("--------扫雷游戏-------\n");
int i = 0;
for (i = 0; i <= row; i++)
{
printf("%d ", i);
if (i == 0)
printf("|");
}
printf("\n");
for (i = 0; i <= row; i++)
{
printf("--");
if (i == 0)
printf("|");
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%-2d|", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void SetMin(char min[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = MIN_NUM;
while (count)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (min[x][y] == '0')
{
min[x][y] = '1';
count--;
}
}
}
//DisplayBoard(min, ROW, COL);
}
int TheMieCount(char min[ROWS][COLS], int x, int y)
{
return (min[x - 1][y] + min[x - 1][y - 1] + min[x][y - 1] +
min[x - 1][y + 1] + min[x + 1][y] + min[x + 1][y + 1] +
min[x][y + 1]+min[x+1][y-1] - 8 * '0');
}
//实现扩展
void spand(char min[ROWS][COLS],char shar[ROWS][COLS], int x, int y,int row,int col, int* count)
{
//防止越界
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
int ret = TheMieCount(min, x, y);
if (ret == 0)
{
shar[x][y] = ' ';
(*count)++;
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (shar[i][j] == '*')
{
spand(min, shar, i, j, ROW, COL,count);
}
}
}
}
else
{
shar[x][y] = ret + '0';
(*count)++;
}
}
}
void PlayGame(char min[ROWS][COLS], char shar[ROWS][COLS], int row, int col)
{
int win = 0;
while (win<ROW*COL-MIN_NUM)
{
int x = 0;
int y = 0;
printf("请输入坐标,中间用空格隔开>:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (min[x][y] == '1')
{
printf("抱歉,你被炸死了\n");
DisplayBoard(min, ROW, COL);
break;
}
if (shar[x][y] != '*')
{
printf("输入坐标重已显示,请重新输入\n");
break;
}
//x,y坐标不是雷
spand(min, shar,x, y,ROW,COL, &win);
DisplayBoard(shar, ROW, COL);
}
else
{
printf("输入坐标错误,请重新输入\n");
}
if (win == ROW * COL - MIN_NUM)
printf("恭喜你,成功过关\n");
}
}
5.3 text.c源文件展示
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("***********************\n");
printf("******** 1.play *******\n");
printf("******** 0.exit *******\n");
printf("***********************\n");
}
void game()
{
//初始化棋盘
char min[ROWS][COLS];
char shar[ROWS][COLS];
//存放雷的棋盘
Initboard(min, ROWS, COLS,'0');
//展示的棋盘
Initboard(shar, ROWS, COLS,'*');
//打印棋盘
//DisplayBoard(min, ROW, COL);
DisplayBoard(shar, ROW, COL);
//布置雷
SetMin(min, ROW, COL);
//开始玩,输入坐标
//计算雷的个数
PlayGame(min, shar, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
printf("选择错误,请重新选择:>");
default:
break;
}
} while (input);
return 0;
}