扫雷游戏的实现分为:初始化游戏界面; 打印游戏界面;,布置雷,排查雷随便判断游戏胜负.
实现游戏的基础设置
为了方便我们后面修改游戏难度,我们应该把雷的个数(MINE_COUNT),游戏界面的大小在头文件表示出来 `
#define MINE_COUNT 10
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
ROW代表行,COl代表列
为什么会有ROWS和COLS呢?
我们在遇到这种在边角的情况时,如果我们使用9行9列作为数组的大小时,我们发现在遍历数字1的九宫格会发生数组越界.
解决方法
我们把数组的行和列分别加2,数组就会覆盖整个界面,不会发生这种情况*
为什么要用两个二维数组
关于游戏的设置,我想使用两个二维数组.就图形来看,我们貌似只需要一个,但是大家有没有想过.如果我们只设置一个二维数组.这个数组要实现的功能有:
1.要有雷存放的信息
2.玩家选定坐标后,如果该位置没有雷,要判断该坐标周围的九宫格有多少雷
3.还需要一些类似游戏中的一个个盖子来制造神秘感.
综上,我们使用两个二维数组,一个用来存放雷的信息,另外一个给玩家展示,也就是我们的游戏界面.
初始化棋盘
void menu() {
printf("**********************************\n");
printf("*********** 1. play ************\n");
printf("*********** 0. exit ************\n");
printf("**********************************\n");
}
int main() {
int input = 0;
do {
srand((unsigned int)time(NULL));//设置rand的起点
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input) {
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
*首先,我们要打印一个菜单,判断玩家要不要玩游戏.*这里我们使用input接收,用switch语句判断.
设置两个二维数组.
注意我设置的是字符数组
雷(Mine)----数组
我们要认识一点,数组传参传的是数组首元素的地址,所以我们要把数组的行和列(大小)也进行传参. 在这个数组里面我想用1来表示有雷,0表示安全,所以把字符 ' 0 ' 传过去
游戏界面(Show)----数组
在游戏刚刚开始的时候,我们什么数字也看不到,所以要先用' * '来制造神秘.
记得要在头文件里面声明
初始化的代码实现
//初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set) {
int i = 0, j = 0;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
board[i][j] = set;
}
}
}
将两个数组全部进行初始化. 发现了没有,上面把字符' 0 '或者' * '也传过去是为了多次调用Initboard这个函数.使我们的代码更加简洁
展示游戏界面
初始化之后要打印出来看看我们写的是否正确,同时将我们的游戏界面展现出来
我们打印的是9*9的,所以把ROW和COL传过去就可以.
上面圈出来的代码是为了实现行号和列号的打印
布置雷
我们上面设置的MINE_COUNT的值为10.所以写一个循环,让它把这10个雷给布置完就行
注意
1.rand()函数的使用要应用2个头文件,并用srand()为rand()设置起点
2.这里的循环不止10次,因为两次产生的随机数可能相同 3.玩家输入的坐标是由1到ROW的,所以要进行+1操作
void Setmine(char board[ROWS][COLS], int row, int col) {
int cnt = MINE_COUNT;
while(cnt>0) {
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
if (board[x][y] == '0') {
board[x][y] = '1';
cnt--;
}
}
}
排雷
排雷是这个游戏的精髓,也是这个游戏最难实现的地方,因为它还嵌套了许多函数让我们一个个来分析.
模拟一次排雷的情况
1.玩家先输入坐标,我们要先判断这个坐标是否在我们数组的范围内
2.然后再去判断Mine数组该坐是否有雷(是否为'1'),如果有雷,那游戏结束.打印雷的分布让玩家死得明白
3.如果非雷,我们要对雷区进行清除,提高玩家的游戏体验
点击一个坐标,其他地方也会暴露出来
我们发现展现的地方遇到边角或者数字就会停留下来.而数字代表的是在那个九宫格里面有雷.
为了方便我们后面的操作,我们先写一个函数来计算一个坐标的九宫格有几个雷
我们对九宫格里面所有字符进行叠加.9个字符进行叠加,字符的ASCLL码叠加,再减去八个字符零,就能得到一圈雷的个数(字符形式)
比如' 0 '的ASCLL值为48,假设一圈都没有雷,九个字符0就是948,
948-848 = 48---' 0 '因为我们刚开始创建的字符数组.
排雷一下子灭掉一片的实现
灭掉一片的前提条件是:
Countmine传回来的是'0',也就是该位置以及周围一圈都没有雷,
用空格取代' * '的原因
像上面所画的一样,我们对第一个红色矩形所围住进行遍历时,发现它符合条件,所以进入递归.进入递归后又会进入刚才红色矩形,也就是红圈圈所画位置
通俗一点来说就是会不断套娃,永无止境,程序就会崩溃
递归是要有终止条件的,不然回不了家.空格相当于标记,在下一次递归发现该坐标已经被标记过就不会再进入----这个跟我们在迷路时对该地方做标记是一样的
上面代码函数Optimize的返回类型是void,不是int
实现多次排雷
注意:游戏的结束有两种情况,所以在打印游戏胜利时要加判断条件
游戏总代码的实现
头文件 game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MINE_COUNT 10
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set);
//展示棋盘
void Displayboard(char board[ROWS][COLS], int row, int col);
//布雷
void Setmine(char board[ROWS][COLS], int row, int col);
//排雷
void GuessMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//数周围一圈雷的个数
char Countmine(char Mine[ROWS][COLS], int x, int y);
//一下子灭掉一大片
void Optimize(char Mine[ROWS][COLS], char Show[ROWS][COLS], int x, int y);
主函数 test.c
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void game() {
char Mine[ROWS][COLS];
char Show[ROWS][COLS];
//初始化棋盘
Initboard(Mine, ROWS, COLS, '0');
Initboard(Show, ROWS, COLS, '*');
//展示棋盘
Displayboard(Show, ROW, COL);
//设置雷
Setmine(Mine, ROW, COL);
//Displayboard(Mine, ROW, COL);
//排雷
GuessMine(Mine, Show, ROW , COL);
}
void menu() {
printf("**********************************\n");
printf("*********** 1. play ************\n");
printf("*********** 0. exit ************\n");
printf("**********************************\n");
}
int main() {
int input = 0;
do {
srand((unsigned int)time(NULL));
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input) {
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
游戏函数的实现 game.c
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
//初始化棋盘
void Initboard(char board[ROWS][COLS], int rows, int cols, char set) {
int i = 0, j = 0;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
board[i][j] = set;
}
}
}
//打印棋盘
void Displayboard(char board[ROWS][COLS], int row, int col) {
printf("-------扫雷---------\n");
int i = 0, j = 0;
for (j = 0; j <= row; j++) {
printf("%d ", j);
}
printf("\n");
for (i = 1; i <= row; i++) {
printf("%d ", i);
for (j = 1; j <= col; j++) {
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-------扫雷---------\n");
}
//布雷
void Setmine(char board[ROWS][COLS], int row, int col) {
int cnt = MINE_COUNT;
while(cnt>0) {
int x = rand() % 9 + 1;
int y = rand() % 9 + 1;
if (board[x][y] == '0') {
board[x][y] = '1';
cnt--;
}
}
}
//数周围一圈雷的个数
char Countmine(char Mine[ROWS][COLS], int x, int y) {
int i = 0;
int j = 0;
int sum = 0;
char cnt = 0;
for (i = x - 1; i <= x + 1; i++) {
for (j = y - 1; j <= y + 1; j++) {
sum += Mine[i][j];
}
}
cnt = sum - 8 * '0';
return cnt;
}
//一下子灭掉一大片
void Optimize(char Mine[ROWS][COLS], char Show[ROWS][COLS], int x, int y) {
int i = 0;
int j = 0;
char cnt = Countmine(Mine, x, y);
if (cnt == '0'&& Show[x][y] != ' ') {
Show[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++) {
for (j = y - 1; j <= y + 1; j++) {
if (i >= 0 && j >= 0 && i <= 9 && j <= 9) {
Optimize(Mine, Show, i, j);
}
}
}
}
if (cnt != '0') {
Show[x][y] = cnt;
}
}
//排雷
void GuessMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int leftover = row * col - MINE_COUNT;
while (leftover>0) {
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= 9 && y >= 1 && y <= 9) {
if (Mine[x][y] == '1') {
printf("你被炸死了,游戏结束\n");
Displayboard(Mine, ROW, COL);
break;
}
else {
Optimize(Mine, Show, x, y);
Displayboard(Show, ROW, COL);
leftover--;
}
}
else {
printf("坐标非法,请重新输入\n");
}
}
if (leftover == 0) {
printf("游戏胜利!\n");
Displayboard(Mine, ROW, COL);
}
}