目录
一、实现的功能
1、可以设置雷数和面板大小
2、可以选择安全模式(第一次必不会踩到雷)和非安全模式。
3、可以知道一局游戏的时长。
4、当踩到的位置本身不是雷且周围没有雷时会连环(如果可以)炸出该位置周围不是雷的信息,而该位置周围有雷时直接显示信息。
二、最终效果显示
2.1、在game.h中可以修改行数和列数以及雷数。
(1)面板,雷10个的游戏开始效果如下图所示:
(2) 面板,雷30个的游戏开始效果如下图所示:
2.2、安全模式下测试如下所示,上面的数组(玩游戏时不会显示,这里为测试方便将其打印出来)中字符'1'即为雷,当第一次踩到雷时,程序会把这个位置变为0,然后程序会在其他地方补一个雷,如下右图所示,在游戏结束的时候会打印雷的分布位置,对比两个数组可以看到,在(5,2)的位置上新增了一个雷,而原本(1,8)的位置变为了0。
非安全模式下第一次踩到雷时游戏结束,效果如下图所示(耗时0秒后面展开说明)。
2.3、 游戏时长是从输入排查坐标后才开始计时,到游戏结束时停止计时。因此第一次踩到雷的时长为0秒。游戏时长只在游戏结束时显示,这里展示排雷成功的效果。
2.4、当排查的坐标(如下图2行2列)不是雷,但周围8个位置有雷时,会直接显示信息,效果如下图所示。
效果类似于下图。
当排查的坐标不是雷 ,且周围8个位置也没雷时会"炸出"一片信息(不会描述就这样说了哈哈),如继续排除上图中的(8,8)位置,效果如下图所示。
效果类似于下图。
三、代码
3.1、代码讲解
(1)安全模式函数
1、判断坐标是否合法,不合法重新输入,合法就判断是否踩到雷。
2、若踩到雷,将该位置用安全区替换;若没踩到雷,直接跳出循环。
3、重新产生一个随机坐标用来布雷,坐标必须保证之前该位置不是雷且不是第一次踩到雷的坐标,完成任务后跳出循环。
void safe_mode(char mine_arr[ROWS][COLS], int x, int y, int row, int col)
{
while (1)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标是否合法
{
if (mine_arr[x][y] == '1')//第一次就踩到雷
{
mine_arr[x][y] = '0';//用安全区替换
while (1)
{
//补一颗雷,雷的坐标为除去(x,y)的面板的安全区的随机值
int x1 = rand() % row + 1;
int y1 = rand() % col + 1;
if (mine_arr[x1][y1] == '0' && x1 != x && y1 != y)
{
mine_arr[x1][y1] = '1';
break;
}
}
//display_board(mine_arr, ROW, COL);//替换后查看雷的分布
break;
}
else
break;//第一次没踩到雷,跳出while循环
}
else//坐标不合法,重新输入
{
printf("输入错误,请重新输入:>");
scanf("%d%d", &x, &y);
}
}
}
(2)排雷函数
1、输入排查坐标,获取起始时间,进入安全模式或非安全模式。
2、判断坐标是否合法,合法后判断之前是否排查过。若排查过则重新输入坐标,没排查过则判断是否是雷,若是雷,获取结束时间,计算时间并打印相关提示;若不是雷,就要判断其周围一共有多少雷(进入周围雷函数)。
3、由于周围雷函数可能会“连环炸”,在判断玩家是否胜利时采用遍历实际玩的面板,计数‘*’剩余个数,若数量等于雷的个数则获取结束时间,计算时间并打印相关提示。
void seek_mine(char mine_arr[ROWS][COLS], char seek_arr[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
clock_t start_time = 0;
clock_t end_time = 0;
end_time = 0;
printf("请输入排查的坐标:>");
scanf("%d%d", &x, &y);//输入排查的坐标
start_time = clock();//获取起始时间,从输入排查坐标后再计时
safe_mode(mine_arr, x, y, ROW ,COL);//安全模式,若不需要,注释即可
while (1)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标是否合法
{
if (seek_arr[x][y] != '*')//判断坐标之前是否排查过
{
printf("此坐标不需要排查了哦!\n");
printf("请输入排查的坐标:>");
scanf("%d%d", &x, &y);//输入排查的坐标
}
else if (mine_arr[x][y] == '1')//若没排查过,判断此处是否是雷
{
end_time = clock();//获取结束时间
printf("\n你踩到雷了!\n");//若是,打印提示和原始布局面板
printf("雷数量:%d个\t耗时:%d秒\n", Mine_Total,
(unsigned int)(end_time - start_time) / CLOCKS_PER_SEC);
display_board(mine_arr, ROW, COL);
printf("是否继续游戏(1/0):");
break;
}
else
{
around_mine(mine_arr, seek_arr, x, y);//若不是,给出周边雷的信息
int i = 0;
int count = 0;//用来计数当前面板还有多少坐标未排查
for (i = 1; i <= row; i++)
{
int j = 0;
for (j = 1; j <= col; j++)
{
if (seek_arr[i][j] == '*')
count++;
}
}
//若未排查数量等于所有雷的数量,即所有不是雷的地方全找出来了
if (count == Mine_Total)
{
end_time = clock();
printf("恭喜你,成功避开所有雷\n");//打印提示和原始面板
printf("雷数量:%d个\t耗时:%d秒\n", Mine_Total,
(unsigned int)(end_time - start_time) / CLOCKS_PER_SEC);
display_board(mine_arr, ROW, COL);
break;
}
printf("------扫雷游戏-----\n");
printf("雷数量:%d\n", Mine_Total);
display_board(seek_arr, ROW, COL);//打印给出信息后的面板
printf("------扫雷游戏-----\n\n");
printf("请输入排查的坐标:>");//游戏继续
scanf("%d%d", &x, &y);//输入排查的坐标
}
}
else//坐标不合法,重新输入
{
printf("输入错误,请重新输入:>");
scanf("%d%d", &x, &y);
}
}
}
(3)周围雷函数
1、计算原始坐标周围雷的个数
2、判断是否为0,若不为0,直接将其信息赋给要显示的位置;若为0,首先将该位置赋0,再产生周围8个位置的坐标,然后继续分别判断这8个坐标周围的8个坐标(递归),注意添加限制条件,否则可能会一直递归下去造成栈溢出,实际上只需判断坐标是否在面板内并且该坐标还没被排查过。
void around_mine(char mine_arr[ROWS][COLS], char seek_arr[ROWS][COLS], int x, int y)
{
int num = count_mine(mine_arr, x, y);//计算输入坐标的周围雷的个数
if (num != 0)
{
//数量不为0时不展开,直接显示雷个数,加'0'是使整数转换为字符(如返回1,则将'1'显示)
seek_arr[x][y] = num + '0';
}
else//数量为0时展开
{
seek_arr[x][y] = '0';
int i = 0;
//产生周边8个坐标
for (i = -1; i <= 1; i++)
{
int j = 0;
for (j = -1; j <= 1; j++)
{
if (i == 0 && j == 0)
continue;//跳过原始坐标
else
{
//限制扩展坐标的范围,若不限制,容易栈溢出
if (x + i>= 1 && x + i <= 9 && y + j >= 1 && y + j <= 9
&& seek_arr[x + i][y + j] == '*')
{
around_mine(mine_arr, seek_arr, x + i, y + j);//递归计算扩展信息
}
}
}
}
}
}
3.2、源代码
(1)test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("-------------------\n");
printf("------扫雷游戏-----\n");
printf("--1.start 0.exit--\n");
printf("-------------------\n");
}
void game()
{
char Mine_arr[ROWS][COLS] = { 0 };
char Seek_arr[ROWS][COLS] = { 0 };
//初始化数组面板
Init_board(Mine_arr, ROWS, COLS, '0');
Init_board(Seek_arr, ROWS, COLS, '*');
//打印数组
printf("------扫雷游戏-----\n");
printf("雷数量:%d\n", Mine_Total);
display_board(Seek_arr, ROW, COL);
printf("------扫雷游戏-----\n\n");
//布雷
put_mine(Mine_arr, ROW, COL);
//display_board(Mine_arr, ROW, COL);//透视
//排雷
seek_mine(Mine_arr, Seek_arr, ROW, COL);
}
int main()
{
menu();
srand((unsigned int)time(NULL));
int choice = 0;
do
{
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
break;
default:
printf("Enter error!try again:");
break;
}
} while (choice);
return 0;
}
(2)game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void Init_board(char arr[ROWS][COLS], int rows, int cols, char set)
{
int x = 0;
//在rows * cols 大小的面板中将每个元素初始化为字符set
for (x = 0; x < rows; x++)
{
int y = 0;
for (y = 0; y < cols; y++)
{
arr[x][y] = set;
}
}
}
void display_board(char arr[ROWS][COLS], int row, int col)
{
int x = 0;
for (x = 0; x <= col; x++)
{
printf("%-2d ", x);//打印纵坐标提示信息
}
printf("\n");
//打印row * col大小的面板
for (x = 1; x <= row; x++)
{
printf("%-2d ", x);//打印行坐标提示信息
int y = 0;
for (y = 1; y <= col; y++)
{
printf("%-2c ",arr[x][y]);
}
printf("\n");
}
}
void put_mine(char arr[ROWS][COLS], int row, int col)
{
//计雷的数量
int count = 0;
while (count < Mine_Total)//布置Mine_Total个雷
{
//随机生成雷的坐标
int x = rand() % row + 1;
int y = rand() % col + 1;
//判断位置是否为雷
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count++;
}
}
}
int count_mine(char mine_arr[ROWS][COLS], int x, int y)
{
return mine_arr[x - 1][y - 1] + mine_arr[x - 1][y] + mine_arr[x - 1][y + 1] +
mine_arr[x][y - 1] + mine_arr[x][y + 1] + mine_arr[x + 1][y - 1] +
mine_arr[x + 1][y] + mine_arr[x + 1][y + 1] - 8 * '0';//返回雷个数(减去8个字符0的ASCII码后即为整数个数)
}
void around_mine(char mine_arr[ROWS][COLS], char seek_arr[ROWS][COLS], int x, int y)
{
int num = count_mine(mine_arr, x, y);//计算输入坐标的周围雷的个数
if (num != 0)
{
//数量不为0时不展开,直接显示雷个数,加'0'是使整数转换为字符(如返回1,则将'1'显示)
seek_arr[x][y] = num + '0';
}
else//数量为0时展开
{
seek_arr[x][y] = '0';
int i = 0;
//产生周边8个坐标
for (i = -1; i <= 1; i++)
{
int j = 0;
for (j = -1; j <= 1; j++)
{
if (i == 0 && j == 0)
continue;//跳过原始坐标
else
{
//限制扩展坐标的范围,若不限制,容易栈溢出
if (x + i>= 1 && x + i <= 9 && y + j >= 1 && y + j <= 9
&& seek_arr[x + i][y + j] == '*')
{
around_mine(mine_arr, seek_arr, x + i, y + j);//递归计算扩展信息
}
}
}
}
}
}
void safe_mode(char mine_arr[ROWS][COLS], int x, int y, int row, int col)
{
while (1)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标是否合法
{
if (mine_arr[x][y] == '1')//第一次就踩到雷
{
mine_arr[x][y] = '0';//用安全区替换
while (1)
{
//补一颗雷,雷的坐标为除去(x,y)的面板的安全区的随机值
int x1 = rand() % row + 1;
int y1 = rand() % col + 1;
if (mine_arr[x1][y1] == '0' && x1 != x && y1 != y)
{
mine_arr[x1][y1] = '1';
break;
}
}
break;
}
else
break;//第一次没踩到雷,跳出while循环
}
else//坐标不合法,重新输入
{
printf("输入错误,请重新输入:>");
scanf("%d%d", &x, &y);
}
}
}
void seek_mine(char mine_arr[ROWS][COLS], char seek_arr[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
clock_t start_time = 0;
clock_t end_time = 0;
end_time = 0;
printf("请输入排查的坐标:>");
scanf("%d%d", &x, &y);//输入排查的坐标
start_time = clock();//获取起始时间,从输入排查坐标后再计时
safe_mode(mine_arr, x, y, ROW ,COL);//安全模式,若不需要,注释即可
while (1)
{
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标是否合法
{
if (seek_arr[x][y] != '*')//判断坐标之前是否排查过
{
printf("此坐标不需要排查了哦!\n");
printf("请输入排查的坐标:>");
scanf("%d%d", &x, &y);//输入排查的坐标
}
else if (mine_arr[x][y] == '1')//若没排查过,判断此处是否是雷
{
end_time = clock();//获取结束时间
printf("\n你踩到雷了!\n");//若是,打印提示和原始布局面板
printf("雷数量:%d个\t耗时:%d秒\n", Mine_Total,
(unsigned int)(end_time - start_time) / CLOCKS_PER_SEC);
display_board(mine_arr, ROW, COL);
printf("是否继续游戏(1/0):");
break;
}
else
{
around_mine(mine_arr, seek_arr, x, y);//若不是,给出周边雷的信息
int i = 0;
int count = 0;//用来计数当前面板还有多少坐标未排查
for (i = 1; i <= row; i++)
{
int j = 0;
for (j = 1; j <= col; j++)
{
if (seek_arr[i][j] == '*')
count++;
}
}
//若未排查数量等于所有雷的数量,即所有不是雷的地方全找出来了
if (count == Mine_Total)
{
end_time = clock();
printf("恭喜你,成功避开所有雷\n");//打印提示和原始面板
printf("雷数量:%d个\t耗时:%d秒\n", Mine_Total,
(unsigned int)(end_time - start_time) / CLOCKS_PER_SEC);
display_board(mine_arr, ROW, COL);
break;
}
printf("------扫雷游戏-----\n");
printf("雷数量:%d\n", Mine_Total);
display_board(seek_arr, ROW, COL);//打印给出信息后的面板
printf("------扫雷游戏-----\n\n");
printf("请输入排查的坐标:>");//游戏继续
scanf("%d%d", &x, &y);//输入排查的坐标
}
}
else//坐标不合法,重新输入
{
printf("输入错误,请重新输入:>");
scanf("%d%d", &x, &y);
}
}
}
(3)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 Mine_Total 10//雷的个数
void Init_board(char arr[ROWS][COLS], int rows, int cols, char set);//初始化面板函数
void display_board(char arr[ROWS][COLS], int row, int col);//打印面板函数
void put_mine(char arr[ROWS][COLS], int row, int col);//布雷函数
void seek_mine(char mine_arr[ROWS][COLS], char seek_arr[ROWS][COLS], int row, int col);//排雷函数
四、结语
程序可能存在未知的bug,欢迎各位读者一起交流。