扫雷—C语言初阶小游戏(二)
1.头文件game.h
在头文件的部分,我们得先想清楚我们该如何实现各个步骤,需要哪些函数,这个时候不需要想的太细致,只需有思路,相关参数,返回类型可以之后完善。
在这里,我先提出几个比较核心的问题:
1.如何埋雷,手动埋吗
2.在棋盘(二维数组)中,如何同时把雷的坐标记录好,又把每个坐标周围的雷个数记录好,会引起混乱吗?
3.每个坐标周围的雷个数如何统计?
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define EASY_COUNT 10
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void DisplayBoard(char board[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);
相信看到这里,你一定一脸懵逼 ,没关系,我会在具体的函数实现过程中把每个步骤细节讲到。
2.源文件game.c
首先,我们需要两个二维数组。一个布置雷,一个排查雷(也就是玩游戏时看到的界面,会显示被选中格子的周围雷个数)。
为什么要两个二维数组?
很显然,我们需要一个二维数组来存放数据,存什么呢,第一,雷;第二,每个格子周围的雷个数。这样存储看上去并没有什么弊端,因为一个格子如果有雷,就不会显示周围雷个数,所以不会造成一个格子存两种数据。
但是,当我们企图统计每个格子周围的雷个数时,就会变得棘手,我们假定雷为‘1’,无雷区为‘0’,当我们要统计无雷格子周围的雷个数时,遍历四周八个格子,却发现有的格子存着每个格子周围的雷个数,我们在遍历时就要跳过这类格子,只关注有没有雷,这使我们的程序更复杂,可读性也更弱。
所以这里我们使用两个数组,一个布置雷,一个排查雷(显示周围雷数),避免上述问题。
实际上,布置雷的数组不会显示(显示了还玩啥??),玩家只会看到排雷的数组。我们输入我们想排查的坐标,如果正好是雷,就游戏失败,如果不是雷,就显示当前坐标的周围雷个数,一直到所有雷被找出胜利。
在这里,我们初始规定,布置雷数组 初始化为‘0’,雷为‘1’。排查雷数组 初始化为 ‘*’。 所以打开游戏,你会看到满盘的 * 号。
这里还有一个小细节,我们的数组改设置多大?(假设游戏为9*9个格子,10个雷)。实际上,这里设置应该设置11,倒不是为了好看之类的,是因为当我们要统计无雷格子周围的雷个数时,遍历四周八个格子,会存在边缘或角落格子,他们周围根本没有八个格子,这时候我们遍历的条件很肯能就不适合了,当然你可以分类讨论,改一改条件,不过不如把数组上下左右都拓宽一个,只把数据放在中间,这样所有格子周围都有八个格子,不用分类了。
大致思路解决了,接下来我们开始实现:
1.初始化函数
布置雷数组 初始化为‘0’。
排查雷数组 初始化为 ‘*’。
ps:初始化函数只有一个,但我们得调用两次,而且赋值不同,所以初始化的值也要作为参数传入,即定义char set的原因.
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
2.打印函数
这里我们为了美观,加入了行 ,列序号,毕竟各自太多容易看走眼
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("---------扫雷游戏---------\n");
//打印列序号
for (i = 0; i <= col; i++)//列序号
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);//行序号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("---------扫雷游戏---------\n");
}
3.布置雷
随机产生雷,并给相应坐标赋值为‘1’.
void SetMine(char mine[ROWS][COLS], int row, int col)
{
//布置10个雷
int cout = EASY_COUNT;
while (cout)
{
//产生随机坐标
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
cout--;
}
}
}
4.统计周围雷函数
static int get_mine_cou(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0';
}
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("请输入要排查的坐标:>");
scanf_s("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, row, col);
break;
}
else
{
int cout = get_mine_cou(mine, x, y);
show[x][y] = cout + '0';//把返回的ASCALL变回数字
//显示排除查信息
DisplayBoard(show, row, col);
win++;
}
}
else
{
printf("坐标不合法,请重新输入\n");
}
}
}
3.测试文件test.c
#include"game.h"
void menu()
{
printf("***********************************\n");
printf("***************1.play**************\n");
printf("***************0.exit**************\n");
printf("***********************************\n");
}
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
DisplayBoard(show, ROW, COL);
SetMine(mine, ROW, COL);
FindMine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
do
{
menu();
printf("请输入:>");
scanf_s("%d", & input);
switch (input)
{
case 1:
game();//扫雷游戏
break;
case 0:
printf("退出游戏\n");
break;
default:
break;
}
} while (input);
return 0;
}