1.基础版实现思路
大致思路就分为:
扫雷的时候,我们需要两个二维数组,一个用来存放数据,另一个用来展示给玩家看;
所以我们需要对两个相同大小的数组进行不同的初始化,我们可以通过传递不同的参数来实现
(因为考虑到在数组最外围的坐标,遍历周围是否有雷时,可能会出现越界访问的情况,所以我们的二维数组的大小应该比我们需要用到的大上一圈)
游戏的效果:
2.改进
改进的思路大致为:
2.1可以对周围没有雷的坐标进行展开
当输入坐标合法,且周围坐标没有雷时,可以进行向外展开
我们可以用递归的方式实现周围八个坐标的连续展开
//将周围八个坐标的ASICC码值加起来,减去8个‘0’的ASICC码值,最后可以得到周围字符为'1'的个数
int how_many_mine(char arr[ROWS][COLS], int row, int col, int x, int y)//判断周围有多少雷
{
return (arr[x + 1][y] + arr[x + 1][y - 1] + 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的位置,用 how_many_mine遍历周围8个坐标,如果返回值为0,循环执行此操作
//递归:用 how_many_mine计算当前位置的周围坐标返回值不为0,当前坐标等于这个函数的返回值
void mine_spread(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS],int row, int col, int x, int y)//展开(递归)
{
if (x >= 1 && x <= row && y >= 1 && y <= col && mine_show[x][y] != ' ')
{
int n = how_many_mine(mine_ifm, ROW, COL, x, y);
if (n == 0)
{
mine_show[x][y] = ' ';
mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y - 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y + 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x, y - 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x, y + 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y - 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y - 1);//展开
}
else
{
mine_show[x][y] = n + '0';
}
}
}
最后的效果是:
2.2使玩家不会在一开始就踩到雷
如果我们的运气不是太好,总是一开始就被炸死了,那就会显得很尴尬😅,我能被自己写的游戏给玩了?
我们可以将布置雷和扫雷两个步骤放到一个函数中,设置一个全局变量set,先输入一个合法坐标,当判断set等于1时,生成雷,生成的雷在指定范围内且不能等于输入的坐标,set改变。可以在进入game()函数之前,给set重置为1,方便下一次游戏继续使用。
生成雷的函数:
void raise_mine(char mine_ifm[ROWS][COLS], int row, int col, int a, int b)//生成雷
{
int i = MINE;
while (i)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine_ifm[x][y] == '0' && x > 0 && y > 0)
{
if (x == a && y == b)
{
continue;
}
mine_ifm[x][y] = '1';
i--;
}
}
}
这样的话即使我们一共有81个坐标,我们给80个雷,雷也会绕着我们走:
2.3玩家可以标记雷
当周围的雷太多,记不住,猪脑过载了怎么办?
我们可以在输入坐标进行扫雷的时候多加一个选项,也就是标记雷;
标记雷:也是输入一个坐标,判断是否合法,只是说不用计算周围雷的个数,而是直接将show数组上你输入的位置的字符改为你标记的字符(再次输入取消标记);
判断输赢时也记得带上它!
void player_mine(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col)//玩家扫雷
{
int x = 0;
int y = 0;
int how = 0;
int set = 1;
int n = 0;
while (1)
{
system("cls");
print_show(mine_show, ROW, COL);//打印数据扫雷棋盘
printf("请选择:\n1.扫雷 2.标记雷\n");
scanf("%d", &n);
if (n == 1)//扫雷
{
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
{
if (mine_show[x][y] == MODEL|| mine_show[x][y] == MARK)//坐标没有被排查过
{
if (set == 1)//没有扫过雷
{
raise_mine(mine_ifm, ROW, COL, x, y);//生成雷
set--;
}
system("cls"); //清除屏幕
if (mine_ifm[x][y] == '1')
{
printf("你被炸死了,游戏结束\n");
print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
break;
}
else
{
how = how_many_mine(mine_ifm, ROW, COL, x, y);
if (how == 0)
{
mine_spread(mine_ifm, mine_show, ROW, COL, x, y);//展开
}
else
{
mine_show[x][y] = how + '0';
}
print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
if (is_win(mine_show, ROW, COL, MODEL, MARK) == MINE)//判断输赢
{
printf("恭喜你,排雷成功\n");
print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
break;
}
}
}
else
printf("该坐标已被排查\n");
}
else
printf("坐标输入错误,请重新输入\n");
}
else if (n == 2)//标记雷
{
printf("如果输入已被标记的坐标,将被视为清除标记 ! ! !\n\n\a");
printf("请输入要标记的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
{
system("cls"); //清除屏幕
if (mine_show[x][y] == MODEL)//坐标没有被排查过
{
mine_show[x][y] = MARK; //标记雷
}
else if (mine_show[x][y] == MARK)
{
mine_show[x][y] = MODEL; //重复标记视为清除标记
printf("清除标记成功\n");
}
else
printf("该坐标已被排查\n");
print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
}
else
printf("坐标输入错误,请重新输入\n");
}
else
printf("选择错误\n");
}
}
2.4可以多人扫雷,积分制
正所谓,知己知彼,百战不殆。为了实现扫雷的更多功能,我去玩了好多不同版本的扫雷,发现一些平台上扫雷还能联机玩!
大致思路就是:
四个人,每人选定一个坐标扫雷,也可以选择标记雷;扫雷成功或标记雷成功都会获得分数,如果踩到雷或标记雷失败,则会扣分;每轮都有计时(我不会实现🤦♂️)
如果两人选择同一个坐标则得到的分数减半;最后计算总分判断输赢
2.5实现代码
最后我们直接看效果:
那让我们来浅浅实现一下吧:
text.c
#define _CRT_SECURE_NO_WARNINGS 1
extern set;
//1.创建两个二维数组,一个存放雷的信息,一个展示给玩家
//2.将两个数组初始化,一个全为字符0,一个全为*
//3.打印扫雷的棋盘
//4.在第一个数组中生成雷,玩家以后的操作都是在第一个数组中操作,再由数组2显示
//5.扫雷。。。
//扫雷:玩家输入一个坐标,如果这个坐标不是雷,显示周围雷的个数
//改进:
//1.展开
//如果棋子周围没有雷,那么可以从当前位置向周围展开,判断周围坐标的周围是否有雷
//有:返回雷的个数,没有:从当前位置继续展开
//2.避免一开始就踩雷
//将布置雷和扫雷放在一起,定义一个变量set,没有生成雷时为1,生成之后为0
//3.标记雷
//输入时,有两个选项1.扫雷,2.标记雷
//注意:标记雷也算作是MODEL
//4.联机模式
//玩家可以选择联机对战,大致思路如下:
//积分制:1.标记一次雷得两分;标记错误扣一分,坐标还原为MODEL;2.扫雷得一分,踩到雷扣三分
//玩家1下棋,执行完操作后进行积分结算,并根据操作改变mine_show,打印,等待玩家2操作
//玩家2下棋,执行完操作后进行积分结算,改变mine_show数组,打印
//玩家踩雷时,不应该结束游戏,应该进行扣分;所以我们要为联机模式下的判断输赢加上一些限制条件
//打印棋盘
#include "game.h"
void menu1()
{
printf("-------------扫雷游戏--------------\n");
printf("***********************************\n");
printf("************1.开始游戏*************\n");
printf("***********************************\n");
printf("************0.退出游戏*************\n");
printf("***********************************\n");
}
void menu2()
{
printf("-------------扫雷游戏--------------\n");
printf("—————————————————-\n");
printf("——————1.经典模式——————-\n");
printf("—————————————————-\n");
printf("——————2.联机对战——————-\n");
printf("—————————————————-\n");
}
void game(int input)
{
char mine_ifm[ROWS][COLS] = { 0 };//放置雷的信息
initial(mine_ifm, ROWS, COLS, '0');//数组的初始化
char mine_show[ROWS][COLS] = { 0 };//展示出来信息
initial(mine_show, ROWS, COLS, MODEL);//数组的初始化
//print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
//print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
//扫雷
if (input == 1)
player_mine(mine_ifm, mine_show, ROW, COL);//玩家扫雷&&布置雷(经典)
else if (input == 2)
{
printf("规则提示:\n");
printf("扫雷成功,玩家获得2分,踩到雷则扣3分\n");
printf("标记雷成功,玩家获得2分,标记失败则扣1分\n");
printf("当棋盘上只剩下雷时,游戏结束,分高的一方获得胜利\n");
player_with_friend(mine_ifm, mine_show, ROW, COL);//(联机)
}
}
int main()
{
int input1 = 0;
int input2 = 0;
srand((unsigned int)time(NULL));
do
{
set = 1;
menu1();
printf("请选择:>");
scanf("%d", &input1);
switch (input1)
{
case 1:
menu2();
scanf("%d",&input2);
if (input2 >= 1 && input2 <= 2)
{
game(input2);
}
else
printf("选择错误,请重新选择\n");
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("输入错误,重新输入!\n");
}
}while (input1);
return 0;
}
game.h
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#define ROW 9 //展示出的扫雷棋盘的【行】
#define COL 9 //展示出的扫雷棋盘的【列】
#define ROWS ROW+2 //扫雷棋盘数据的【行】
#define COLS COL+2 //扫雷棋盘数据的【列】
#define MINE 9 //雷的数量
#define MODEL '-' //雷的模型
#define MARK '@' //标记的模型
void initial(char mine_ifm[ROWS][COLS], int rows, int cols, char n);//数组的初始化
void print_show(char mine_ifm[ROWS][COLS], int row, int col);//打印棋盘
void raise_mine(char mine_ifm[ROWS][COLS], int row, int col);//生成雷
void player_mine(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col);//经典模式
void player_with_friend(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col);//联机对战
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
int set = 1;
void initial(char mine_ifm[ROWS][COLS], int rows, int cols, char n)//数组的初始化
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
mine_ifm[i][j] = n;
}
}
}
void print_show(char mine_ifm[ROWS][COLS], int row, int col)//扫雷棋盘的打印
{
int i = 0;
int j = 0;
printf("———————扫雷游戏——————-\n");
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
printf(" %c |",mine_ifm[i][j]);
}
printf(" %d",i);//打印坐标【行】
printf("\n");
for (j = 1; j <= col; j++)
{
printf("-—|");
}
printf("\n");
}
//打印坐标【列】
for (j = 1; j <= col; j++)
{
printf(" %d ",j);
}
printf(" 0\n");
printf("———————扫雷游戏——————-\n");
}
void raise_mine(char mine_ifm[ROWS][COLS], int row, int col, int a, int b)//生成雷
{
int i = MINE;
while (i)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine_ifm[x][y] == '0' && x > 0 && y > 0)
{
if (x == a && y == b)
{
continue;
}
mine_ifm[x][y] = '1';
i--;
}
}
}
int is_win(char arr[ROWS][COLS], int row, int col, char model, char mark)//判断输赢
{
int i = 0;
int j = 0;
int count = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (arr[i][j] == model || arr[i][j] == mark)
count++;
}
}
return count;
}
//将周围八个坐标的ASICC码值加起来,减去8个‘0’的ASICC码值,最后可以得到周围字符为'1'的个数
int how_many_mine(char arr[ROWS][COLS], int row, int col, int x, int y)//判断周围有多少雷
{
return (arr[x + 1][y] + arr[x + 1][y - 1] + 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的位置,用 how_many_mine遍历周围8个坐标,如果返回值为0,循环执行此操作
//递归:用 how_many_mine计算当前位置的周围坐标返回值不为0,当前坐标等于这个函数的返回值
void mine_spread(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS],int row, int col, int x, int y)//展开(递归)
{
if (x >= 1 && x <= row && y >= 1 && y <= col && mine_show[x][y] != ' ')
{
int n = how_many_mine(mine_ifm, ROW, COL, x, y);
if (n == 0)
{
mine_show[x][y] = ' ';
mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y - 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x - 1, y + 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x, y - 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x, y + 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y - 1);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y);//展开
mine_spread(mine_ifm, mine_show, ROW, COL, x + 1, y - 1);//展开
}
else
{
mine_show[x][y] = n + '0';
}
}
}
void player_mine(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col)//玩家扫雷
{
int x = 0;
int y = 0;
int how = 0;
int set = 1;
int n = 0;
while (1)
{
system("cls");
print_show(mine_show, ROW, COL);//打印数据扫雷棋盘
printf("请选择:\n1.扫雷 2.标记雷\n");
scanf("%d", &n);
if (n == 1)//扫雷
{
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
{
if (mine_show[x][y] == MODEL|| mine_show[x][y] == MARK)//坐标没有被排查过
{
if (set == 1)//没有扫过雷
{
raise_mine(mine_ifm, ROW, COL, x, y);//生成雷
set--;
}
system("cls"); //清除屏幕
if (mine_ifm[x][y] == '1')
{
printf("你被炸死了,游戏结束\n");
print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
break;
}
else
{
how = how_many_mine(mine_ifm, ROW, COL, x, y);
if (how == 0)
{
mine_spread(mine_ifm, mine_show, ROW, COL, x, y);//展开
}
else
{
mine_show[x][y] = how + '0';
}
print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
if (is_win(mine_show, ROW, COL, MODEL, MARK) == MINE)//判断输赢
{
printf("恭喜你,排雷成功\n");
print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
break;
}
}
}
else
printf("该坐标已被排查\n");
}
else
printf("坐标输入错误,请重新输入\n");
}
else if (n == 2)//标记雷
{
printf("如果输入已被标记的坐标,将被视为清除标记 ! ! !\n\n\a");
printf("请输入要标记的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
{
system("cls"); //清除屏幕
if (mine_show[x][y] == MODEL)//坐标没有被排查过
{
mine_show[x][y] = MARK; //标记雷
}
else if (mine_show[x][y] == MARK)
{
mine_show[x][y] = MODEL; //重复标记视为清除标记
printf("清除标记成功\n");
}
else
printf("该坐标已被排查\n");
print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
}
else
printf("坐标输入错误,请重新输入\n");
}
else
printf("选择错误\n");
}
}
int play(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col, int score)//联机对战
{
int n = 0;
int x = 0;
int y = 0;
int how = 0;
again:
printf("请选择:\n1.扫雷 2.标记雷\n");
scanf("%d", &n);
if (n == 1)//扫雷
{
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
{
if (mine_show[x][y] == MODEL || mine_show[x][y] == MARK)//坐标没有被排查过
{
if (set == 1)//没有扫过雷
{
raise_mine(mine_ifm, ROW, COL, x, y);//生成雷
set--;
}
if (mine_ifm[x][y] == '1')
{
score = score - 3; //被炸了,扣3分
}
else
{
how = how_many_mine(mine_ifm, ROW, COL, x, y);
if (how == 0)
{
mine_spread(mine_ifm, mine_show, ROW, COL, x, y);//展开
}
else
{
mine_show[x][y] = how + '0';
}
score = score + 2;//玩家正确排雷,得到1分
}
}
else
{
printf("该坐标已被排查\n");
goto again;
}
}
else
{
printf("坐标输入错误,请重新输入\n");
goto again;
}
system("cls");
print_show(mine_show, ROW, COL);//打印数据扫雷棋盘
}
else if (n == 2)//标记雷
{
printf("请输入要标记的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//输入坐标在指定范围内
{
if (mine_show[x][y] == MODEL)//坐标没有被排查过
{
mine_show[x][y] = MARK; //标记雷
if (mine_ifm[x][y] == '1')
{
score = score + 2; //标记雷成功,得两分
}
else
{
score = score - 1; //标记雷失败,扣1分
}
}
else if (mine_show[x][y] == MARK)
{
printf("请勿标记已排查过的雷\n");
goto again;
}
else
{
printf("该坐标已被排查\n");
goto again;
}
}
else
{
printf("输入坐标超出范围\n");
goto again;
}
}
else
{
printf("选择错误\n");
goto again;
}
return score;
}
void player_with_friend(char mine_ifm[ROWS][COLS], char mine_show[ROWS][COLS], int row, int col)//联机对战
{
int n = 0;
int score1 = 0; //玩家1的得分
int score_1 = 0;
int score2 = 0; //玩家2的得分
int score_2 = 0;
while (1)
{
print_show(mine_show, ROW, COL);//打印数据扫雷棋盘
score_1 = score1;
score_2 = score2;
printf("玩家1扫雷:>\n");
score1 = play(mine_ifm, mine_show, ROW, COL, score1);
printf("玩家2扫雷:>\n");
score2 = play(mine_ifm, mine_show, ROW, COL, score2);
system("cls");
int i = 0;
printf("正在计算结果");
for (i = 0; i < 3; i++)
{
printf(".");
Sleep(500);
}
printf("\n");
print_show(mine_show, ROW, COL);//打印显示扫雷棋盘
printf("玩家1得分:%d,玩家2得分:%d\n", score1 - score_1, score2 - score_2);
if (score1 < 0)
score1 = 0;
if (score2 < 0)
score2 = 0;
printf("\n玩家1的总分是 %d\n", score1);
printf("玩家2的总分是 %d\n", score2);
Sleep(3000);
if (is_win(mine_show, ROW, COL, MODEL, MARK) == MINE)//判断输赢
{
printf("雷已排完,玩家1得分:%d,玩家2得分%d\n", score1, score2);
if (score1 > score2)
printf("玩家1胜利\n");
else if (score1 < score2)
printf("玩家2胜利\n");
else
printf("比分相同,平局\n");
print_show(mine_ifm, ROW, COL);//打印数据扫雷棋盘
break;
}
system("cls");
}
}