今天给大家带来的c语言实现的简易版扫雷,和上次的大体框架基本一样。接下来就开始实现扫雷游戏。
首先,还是一样先打开我们的编译器,创建一个空项目,在这个项目里面,我们添加三个文件,game2.h头文件,game2.c和test.c的源文件。这样分模块去写代码,项目的整体框架,思路,逻辑可以很好的展现出来,我们将所有你需要使用的头文件以及函数的声明都包含在game2.h中,在game2.c中去实现各类函数的功能,在test.c中去测试整个2代码。
和上次写的三子棋的游戏界面的选择是基本相同的,还是通过do while和switch语句实现。
void munu()
{
printf("*******************\n");
printf("**** 1.play *******\n");
printf("**** 0.exit *******\n");
printf("*******************\n");
}
int main()
{
int input = 0;
do
{
munu();
printf("请选择:\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷游戏开始!\n");
game();
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("选择有误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
这个游戏的界面的选择设计好之后,我们要设计扫雷的实现,这次给大家展示9*9版的简易扫雷。
这是个9*9的一个棋盘,我们可以很容易想到将其抽象成一个二维数组来实现,我们来分析这个游戏,玩家在选择一个位置,如果这个位置是雷,那么就会显示”你被雷炸死了“, 如果不是雷,你选择的位置将会显示一个数字,这个数字代表了你所选择的位置的周围8个位置有几个雷,就这样一直排雷直到到最后你将所有不是雷的地方找出来,你就获胜了。游戏的大概就是这样的逻辑,那么我们怎么样去实现呢?
首先是棋盘的设计,为了让逻辑更清晰,我们可以设计两个相同的二维数组,其中一个数组来初始化棋盘将雷布置好,另一个数组中的相同位置存放排除雷的信息。接下来就是创建两个二维数组,那么这两个数组应该创建多大呢?(注意:我们可以想到在遍历最外层的位置时,如果我们设计的是9*9的数组,那么会出现数组越界的情况,这是不行的,那我们可以在9*9的基础上在外围加上一圈,这些位置在初始化时候不防雷,那么问题就解决了)
接下来就是创建二维数组
#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 };//存放排出的雷的信息
虽然我们设计了11*11的数组,但是我们在使用时候还是用到它的9*9的位置,为了方便,我定义了这些宏。
数组创建完成后就是对它初始化,设计函数InItBoard()对两个二维数组初始化,将存放雷的数组初始化全为‘0’,存放排雷信息的数组初始化为‘*’;
char mine[ROWS][COLS] = { 0 };//存放雷的信息
char show[ROWS][COLS] = { 0 };//存放排出的雷的信息
//初始化棋盘
InItBoard(mine,ROWS,COLS,'0');
InItBoard(show, ROWS, COLS,'*');
void InItBoard(char board[ROWS][COLS], int rows, int cols,char set) //函数的设计
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] =set;
}
}
}
接下来是实现打印棋盘的功能,设计函数DisplayBoard()这个函数要打印棋盘的行标和列标,如下
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf(" ");
for (i = 1; i <= 9; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("-------------------------------\n");
}
接下来就是存放雷。这里我们设计一个函数SetMine()那么这个雷的位置信息应该是随机的,所以我们想到了c语言中生成随机值的方法,生成的随机值还要让它在1到9的范围内,可以对生成的随机值进行%9的操作,这样生成的数是在范围内的了。需要注意的是:在存放雷的时候要判断所放位置是否已经是雷,如果不是,在存放雷。代码如下:
srand((unsigned int)time(NULL));//在main函数中调用一次即可
void 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] != '1')
{
board[x][y] = '1';
count--;
}
}
雷放置好后,就是进行扫雷的步骤了。设计函数FindMine(),要实现的功能有,提示玩家输入坐标,进行坐标的判断,如果是雷,显示”你被炸死了“,如果不是,要计算它周围的雷数,在放进对应的位置,并将棋盘信息打印出来,一直重复,直到所有的不是雷的位置被找出来。
这个位置是需要注意的地方,GetMine函数返回值是整型,而数组的类型是字符型,那么怎么将整型数字转换成字符型呢,很简单,只需将字符n-字符0就是对应的n值,在将这个值赋值给数组的时候,要将其转成字符n,只要将这个n加上‘0’,就会转换成对应得字符型,我们可以看下面的ASCII表格
接下来实现得展开功能,如果玩家所选择得位置得周围都没有雷就会将周围得地方全部展开,以此类推,这里我们用递归来实现此功能,代码如下:
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = 0;
count = GetMine(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
if (x - 1 > 0 && y - 1 > 0 && show[x - 1][y - 1] == '*')
OpenMine(mine, show, x - 1, y - 1);
if (x - 1 > 0 && y > 0 && show[x - 1][y] == '*')
OpenMine(mine, show, x - 1, y);
if (x - 1 > 0 && y + 1 > 0 && show[x - 1][y + 1] == '*')
OpenMine(mine, show, x - 1, y + 1);
if (x > 0 && y - 1 > 0 && show[x][y - 1] == '*')
OpenMine(mine, show, x, y - 1);
if (x > 0 && y + 1 > 0 && show[x][y + 1] == '*')
OpenMine(mine, show, x, y + 1);
if (x + 1 > 0 && y - 1 > 0 && show[x + 1][y - 1] == '*')
OpenMine(mine, show, x + 1, y - 1);
if (x + 1 > 0 && y > 0 && show[x + 1][y] == '*')
OpenMine(mine, show, x + 1, y);
if (x + 1 > 0 && y + 1 > 0 && show[x + 1][y + 1] == '*')
OpenMine(mine, show, x + 1, y + 1);
}
else
{
show[x][y] = count + '0';
}
}
接下来,设计了一个函数来计算此时棋盘上没有展开的位置的数,如果这个数量等于雷数,那么玩家获胜。
int Count(char show[ROWS][COLS])
{
int count = 0;
for (int i = 1; i <= ROW; i++)
{
for (int j = 1; j <= COL; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
整个功能基本实现,我们可以看一下效果
game2.h
#pragma once
#include
#include
#include
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 3
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 FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);//排查雷
game2.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game2.h"
void InItBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] =set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf(" ");
for (i = 1; i <= 9; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
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 count = EASY_COUNT;
while (count)
{
//生成随机下标
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;
}
}
}
int GetMine(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 - 1][y + 1] + mine[x][y + 1] - 8 * '0');
}
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = 0;
count = GetMine(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
if (x - 1 > 0 && y - 1 > 0 && show[x - 1][y - 1] == '*')
OpenMine(mine, show, x - 1, y - 1);
if (x - 1 > 0 && y > 0 && show[x - 1][y] == '*')
OpenMine(mine, show, x - 1, y);
if (x - 1 > 0 && y + 1 > 0 && show[x - 1][y + 1] == '*')
OpenMine(mine, show, x - 1, y + 1);
if (x > 0 && y - 1 > 0 && show[x][y - 1] == '*')
OpenMine(mine, show, x, y - 1);
if (x > 0 && y + 1 > 0 && show[x][y + 1] == '*')
OpenMine(mine, show, x, y + 1);
if (x + 1 > 0 && y - 1 > 0 && show[x + 1][y - 1] == '*')
OpenMine(mine, show, x + 1, y - 1);
if (x + 1 > 0 && y > 0 && show[x + 1][y] == '*')
OpenMine(mine, show, x + 1, y);
if (x + 1 > 0 && y + 1 > 0 && show[x + 1][y + 1] == '*')
OpenMine(mine, show, x + 1, y + 1);
}
else
{
show[x][y] = count + '0';
}
}
int Count(char show[ROWS][COLS])
{
int count = 0;
for (int i = 1; i <= ROW; i++)
{
for (int j = 1; j <= COL; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = Count(show);
while (win > EASY_COUNT)
{
printf("请输入你要排查的坐标:\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("你被炸死了!\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
if (show[x][y] == '*')
{
OpenMine(mine, show, x, y);
DisplayBoard(show, ROW, COL);
win=Count(show);
}
}
}
else
{
printf("坐标非法,请重新输入:\n");
}
}
if (win == EASY_COUNT)
{
printf("恭喜你,成功通关!\n");
DisplayBoard(mine, ROW, COL);
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game2.h"
void munu()
{
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(mine, ROW, COL);*/
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
{
munu();
printf("请选择:\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷游戏开始!\n");
game();
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("选择有误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
运行效果,为了方便测试,这里我设置了3个雷
由于作者还是初学,有些其他得功能还没有实现,在未来我会将功能及文章继续改进,感谢大家支持!