1.扫雷游戏的简单规则
扫雷游戏的规则就是,在一块的网格中,点开所有没有雷的网格。 选择要扫的坐标,若该网格为雷则失败;若该网格周边八个网格中有雷,则显示雷。在C语言中,通过自己输入坐标来实现扫雷。
2.基本理念
其本质是二维数组,需要两块面板,一块面板show数组面对用户,显示游戏界面,另一块面板mine数组用来存储雷的位置。
为什么需要两块面板呢?
因为我们使用字符‘1’来表示雷,而如果面对用户的面板中显示周围有一个雷,也显示1,那么我们怎么判断这是表示周围有一个雷还是这里就是一个雷呢?因此用两个面板可以很好地解决此问题。
show界面让用户输入坐标来扫雷,mine界面判断用户是否踩到雷或者判断周围有几颗雷。
3.界面的设计
一般来说扫雷的简单模式是9*9的界面,我们需要判断周围8个格子有几颗雷,这样就会带来一个问题,四周的格子无法判断,因为他们周围没有8个格子,所以我们在设计界面时就用11*11的界面,这样就保证了所有格子都可以判断周围的8个格子,而在显示的时候,仅显示9*9的界面即可。
4.具体功能的实现
1.初始化界面
我们需要初始化show和mine界面,我们定义show的初始化界面为字符*,mine界面的初始化为字符0,在此之前我们需要定义一个宏ROW和COL的值为9,这样方便我们以后代码的修改。此外我们还需要定义ROWS和COLS的值为ROW+2以及COL+2,以定义11*11的面板,初始化的代码如下
//初始化棋盘
void InitilizeBoard(char arr[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = set;
}
}
}
//定义两个棋盘
InitilizeBoard(mine, ROWS, COLS, '0');
InitilizeBoard(show, ROWS, COLS, '*');
在这里我们定义一个函数来初始化,形参中需要有一个char set变量,来自定义初始化界面的元素,否则就要定义两个函数来初始化界面,这样就会让代码冗余。
2.打印show界面
由于mine数组是用来存放地雷信息,不应该给用户显示,所有只显示show数组来让用户扫雷,代码如下
//显示棋盘show
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
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 ", arr[i][j]);
}
printf("\n");
}
}
//显示棋盘show
DisplayBoard(show, ROW, COL);
//DisplayBoard(mine, ROW, COL);
在这里使用for循环来打印,切注意for循环中i和j的值,确保只打印9*9的区域,并且为了方便用户观察坐标,可以打印行号和列号。
3.随机放置地雷
定义一个函数SetMine来实现随机放置地雷,代码如下
//随机放置地雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
//随机放置地雷
SetMine(mine, ROW, COL);
在此之前我们需要定义一个EASY_COUNT的宏来确定雷的个数,方便以后修改,这里EASY_COUNT的值为10,此代码中,使用了if语句,判断如果mine界面为字符0则可以放下地雷,如果不是字符0则代表已经布置过地雷了,在while循环中,每放置一个地雷count就减一,直到count为0跳出循环。
随机函数需要srand,则需要包含stdlib.h头文件,并且以时间作为种子,需要包含time.h头文件
4.判断周围雷的个数
//判断周围有多少雷
int GetMineCount(char arr[ROWS][COLS], int x, int y)
{
return arr[x - 1][y - 1] +
arr[x - 1][y] +
arr[x - 1][y + 1] +
arr[x][y - 1] +
arr[x][y + 1] +
arr[x + 1][y - 1] +
arr[x + 1][y] +
arr[x + 1][y + 1] - 8 * '0';
假设我们输入的坐标是x和y,那么周围8个格子的坐标也随之可以推得,此代码通过计算周围八个格子的ASCII码值再减去8个字符0的ASCII值来得出周围雷的个数,并返回此值。
5.用户输入扫雷
//让用户输入坐标扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < ROW * COL - EASY_COUNT)
{
printf("请输入坐标(e.g. 1 2):>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
if (mine[x][y] == '1')
{
printf("哇哦,你被炸死了呢^_^\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int minecount = GetMineCount(mine, x, y);
show[x][y] = minecount + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
printf("该坐标已经被排查,请重新输入^_^\n");
}
}
else
{
printf("你输入的坐标有误,请重新输入^_^\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,扫雷成功^_^\n");
DisplayBoard(mine, ROW, COL);
}
}
这里的形参有两个二维数组,用来联合判断是否用户踩到雷以及判断周围雷的个数
首先需要判断用户输入的坐标是否合法,判断方法为x>=1 && x<=row && y>=1 && y<=col
如果不满足这个条件则提示坐标非法,如果满足,进入子循环,需要判断这个坐标是不是被排查过,即如果该坐标的值为字符*代表没有被排查,繁殖则为排查过,若没有被排查,进入子循环,判断该坐标是否为雷(即在mine数组中是否为字符1),若是,则提示被炸死,打印mine数组显示雷的位置并跳出循环,若不是,则计算周围雷的个数,用该处的地雷数加上字符0的ASCII值即可判断。
在这里有一个问题,什么时候才算赢?
一般来说简单模式是10个雷,而只需要选出其他所有不是雷的坐标便可胜利,因此这里引入一个变量win,给win赋值row*col-EASY_COUNT,每成功扫一个雷win就加一,直到满足条件后跳出该循环,表示游戏胜利。
6.开始界面
void menu()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
printf("*************************************\n");
printf("*************************************\n");
printf("**************1.play*****************\n");
printf("**************0.exit*****************\n");
printf("*************************************\n");
printf("*************************************\n");
printf("请选择1或0确定是否开始游戏:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏^_^\n");
break;
default:
printf("输入有误,请重新输入^_^\n");
break;
}
} while (input);
}
int main()
{
menu();
return 0;
}
这里定义了一个函数菜单,且使用循环函数,用来判断是否开始游戏,并且游戏结束后可以再次选择,用do while函数,while用input来判断,若1则循环,若0则跳出循环,选择是否开始用了switch语句,若输入1则开始执行game()函数,即开始游戏,若输入0则退出游戏,若输入其他值则提示输入有误,重新输入。
5.全部代码
在这里我们使用多个文件写代码,game.h里包含了头文件的引用,函数的申明,game.c里面实现游戏的核心操作,main.c用来引用函数来实现游戏,全部代码如下
game.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//初始化棋盘
void InitilizeBoard(char arr[ROWS][COLS], int rows, int cols, char set);
//显示棋盘show
void DisplayBoard(char arr[ROWS][COLS], int row, int col);
//随机放置地雷
void SetMine(char arr[ROWS][COLS], int row, int col);
//让用户输入坐标扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
main.c
#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘
void InitilizeBoard(char arr[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
arr[i][j] = set;
}
}
}
//显示棋盘show
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
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 ", arr[i][j]);
}
printf("\n");
}
}
//随机放置地雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
//判断周围有多少雷
int GetMineCount(char arr[ROWS][COLS], int x, int y)
{
return arr[x - 1][y - 1] +
arr[x - 1][y] +
arr[x - 1][y + 1] +
arr[x][y - 1] +
arr[x][y + 1] +
arr[x + 1][y - 1] +
arr[x + 1][y] +
arr[x + 1][y + 1] - 8 * '0';
}
//让用户输入坐标扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < ROW * COL - EASY_COUNT)
{
printf("请输入坐标(e.g. 1 2):>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
if (mine[x][y] == '1')
{
printf("哇哦,你被炸死了呢^_^\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int minecount = GetMineCount(mine, x, y);
show[x][y] = minecount + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
printf("该坐标已经被排查,请重新输入^_^\n");
}
}
else
{
printf("你输入的坐标有误,请重新输入^_^\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,扫雷成功^_^\n");
DisplayBoard(mine, ROW, COL);
}
}
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//定义两个棋盘,一个作为用户显示,另一个作为隐藏地雷的表,显示地雷位置
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//游戏主程序,这里这是申明函数,函数的实现在main.c
void game()
{
//定义两个棋盘
InitilizeBoard(mine, ROWS, COLS, '0');
InitilizeBoard(show, ROWS, COLS, '*');
//随机放置地雷
SetMine(mine, ROW, COL);
//显示棋盘show
DisplayBoard(show, ROW, COL);
//DisplayBoard(mine, ROW, COL);
//让用户输入坐标扫雷
FindMine(mine, show, ROW, COL);
}
void menu()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
printf("*************************************\n");
printf("*************************************\n");
printf("**************1.play*****************\n");
printf("**************0.exit*****************\n");
printf("*************************************\n");
printf("*************************************\n");
printf("请选择1或0确定是否开始游戏:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏^_^\n");
break;
default:
printf("输入有误,请重新输入^_^\n");
break;
}
} while (input);
}
int main()
{
menu();
return 0;
}