目录
一、前言
扫雷游戏是一款有着悠久历史的十分经典的小游戏,在1992年4月6日,扫雷和纸牌、空当接龙等小游戏搭载在Windows 3.1系统中与用户见面,主要目的是让用户训练使用鼠标,这个游戏的玩法很简单,有初级、中级、高级和自定义等模式,雷区中随机布置一定数量的地雷,玩家需要尽快找出所有不是地雷的方块,但不许踩到地雷,随着电脑在全世界的不断普及,这款游戏也在全球风靡了起来。通过对C语言的进一步学习,我们可以通过数组和函数的知识来实现一个简单的扫雷游戏。
二、游戏的分析与设计
扫雷的过程中,布置出的雷和排查出的雷的信息都需要存储,因此需要两个空间分别存储,对应的是玩家看到的扫雷游戏界面和后台统计的排雷信息,因为我们要在9*9的棋盘上布置雷和排查雷,我们首先想到的便是创建一个9*9的数组来存放信息。
如果某个位置布置雷,则放置“1”,没有雷则是“0”
在游戏中,我们会对棋盘中的坐标进行排查,排查的坐标如果是雷,则被“炸死”,游戏结束,如果不是雷,则显示周围八格里的雷的数量,例如,我们排查(3,5)这个坐标,它不是雷,则显示周围雷的个数,为1个。然而,如果雷刚好设置在了棋盘边缘,在进行排雷的过程中我们便会发现其统计周围雷的个数时会造成越界,因此考虑为棋盘再增添一圈,由原来的9*9变成11*11。这样变不会导致排雷统计的时候造成越界了。
解决了这个问题后,我们继续分析,我们在游玩过程中,排查坐标周围雷的个数的信息需要记录、存储并打印起来,作为排雷的参考信息。那么这个信息存放在哪里呢?如果存放在布置雷的数组中,这样雷的信息和雷的个数信息就可能或产生混淆和打印上的困难。
我们首先可能会想到将不使用数字表示雷和非雷的信息,而是使用字符,这样就避免冲突了,不过这样做棋盘上有雷和非雷的信息,还有排查出的雷的个数信息,就比较混杂,不够方便。
于是经过思考后我们采用另外一个方案:我们专门给一个棋盘(对应一个数组mine)存放布置好的雷的信息,再给另外一个棋盘(对应另外一个数组show)存放排查出的雷的信息。这样就互不干扰了,把雷布置到mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期排查参考。
同时为了保持神秘,show数组开始时初始化为字符 '*',为了保持两个数组的类型一致,可以使用同一套函数处理,mine数组最开始也初始化为字符'0',布置雷改成'1'。
三、代码参考
game.h:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10
//以上这样定义而不是直接给出具体数字是为了方便日后对游戏进行修改
void InitBorad(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);
game.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBorad(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
for (int a = 0; a <= row; a++)
{
printf("%d ", a);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
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] == '0')//防止重复设置的可能性
{
board[x][y] = '1';
count--;
}
}
}
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');
//计算坐标周围八格的雷的数量
//字符和数字之间的变化只需要加减'0'即可
//原因:'0' ASCII码--48;'1' ASCII码值--49...类推
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0, y = 0, win = 0;
while (win < row * col - 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");
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入:\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜您,成功通过!");
DisplayBoard(mine, ROW, COL);//展示雷区
}
}
test.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "game.h"
void menu()
{
printf("-------1.play-------\n");
printf("-------2.exit-------\n");
}
void game()
{
char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息
char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息
//初始化棋盘
InitBorad(mine, ROWS, COLS,'0');
InitBorad(show, ROWS, COLS,'*');
//DisplayBoard(mine,ROW,COL);可以检查一下棋盘的信息是否无误
SetMine(mine, ROW, COL);//布置雷
//DisplayBoard(mine, ROW, COL);检查一下雷的布置是否正确合理
DisplayBoard(show, ROW, COL);
FindMine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
int confirm = 0;
do1:do
{
menu();
printf("请选择: ");
scanf("%d", &input);
switch (input)
{
case 1:
printf("--------扫雷游戏--------\n");
game();
break;
do2: case 2:
printf("是否退出游戏?\n1.是 0.否\n");
scanf("%d", &confirm);
if (confirm == 0)
goto do1;
else if (confirm != 1)
{
printf("输入错误,重新输入!\n");
goto do2;
}
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input != 2);
return 0;
}