相信大家都玩过扫雷游戏,在学习了二维数组之后,我也用c语言写了一个简单的扫雷游戏规则如下:
1.通过对宏Row和宏List来设置游戏的棋盘大小(如果想玩10x10的扫雷则需设置Row和List为12);
2.通过对 宏BoomNumber 来设置局中雷的个数(雷的个数需要<=(Row-2)*(List-2));
3.对 用户输入要扫描的坐标,如果有雷则雷爆炸游戏结束,如果没有雷则统计出该坐标周围的雷的个数,一直循环此操作,直到棋盘中未扫描的地方全是雷为止则扫雷成功。
#pragma warning (disable:4996)
#pragma once
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define Row 12
#define List 12
#define BoomNumber 20
#define Boom '1'//1为有雷
#define nice '0'
extern void Game();
extern void SetMine(char MineBoard, int row, int list);
extern void Select(char MineBoard, int row, int list);
extern void Menu();
extern void CountBoom(char MineBoard[][List], int row, int list, int x, int y);
这便是扫雷游戏的头文件,里面声明的函数和定义的宏会在下文中一一解释
问题的关键在于:
1.如何给棋盘随机埋入BoomNumber个雷
2.用户选择扫雷的坐标后系统发现此处没有雷如何统计出周围雷的个数并且让用户看到
考虑到只有在雷爆炸后用户才需要知道系统埋雷的位置,其余时间不需要让用户看见埋雷的位置,为了方便我们需要设置两个棋盘,一个用来埋雷另外一个则展示给用户,棋盘有横有纵,所以我们考虑用 两个类型相同的二维数组埋雷的棋盘为MineBoard[Row][List]展示给用户的则为ShowBoard[Row][List],考虑到内存的问题我们设置这两个数组都为char类型;
棋盘的问题解决了,那么在用户第一次选择坐标之前我们还需要1.埋雷;2.把ShowBoard数组展示出来,因为需要随机埋下BoomNumber个雷所以我们使用随机数的方法,让系统随机产生坐标而且不能有重复的坐标,在生成的坐标位置埋入Boom,其余位置则为nice(没有雷),因为埋雷是少数操作我们先初始化MineBoard数组里的元素全为nice;
void SetMine(char MineBoard[][List], int row, int list)
{
for (int i = 0;i < row;i++)
{
for (int j = 0;j < list;j++)
{
MineBoard[i][j] =nice;
}
}//无雷为nice
int num = 0;
int _x;
int _y;
while (num < BoomNumber)
{
_x = 1 + rand()%(Row-2);
_y = 1 + rand()%(Row-2);
if (MineBoard[_x][_y] != Boom)
{
MineBoard[_x][_y] = Boom;
num++;
}
}
}
埋雷完成后则给用户展示ShowBoard
void ShowBoards(char ShowBoard[][List], int row, int list)
{
printf(" ");
for (int x = 1;x < list - 1;x++)
{
printf(" %2d ",x);
}
printf("\n");
for (int i = 1;i < row-1;i++)
{
printf(" %2d",i);
for (int j = 1;j < list-1;j++)
{
printf(" %c |",ShowBoard[i][j]);
}
printf("\n");
printf(" ");
for (int k = 0;k < row - 2;k++)
{
printf("----");
}
printf("\n");
}
}
此时用户选择要扫描的坐标若该坐标没有雷则统计其周围雷的个数,要是有雷则中雷游戏结束
考虑到用户输入的坐标可能已经被扫描或者用户输入的坐标不在(1.1)~(Row-2,Row-2)这个范围内我们就要提醒用户输入的坐标以及被扫描或者输入的坐标不合法,直到用户为扫描的坐标全为Boom为止
void Select(char ShowBoard[Row][List], char MineBoard[][List], int row, int list)
{
int step = 0;
int x = 0;
int y = 0;
while (step < ((row - 2) * (list - 2) - BoomNumber))
{
printf("请输入你要排雷的坐标:(x,y)\n");
scanf("%d,%d", &x, &y);
if (x > 0 && x < (List-1) && y>0 && y < (List-1))
{
if (ShowBoard[x][y] == 42)
{
if (MineBoard[x][y] == nice)
{
CountBoom(ShowBoard, MineBoard, Row, List,x,y);
system("cls");
ShowBoards(ShowBoard, Row, List);
step++;
if (step == ((row - 2) * (list - 2) - BoomNumber))
{
printf("扫雷成功!\n");
Menu();
}
}
else if(MineBoard[x][y] == Boom)
{
system("cls");
printf("你已经被炸死了!游戏结束\n");
ShowMineBoard(MineBoard, Row, List);
}
}
else {
printf("此处已经被扫描,请输入合法的坐标\n");
}
}
else {
printf("请输入合法的坐标\n");
}
}
}
统计用户选择的坐标的周围雷的个数并让这个数字展示在ShowBoard上,我用到了sprintf函数来把数字转换成字符(因为我们定义ShowBoard数组为char类型)统计雷的个数时因为MineBoard数组也是char类型在把(x,y)周围8个位置的雷的个数加起来时用到的是Boom的ascii码值,又因为有雷为‘1’(ascii码值为49)没有雷为‘0’(ascii码值为48)所以在运算后需要减去8个‘0’的ASCII码值,再返回去看定义这两个棋盘时我们只需要10x10的棋盘却定义了12x12的棋盘这是为了方便统计边缘棋盘周围雷的个数
void CountBoom(char ShowBoard[Row][List],char MineBoard[][List], int row, int list, int x, int y)
{
int a = MineBoard[x][y + 1]\
+ MineBoard[x + 1][y + 1] + MineBoard[x - 1][y + 1] \
+ MineBoard[x][y - 1] + MineBoard[x + 1][y - 1] \
+ MineBoard[x - 1][y - 1] + MineBoard[x - 1][y] \
+ MineBoard[x + 1][y] - 384;
char arr[32];//设置一个较大的数组防止溢出
sprintf(arr, "%d", a);//把数字转成字符
ShowBoard[x][y] = arr[0];
}
至此扫雷游戏的主要逻辑已经梳理完毕
#include"FindMine.h"
void Menu()
{
printf("*****1.开始游戏*****\n");
printf("*****2.退出游戏*****\n");
printf("请选择:\n");
int a = 0;
scanf("%d", &a);
system("cls");
if (a == 1)
{
Game();
}
else if (a == 2)
{
exit(0);
}
else {
printf("输入有误,请重新选择\n");
Menu();
}
}
void Game()
{
char ShowBoard[Row][List];
for (int i = 0;i < Row;i++)
{
for (int j = 0;j < List;j++)
{
ShowBoard[i][j] = '*';
}
}
char MineBoard[Row][List];
SetMine(MineBoard, Row, List);
ShowBoards(ShowBoard, Row, List);
Select(ShowBoard, MineBoard, Row, List);
}
int main()
{
srand((unsigned int)time(NULL));
Menu();
return 0;
}
这些为用户可见的函数
#include"FindMine.h"
void ShowBoards(char ShowBoard[][List], int row, int list)
{
printf(" ");
for (int x = 1;x < list - 1;x++)
{
printf(" %2d ",x);
}
printf("\n");
for (int i = 1;i < row-1;i++)
{
printf(" %2d",i);
for (int j = 1;j < list-1;j++)
{
printf(" %c |",ShowBoard[i][j]);
}
printf("\n");
printf(" ");
for (int k = 0;k < row - 2;k++)
{
printf("----");
}
printf("\n");
}
}
void ShowMineBoard(char MineBoard[][List], int row, int list)
{
for (int i = 1;i < row-1;i++)
{
for (int j = 1;j < list-1;j++)
{
printf("%c", MineBoard[i][j]);
}
printf("\n");
}
Menu();
}
void CountBoom(char ShowBoard[Row][List],char MineBoard[][List], int row, int list, int x, int y)
{
int a = MineBoard[x][y + 1]\
+ MineBoard[x + 1][y + 1] + MineBoard[x - 1][y + 1] \
+ MineBoard[x][y - 1] + MineBoard[x + 1][y - 1] \
+ MineBoard[x - 1][y - 1] + MineBoard[x - 1][y] \
+ MineBoard[x + 1][y] - 384;
char arr[32];//设置一个较大的数组防止溢出
sprintf(arr, "%d", a);//把数字转成字符
ShowBoard[x][y] = arr[0];
}
void Select(char ShowBoard[Row][List], char MineBoard[][List], int row, int list)
{
int step = 0;
int x = 0;
int y = 0;
while (step < ((row - 2) * (list - 2) - BoomNumber))
{
printf("请输入你要排雷的坐标:(x,y)\n");
scanf("%d,%d", &x, &y);
if (x > 0 && x < (List-1) && y>0 && y < (List-1))
{
if (ShowBoard[x][y] == 42)
{
if (MineBoard[x][y] == nice)
{
CountBoom(ShowBoard, MineBoard, Row, List,x,y);
system("cls");
ShowBoards(ShowBoard, Row, List);
step++;
if (step == ((row - 2) * (list - 2) - BoomNumber))
{
printf("扫雷成功!\n");
Menu();
}
}
else if(MineBoard[x][y] == Boom)
{
system("cls");
printf("你已经被炸死了!游戏结束\n");
ShowMineBoard(MineBoard, Row, List);
}
}
else {
printf("此处已经被扫描,请输入合法的坐标\n");
}
}
else {
printf("请输入合法的坐标\n");
}
}
}
void SetMine(char MineBoard[][List], int row, int list)
{
for (int i = 0;i < row;i++)
{
for (int j = 0;j < list;j++)
{
MineBoard[i][j] =nice;
}
}//无雷为nice
int num = 0;
int _x;
int _y;
while (num < BoomNumber)
{
_x = 1 + rand()%(Row-2);
_y = 1 + rand()%(Row-2);
if (MineBoard[_x][_y] != Boom)
{
MineBoard[_x][_y] = Boom;
num++;
}
}
}
以上则为Game函数中需要调用的函数