扫雷游戏规则::
- 扫雷游戏的实现需要两个数组,假设我们要实现一个10 * 10的扫雷游戏,我们得定义两个 12 * 12的数组,一个是Map[12][12],一个是mine[12][12]
- 其中Map[12][12]的作用就是给玩家看,mine[12][12]的作用是用来布雷;当我们开始扫雷时,显示周围埋雷数的棋盘是Map,而 Mine 除了布雷其他函数都不可以改变它
- 为什么10 * 10的扫雷需要 12 * 12 的数组实现呢?这是为了 10 * 10 边界的元素好统计周围的埋雷数(即我们把10 * 10外面的一圈都设为0,没有雷 ),同时也是为了玩家输入方便
- 对于mine,当有雷存在我们设为1,没有则为0;我们将雷数设置为10,放置在随机的位置上。
- 对于Map,我们初始化为 * ,当玩家输入坐标后,则显示坐标周围埋雷的个数
- 规定玩家第一次输入坐标时,若这个坐标就是埋雷的坐标,则我们将此坐标重置为0,并重新找一个位置放雷。
- 我们的输赢判断条件为:当玩家踩到雷即为输家(除了第一次);当玩家一直没有踩到雷并且最后剩余未解封的坐标数恰好等于我们设置的埋雷数,则玩家胜利;当游戏结束时 我们才会将 mine 输出。
看代码:
- game.h
#ifndef __GAME_H__
#define __GAME_H__
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#define ROW 10
#define COL 10
#define ROWS ROW+2 //我们的扫雷是10 * 10的大小,为了边界的元素好统计周围的埋雷数,我们给10*10周围再加上一圈,就相当于12*12的数组,这就是为什么要+2
#define COLS COL+2
#define MINE_COUNT 10 //我们所设置的雷数
//初始化界面
void InitMap(char Map[ROWS][COLS], int row, int col, char set);
//打印界面
void Display(char Map[ROWS][COLS], int row, int col);
//布雷
void SetMine(char mine[ROWS][COLS], int row, int col, int num);
//统计坐标(x,y)周围雷的个数
char CountMine(char mine[ROWS][COLS], int x, int y);
//展开周围不是雷的地方
void OpenMine(char mine[ROWS][COLS], char Map[ROWS][COLS], int x, int y);
//统计剩余未解封的坐标个数
int CountMap(char Map[ROWS][COLS], int row, int col);
//保证第一次扫雷即使遇到雷也不会被炸死
void SafeMine(char mine[ROWS][COLS], char Map[ROWS][COLS], int row, int col,int x, int y);
#endif //__GAME_H__
- test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//菜单函数
void menu()
{
printf("*********************************\n");
printf("******* 1.play 0.exit *******\n");
printf("*********************************\n");
}
//游戏函数
void game()
{
//x y 存储玩家输入的坐标
int x = 0;
int y = 0;
int tmp = 0;//判断是不是第一次踩雷
//定义两个二维数组:Map是给玩家看的棋盘,mine是用来布雷的棋盘
char Map[ROWS][COLS] = { '0' };
char mine[ROWS][COLS] = { '0' };
//初始化界面和雷阵
InitMap(Map, ROWS, COLS, '*');
InitMap(mine, ROWS, COLS, '0');
//布雷
SetMine(mine, ROWS, COLS, MINE_COUNT);
//Display(mine, ROWS, COLS);
Display(Map, ROWS, COLS);
//排雷
while (1)
{
printf("请玩家输入坐标:");
scanf("%d%d", &x, &y);
//先判断坐标是否正确
if ((x >= 1) && (x <= ROW) && (y >= 1) && (y <= COL)) {
//判断当前位置是否有雷
if ('1' == mine[x][y])
{
//判断是不是第一次排雷,因为我们的要求说第一次即使踩到雷也不能被炸死,
if (0 == tmp)
{
SafeMine(mine, Map,ROWS,COLS, x, y);//防止第一次被炸死
OpenMine(mine, Map, x, y);
Display(Map, ROWS, COLS);
//判断是不是第一次就赢
if (MINE_COUNT == CountMap(Map, ROWS, COLS))
{
printf("恭喜你,排雷成功\n");
printf("排雷的棋盘如下:\n");
Display(mine, ROWS, COLS);//成功就展示下雷的数组
return;
}
}
else
{
//不是第一次并且踩到了雷,则输了
printf("很遗憾,你被炸死了\n");
printf("排雷的棋盘如下:\n");
Display(mine, ROWS, COLS);
return;
}
}
else //当前位置没有雷
{
//在当前位置处输出周围雷的个数
Map[x][y] = CountMine(mine, x, y);
//拓展开周围没有雷的地方
OpenMine(mine, Map, x, y);
//展示
Display(Map, ROWS, COLS);
}
}
else {
printf("输入坐标不合法\n");
}
//当剩余坐标数 == 设置的雷数的时候,即剩余的坐标都存在着雷的时候胜利
if (MINE_COUNT == CountMap(Map, ROWS, COLS))
{
printf("恭喜你,排雷成功!\n");
printf("排雷的棋盘如下:\n");
Display(mine, ROWS, COLS);//成功就展示下雷的数组
break;
}
tmp++;//别忘了这一步
}
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("亲,请选择:");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出游戏\n");
break;
case 1:
game();
break;
default:
printf("输出有误,请重新选择\n");
}
} while (input);
}
int main()
{
test();
system("pause");
return 0;
}
- game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化界面
void InitMap(char Map[ROWS][COLS], int row, int col, char set)
{
memset(Map, set, row*col*sizeof(Map[0][0]));
}
//打印界面
void Display(char Map[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf(" 1 2 3 4 5 6 7 8 9 10\n");
printf(" ----------------------\n");
for (i = 1; i < row - 1; i++)
{
printf("%2d|", i);
for (j = 1; j < col - 1; j++)
{
printf(" %c", Map[i][j]);
}
printf("|\n");
}
printf(" ----------------------\n");
}
//布雷
void SetMine(char mine[ROWS][COLS], int row, int col, int Mine_Count)
{
int x = 0;
int y = 0;
while (Mine_Count)
{
x = rand() % (row - 2) + 1; //rand()%10产生的是0到9的随机数,+1产生的是1到10的随机数
y = rand() % (col - 2) + 1; //产生随机坐标
//如果随机坐标处没有雷,则设置雷
if ('0' == mine[x][y])
{
mine[x][y] = '1';//布雷
Mine_Count--;//布雷成功则Mine_Count减一
}
}
}
//统计坐标(x,y)周围雷的个数
char CountMine(char mine[ROWS][COLS], int x, int y)
{
/*
题外话:
字符型数值-‘0’就是相应的int型,比如看下面的代码:
char a = '1';
int b = a ;
printf("%d\n", b);//49
int b = a - '0';
printf("%d\n", b);//1
*/
int a = 0;
a = (mine[x - 1][y - 1] - '0') + (mine[x - 1][y] - '0') + (mine[x - 1][y + 1] - '0') + (mine[x][y - 1] - '0') + (mine[x][y + 1] - '0') + (mine[x + 1][y - 1] - '0') + (mine[x + 1][y] - '0') + (mine[x + 1][y + 1] - '0');
return a + '0';//+'0'是为了再变回char类型
}
//展开坐标(x,y)的周围(前提是此处不是雷)
void OpenMine(char mine[ROWS][COLS], char Map[ROWS][COLS], int x, int y)
{
//如果对应的位置不是雷的话,就展开他
if ('0' == mine[x - 1][y - 1])
{
//记住判断得是mine数组,但改变的是Map数组的内容,因为我们对外展示的是Map数组
Map[x - 1][y - 1] = CountMine(mine, x - 1, y - 1) ;
}
if ('0' == mine[x - 1][y])
{
Map[x - 1][y] = CountMine(mine, x - 1, y);
}
if ('0' == mine[x - 1][y + 1])
{
Map[x - 1][y + 1] = CountMine(mine, x - 1, y + 1);
}
if ('0' == mine[x][y - 1])
{
Map[x][y - 1] = CountMine(mine, x, y - 1);
}
if ('0' == mine[x][y + 1])
{
Map[x][y + 1] = CountMine(mine, x, y + 1);
}
if ('0' == mine[x + 1][y - 1])
{
Map[x + 1][y - 1] = CountMine(mine, x + 1, y - 1);
}
if ('0' == mine[x + 1][y])
{
Map[x + 1][y] = CountMine(mine, x + 1, y);
}
if ('0' == mine[x + 1][y+1])
{
Map[x + 1][y+1] = CountMine(mine, x + 1, y+1) ;
}
}
//判断剩余未知区域的个数
int CountMap(char Map[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int num = 0;
for (i = 1; i < row - 1; i++)
{
for (j = 1; j < col - 1; j++)
{
if ('*' == Map[i][j]) {
num++;
}
}
}
return num;
}
//保证第一次扫雷,不会被炸死
void SafeMine(char mine[ROWS][COLS], char Map[ROWS][COLS], int row,int col,int x, int y)
{
mine[x][y] = '0';//先将此处的状态至为无雷
Map[x][y] = CountMine(mine, x, y);//对应的Map数组此处显示周围雷的个数
int co = 1;
//重新布雷
while (co)
{
//重新获得一个随机坐标,并且这个坐标不能等于上面的坐标,且这个新坐标未布雷
int i = rand() % (row - 2) + 1;
int j = rand() % (col - 2) + 1;
if ((x != i) && (y != j) && ('0' == mine[i][j]))
{
mine[i][j] = '1';
co--;//重新布局后就不用再重复
}
}
}