一、游戏的分析与设计
目标:实现扫雷游戏的基本功能
1.可以通过菜单控制继续玩还是退出游戏
2.随即设置棋盘的类型,本文为9*9
3.随随机布置任意的雷,本文为10个
4.通过输入坐标进行排雷
- 如果不是雷显示周围的雷数
- 是雷则被炸死,结束游戏
- 直到所有的雷被找出即为胜利
初始界面: 排雷界面: 排雷失败界面:
扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些信息。
因为我们需要在9*9的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个9*9的数 组来存放信息。
我们可以让‘1’表示为雷,‘0’表示不是雷。
未布置雷的棋盘 布置雷的棋盘
但是如果我们要访问边沿元素有几个雷的时候就很难实现,所以我们可以将棋盘扩大一圈,变成11*11这样访问边缘元素周围雷的个数也很方便了。
如果访问的位置并不是雷,那么我们需要将他周围雷的个数计算并打印出来,那么这个个数要存在哪里呢?
如果存放在布置雷的棋盘中就会导致概念混淆,这样的话我们设置一个数组专门存放雷的信息,而另一个数组专门用来打印给人以参考。
对应的数组应该是:
char mine[11][11] = {0};//⽤来存放布置好的雷的信息
char show[11][11] = {0};//⽤来存放排查出的雷的个数信息
二、代码实现:
2.1 test.c
由于要实现可以多次玩,由菜单控制退出与否,我们使用了一个do~while循环
进入之后要给用户选择的空间,因此调用一个menu函数来显示菜单,接下来通过用户所输入的数字判断下一步要进行的操作,因此用switch语句作为多分支选择。
2.2 mine.c
本文件在用户输入1后进入即可进入。
首先我们需要对main.c中定义的两个数组进行初始化,将对用户所展示的棋盘全部初始化为‘*’,将进行埋雷的棋盘(下文简称为"雷盘”)初始化为‘0’。
接下来我们将雷盘进行埋雷操作,由于我们要保证雷的位置随机,因此我们需要调用rand()赋随机值(具体已在主页随机数游戏中讲过原理)。
再接下来就可以让用户进行找雷了。当找到的不是雷的时候就会显示附近雷的个数。
最后如果用户成功排雷,那么就获得胜利。
2.3 mine.h
此文件用来存放函数的声明。
2.4 具体实现
//mine.h
#pragma once
#include <stdio.h>
#include<time.h>
#include<stdlib.h>
#include<windows.h>
#define ROW 9 //可见的行数
#define COL 9//可见列数
#define ROWS ROW+2 //设置的底板,不可见,行数
#define COLS COL+2 //设置的底板,不可见,列数
#define MINE 10 //雷的个数
void init(char board[ROWS][COLS], int row, int col, char ch);//初始化棋盘
void display(char showboard[ROWS][COLS], int row, int col);//打印棋盘
void setmine(char mine[ROWS][COLS], int row, int col);//埋雷
void findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//找雷
int GetMineCount(char mine[ROWS][COLS], int x, int y);//统计个数
void ExplodeBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pwin);//爆炸展开
void Signmine(char board[ROWS][COLS], int row, int col);//标记
//mine.c
#define _CRT_SECURE_NO_WARNINGS
#include"mine.h"
//初始化棋盘
void init(char board[ROWS][COLS], int row, int col, char ch)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
board[i][j] = ch;
}
}
}
//展示棋盘
void display(char showboard[ROWS][COLS], int row, int col)
{
printf(" ----开始游戏----\n");
for (int i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
printf("%c ", showboard[i][j]);
}
printf("\n");
}
}
//埋雷
void setmine(char mine[ROWS][COLS], int row, int col)
{
int count = MINE;//初始化雷的个数
while (count)
{
int x = 0;
int y = 0;
x = rand() % row + 1;//1-9
y = rand() % col + 1;//1-9
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
//找雷
void findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
char ch;
while (win < row * col - MINE)
{
printf("请输入坐标来排查;>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
display(mine, ROW, COL);
break;
}
else
{
//爆炸展开
win++;
ExplodeBoard(mine, show, row, col, x, y, &win);
//打印棋盘
display(show, ROW, COL);
printf("需要标注地雷输入:Y,不需要则输入:N\n");
//清空缓冲区
while ((getchar()) != '\n');
scanf("%c", &ch);
if (ch == 'Y')
{
//标记雷的位置
Signmine(show, ROW, COL);
}
}
}
else
printf("输入的数据非法,请重新输入!\n");
}
if (win == row * col - MINE)
{
printf("恭喜你,排雷成功\n");
display(mine, ROW, COL);
}
}
//统计个数
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y - 1] +mine[x - 1][y] +mine[x - 1][y + 1]
+mine[x][y - 1] +mine[x][y + 1] +mine[x + 1][y + 1]
+mine[x + 1][y] +mine[x + 1][y + 1]
- 8 * '0';
}
//爆炸展开
void ExplodeBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pwin)
{
//限制条件
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//计算该位置周围雷的个数
int count = GetMineCount(mine, x, y);
if (count == 0)
{
//把该位置变成空格
show[x][y] = ' ';
int i = 0;
//向周围进行递归遍历
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
//限制对重复递归调用的条件,避免死递归
if (show[i][j] == '*')
{
ExplodeBoard(mine, show, row, col, i, j, pwin);
*pwin++;
}
}
}
}
else
{
show[x][y] = count + '0';
}
}
}
//标记雷的函数
void Signmine(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
printf("请输入要标记的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (board[x][y] == '*')
{
board[x][y] = '!';
break;
}
else
{
printf("该位置不能被标记,请重新输入:\n");
}
}
else
{
printf("坐标非法,请重新输入:\n");
}
}
}
//test.c
#define _CRT_SECURE_NO_WARNINGS
#include"mine.h"
enum { Exit, Play };
//菜单
void menu()
{
printf("*******************\n");
printf("*** 1. Play ***\n");
printf("*** 0. Exit ***\n");
printf("*******************\n");
}
//游戏内容
void game(void)
{
//声明数组
char show[ROWS][COLS];//可见
char mine[ROWS][COLS];//埋雷
//初始化两个数组
init(show, ROWS, COLS, '*');
init(mine, ROWS, COLS, '0');
//展示棋盘
display(show, ROW, COL);
//埋雷
setmine(mine, ROW, COL);
//找雷
findmine(mine, show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));//初始化种子
int input = 0;
do
{
menu();//菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case Play:
game();//进入游戏
break;
case Exit:
printf("----游戏结束----\n");
break;
default:
printf("----请重新输入----\n");
break;
}
} while (input);
return 0;
}