目录
- 玩法说明
- 分析和设计
- 代码实现
- 扩展
- 扩展修改
1. 玩法说明
控制台界面,在地图上提前布置好雷,地图内容用户看不到。开始选择点击位置,如果不是雷,显示周围雷的数量,如果是雷,则被炸死,游戏结束。将所有雷都找出来,或者地图上剩余位置全是雷,胜利,游戏结束
2. 分析和设计
2.1 数据结构分析
布置雷的地图数据和排查出显示的数据都需要存储,首先想到用一个9*9的二维数组存放。雷的位置为1,非雷用0表示
如果排查位置是(2,5)这个坐标,访问周围8个位置,统计周围雷的个数显示在当前位置
图里(2,5)坐标本身存的值为0,遍历周围位置共有1个雷,所以该位置显示给用户1表示周围有1个雷
问题:
- 有两个需要显示的信息,一个是地图本身是雷和非雷的信息,用0和1表示。另一个是需要给用户看到的信息,也就是是雷或周围雷的总数的信息。这里如果都放在1个数组,会有不明确的问题,且比较混乱,如1无法分辨是周围雷的总数还是是地雷表示的1
- 在统计最边上数字的雷的数量的时候,容易越界。如果每一次都判断是否越界,比较麻烦
解决方案:
3. 我们可以用两个数组,一个存放是地雷和非雷的信息,另一个用来给用户显示周围地雷的信息。统一使用char类型二维数组,方便打印字符和传参的统一。第二个地图刚开始全部为“”字符,是初始化的状态
4. 需要存放数据的是99的数组,将其扩展为1111的数组,使用里面99的元素,这样最边上的统计也不会越界
5. 尽量使用同一套函数处理,参数统一
对应的数组:
char minemap[11][11] = {0};//⽤来存放布置好的雷的信息
char flipmap[11][11] = {0};//⽤来存放排查出的雷的个数信息
2.2 文件结构的设计
分三个文件编写,一个是游戏内容模块和主程序
test.c //测试逻辑
game.h //游戏数据类型和函数定义
game.c //游戏函数的实现
2.3 函数说明
- 显示菜单
void menu();
- 变量定义
#define ROWS 11 //地图行数
#define COLS 11 //地图列数
#define ROW ROWS-2 //使用的行数
#define COL COLS-2 //使用的列数
#define MINE 10 //地雷个数
3.游戏函数
//1.初始化地图
void InitMap(char ary[ROWS][COLS],int rows,int cols);
//2.设置地雷
void SetMine(char mine[ROWS][COLS],int row,int col);
//3.显示地图
void ShowMap(char ary[ROWS][COLS], int row, int col);
//4.找雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);
2.4 界面展示
- 开始界面
- 选择1,进入游戏,分别打印雷的存放地图 (此地图无需展示,用于测试),用户显示的地图。下面输入想翻开的坐标
坐标输入错误,报非法重新输入
输入翻开2,3坐标,显示周围地雷总数2
-
点到地雷,提示游戏结束,回到主界面
-
排查完所有地雷,提示胜利,回到主界面
3. 代码实现
3.1 难点分析
- 游戏内需要多处改动的统一定义为宏定义,如地图行数和地雷个数
- 统一接口传入二维数组,行和列根据需要的不同更改
- 地图为了方便用户输入,同时打印行和列号的标识
- 计算周围地雷个数,目标点周围几个坐标的变化,因为最后加的总数是char字符的ascii总和的int类型,所以减去8个字符’0’转化为地雷的数量
- 每次点开一个,记录一下次数,如果等于格子数减去地雷数,则雷全部找出,胜利
3.2 代码
1.game.h
#pragma once
#define ROWS 11 //地图行数
#define COLS 11 //地图列数
#define ROW ROWS-2 //使用的行数
#define COL COLS-2 //使用的列数
#define MINE 10 //地雷个数
//1.初始化地图
void InitMap(char ary[ROWS][COLS],int rows,int cols);
//2.设置地雷
void SetMine(char mine[ROWS][COLS],int row,int col);
//3.显示地图
void ShowMap(char ary[ROWS][COLS], int row, int col);
//4.找雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);
2.game.c
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void InitMap(char ary[ROWS][COLS], int rows, int cols,char set)
{
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
ary[i][j] = set;
}
}
}
void SetMine(char mine[ROWS][COLS],int row, int col)
{
int count = MINE;
while (count > 0) {
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0') {
mine[x][y] = '1';
count--;
}
}
}
void ShowMap(char ary[ROWS][COLS], int row, int col)
{
printf("--------扫雷--------\r\n");
//先打印列号
for (int i = 0; i <= row; i++) {
printf("%d ", i);
}
printf("\r\n");
for (int i = 1; i <= row; i++) {
//打印行号
printf("%d ", i);
for (int j = 1; j <= col; j++) {
printf("%c ", ary[i][j]);
}
printf("\r\n");
}
}
//计算周围地雷数
static int MnieCnt(char mine[ROWS][COLS],int x, int y) {
return mine[x - 1][y - 1] + mine[x + 1][y + 1]+
mine[x][y - 1] + mine[x][y + 1] +
mine[x + 1][y - 1] + mine[x - 1][y + 1] +
mine[x - 1][y] + mine[x + 1][y]
- 8 * '0';
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{
int win = 0; //记录排查的雷个数
while (win < row * col -MINE) {
printf("输入要点的坐标:");
int x, y;
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
if (mine[x][y] == '1') {
system("cls");
printf("点到地雷,游戏结束\r\n");
ShowMap(mine,ROW,COL);
return;
}
}
else {
system("cls");
printf("坐标非法,重新输入\r\n");
ShowMap(show, ROW, COL);
continue;
}
win++;
int n = MnieCnt(mine, x, y);
show[x][y] = n + '0';
system("cls");
ShowMap(show, ROW, COL);
}
printf("排雷成功\r\n");
}
3.main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include "game.h"
void menu() {
printf("********************\r\n");
printf("*****1.开始游戏*****\r\n");
printf("*****0.退出游戏*****\r\n");
printf("********************\r\n");
}
void game() {
char minemap[ROWS][COLS]; //地雷地图
char flipmap[ROWS][COLS]; //显示的地图
//1.初始化地图
InitMap(minemap,ROWS,COLS,'0');
InitMap(flipmap, ROWS, COLS, '*');
//2.设置地雷
SetMine(minemap,ROW,COL);
//3.显示地图
ShowMap(minemap, ROW, COL);
ShowMap(flipmap, ROW, COL);
//4.找雷
FindMine(minemap, flipmap,ROW,COL);
}
int main()
{
srand((unsigned int)time(NULL));
int sel; //获取用户选择
do
{
menu();
scanf("%d", &sel);
switch (sel) {
case 1:
system("cls");
game();
break;
case 0:
printf("退出游戏\r\n");
return;
break;
default:
printf("输入错误\r\n");
break;
}
} while (1);
return 0;
}
4.扩展
- 选择游戏难度
*简单9*9棋盘,10个雷
*中等16*16棋盘,40个雷
*困难30*30棋盘,99个雷 - 如果排查位置不是雷,周围也不是雷,可以展开一片
- 可以标记雷
- 加上排雷的时间显示
5. 扩展修改
- 加入计时显示
- 标记地雷
重写FindMine函数,加入clock_t开始和结束计时变量,新增输入1为标记地雷,地雷显示为@符号
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{
int win = 0; //记录排查的雷个数
clock_t start,end;
int sec = 0;
start = clock();
while (win < row * col -MINE) {
end = clock();
sec = end - start;
printf("已用时间:%ds ,0输入坐标 1标记地雷:",sec/ CLOCKS_PER_SEC);
int n;
scanf("%d", &n);
if (n == 0) {
sec = end - start;
printf("已用时间:%ds,输入要点的坐标:", sec / CLOCKS_PER_SEC);
int x, y;
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
if (mine[x][y] == '1') {
system("cls");
printf("点到地雷,游戏结束\r\n");
ShowMap(mine, ROW, COL);
return;
}
}
else {
system("cls");
printf("坐标非法,重新输入\r\n");
ShowMap(show, ROW, COL);
continue;
}
win++;
int n = MnieCnt(mine, x, y);
show[x][y] = n + '0';
system("cls");
ShowMap(show, ROW, COL);
}
else if (n == 1) {
sec = end - start;
printf("已用时间:%ds ,输入标记的坐标(显示@,重新输入取消标记):", sec / CLOCKS_PER_SEC);
int x, y;
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL) {
if(show[x][y]='@')
show[x][y] = '*';
show[x][y] = '@';
system("cls");
ShowMap(show, ROW, COL);
}
}
}
printf("排雷成功\r\n");
}