目录:
1、设计思路
1.1函数概要设计
1.2流程
2、函数实现
3、文件及函数布置
1、设计思路
1、1流程
扫雷游戏,首先让玩家知晓如何进入游戏,退出游戏,这里需要给玩家提示,设计一个菜单选项函数menu(),想支持玩家一局一局的连续玩,玩家可选择继续游戏,或者退出游戏,该过程是需要一个循环实现,同时里面包含不同分支,进入游戏分支和退出游戏分支,同时可甄别玩家输入的选项是否合法。
扫雷开始前,需要准备一个棋盘,显示给玩家,同时需要对该棋盘上进行布雷,为实现这两个目的,设置两个相同大小的数组,一个用于显示给玩家show_border[][],一个用于保存布雷的位置mine[][]。定义这两个数组时,对数组进行初始化,这里引入初始化函数Init()实现该功能。初始化函数中 对show_border[][]类型选用字符数组 ,初始化为'*'可以很好的隐藏雷的位置,mine[][]选用字符型数组,初始化为字符‘0’。在mine数组中完成初始化以后,需要在该数组中随机布雷,引入set_mine()函数,布雷的数量需要提前设置,传入该函数,每次布雷,使用rand()进行随机选择行和列,将雷所在位置元素设置为'1',表示该位置有一个雷。由于是随机布置,有可能会选到相同的位置,为此需进行判断。实现连续布雷,需要以布雷数量作为参数设计一个循环。为方便将棋盘显示给玩家,引入print_函数,将棋盘打印给玩家。考虑到在排雷过程中计算每个位置周围8个位置的布雷数量,为计数方便,将需打印的出来的棋盘外围加一层,例如,打印的棋盘为9*9时,show_border 和mine均设置为11*11。
扫雷游戏开始,需使用一个计数变量来记录玩家总共的排雷次数,以便判断是否已将全部雷排除,退出本次排雷游戏,引入fine_mine()函数实现玩家可以连续排雷,所以排雷在一个循环中完成。当玩家选择排雷位置时,需要判断玩家输入的位置是否合法,是否在打印的棋盘内,同时该位置没有排查过,方可进行排雷, 当该位置有雷,需提示玩家排雷失败,退出排雷循环,退出游戏。引入count_mine()函数计算排雷位置周围的布雷数量,当进入该函数时,表明该位置是没有雷的,如此在计算周围8个位置的雷时,可以连同当前位置进行计算,不会对结果造成影响,可以设置一个嵌套循环排查3*3 9个位置的布雷数量。将计算出周围布雷数量填到当前位置,当当前位置没有雷时,可以自动排查周围位置的雷数量,即进行递归,可以设计逐个排查周围8个位置,需注意为避免出现死循环,对这8个位置进行限制,其一,该位置没有雷(上面已经满足该条件),该位置没有被排查过,show_border数组该位置为‘*’表示该位置没有被排查,为防止该位置为最外一层位置,即第一行、最后一行、第一列、最后一列,会造成死循环。对该位置坐标需进行限制为打印数组的坐标范围内,也可以设计一个嵌套循环调用3*3,共9次递归函数,实现对该位置周围位置的排查。
具体流程如下:
2、函数实现
菜单函数:
void menu_1()
{
printf("****************************\n");
printf("***** 1、play 0、exit *****\n");
printf("****************************\n");
}
初始化函数
void init_(char show_board[ROW_][COL_],char mine[ROW_][COL_],int row_,int col_)
{
int i = 0;
int j = 0;
for (i = 0; i < row_; i++)
{
for (j = 0; j < col_; j++)
{
show_board[i][j] = '*';
mine[i][j] = '0';
}
}
}
打印函数
//便于玩家观察棋盘首行和首列分别打印行号和列号,打印部分的数组与show_border 和mine 同行同列
void print_(char arr[ROW_][COL_],int row,int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= row; i++)
{
for (j = 0; j <= col; j++)
{
if (i == 0)
{
printf("%c ", j + '0');
}
else if(j == 0)
{
printf("%c ", i + '0');
}
else
{
printf("%c ", arr[i][j]);
}
}
printf("\n");
}
}
布雷函数:
void set_mine(char mine[ROW_][COL_],int row,int col,int sum)
{
int i = 0;
int j = 0;
//for (int k = 0; k < sum; k++)//该屏蔽部分,忽略了已布雷位置是不能布雷的。
//{
// i = rand() % row + 1;
// j = rand() % col + 1;
// brr[i][j] = '1';
//}
while (sum)
{
i = rand() % row + 1;//用于生成随机数
j = rand() % col + 1;
if (mine[i][j] == '0')//当该位置没有布置雷,可以在该位置布置
{
mine[i][j] = '1';
sum--;
}
}
}
计算布雷数量函数
void count_mine(char show_board[ROW_][COL_], char mine[ROW_][COL_], int row, int col,int*p_win)
{
int count = 0;
int i = 0;
int j = 0;
int m = 0;
int n = 0;
int row_1 = 0;
int col_1 = 0;
int row_ = ROW_;
int col_ = COL_;
for (i = -1; i < 2; i++)
{
for (j = -1; j < 2; j++)
{
if (row + i >= 0 && row + i < row_ && col + j >= 0 && col + j < col_)
{
if (mine[row + i][col + j] == '1')
{
count++;
}
}
}
}
(* p_win)++;
show_board[row][col] = '0' + count;//将排查结果记录
if (count == 0)//说明周围没有雷,且该位置没有雷
{
for (m = -1; m < 2; m++)
{
for (n = -1; n < 2; n++)
{
row_1 = row + m;
col_1 = col + n;
if (show_board[row_1][col_1] == '*'&& row_1>= 1&&row_1<=ROW&&col_1>=1&&col_1<=COL)//判断递归条件:该位置没有被排查过,该位置合法
{
count_mine(show_board, mine, row_1, col_1,p_win);
}
}
}
}
}
排雷函数
//返回玩家排雷次数,以供判断玩家是否成功排雷结束
int find_mine(char arr[ROW_][COL_], char brr[ROW_][COL_])
{
int win = 0;
while (win < ((COL * ROW) - SUM))
{
int row = 0;
int col = 0;
int count = 0;
int i = 0;
int j = 0;
printf("开始扫雷->\n");
printf("请输入坐标->\n");
scanf("%d%d", &row, &col);
if (row >= 1 && row <= ROW && col >= 1 && col <= COL)
{
if (arr[row][col] != '*')
{
printf("该位置已扫过,请重新输入->\n");
}
else
{
if (brr[row][col] == '1')
{
printf("被炸死了\n");
break;
}
else
{
count_mine(arr, brr, row, col, &win);
print_(arr, ROW, COL);
}
}
}
else
{
printf("输入非法坐标,请重新输入->\n");
}
}
return win;
}
排雷游戏函数
void game_1()
{
char show_board[ROW_][COL_];
char mine[ROW_][COL_];
init_(show_board, mine, ROW_, COL_);
print_(show_board, ROW, COL);
set_mine(mine, ROW, COL, SUM);
//print_(mine, ROW, COL);
int win = 0;
win = find_mine(show_board, mine);
if (win == ((COL * ROW) - SUM))
{
printf("已排除全部雷\n");
print_(mine, ROW, COL);
}
}
3、文件及函数布置
该程序包含3个文件 test.c 、minesweeping.h、minesweeping.c,test.c中放置排雷游戏函数和测试函数即主函数。主函数如下:
# include "minesweeping.h"
int main()
{
srand((unsigned int) time(NULL));//随机数产生器的初始值 (种子值)
int input = -1;
while (input)
{
menu_1();
scanf("%d", &input);
switch (input)
{
case 1:
{
printf("游戏开始->\n");
game_1();
break;
}
case 0:
{
printf("游戏退出->\n");
break;
}
default:
{
printf("输入错误,请重新输入->\n");
break;
}
}
}
return 0;
}
引入函数的实现,均放置在minesweeping.c,minesweeping.h中放置头文件,和函数声明。minesweeping.h 如下所示,其中的ROW COL表示打印的行和列,而ROW_ 和COL_ 表示show_mine 和mine的行和列,采用宏定义方式,可实现快速扩展。
# include<stdlib.h>
# include<time.h>
# include<stdio.h>
# define ROW 4
# define COL 4
# define ROW_ ROW+2
# define COL_ COL+2
# define SUM 15//雷数量
void menu_1();
void game_1();
void init_(char show_board[ROW_][COL_], char mine[ROW_][COL_], int row_, int col_);
void print_(char arr[ROW_][COL_], int row, int col);
void set_mine(char mine[ROW_][COL_], int row, int col, int sum);
void count_mine(char show_mine[ROW_][COL_], char mine[ROW_][COL_], int row, int col,int* p_win);
int find_mine(char show_mine[ROW_][COL_], char mine[ROW_][COL_]);