第一次尝试编写扫雷游戏,对于我们小白来说,是对逻辑能力的培养。在创作扫雷游戏的过程中不断创新去优化这个游戏,也是一个自我提升与提高逻辑能力的最有效的方法。
一、总代码
我先将自己的总代码放在这里,以供大家参考。
这里用了一个自创头文件game.h,与两个源文件 game.c和test.c。
game.h里面存放的是test.c文件中所需要的头文件以及对一些函数的声明:
#pragma once
#include<stdio.h>
#include<windows.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define set_count 10
#include<time.h>
#include<stdlib.h>
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);
void DisPlay(char board[ROWS][COLS], int row, int col);
void Set_mine(char ms[ROWS][COLS], int row, int col);
void Findmine(char ms[ROWS][COLS], char dp[ROWS][COLS], int row, int col);
game.c中存放的便是test.c文件中game()函数中所需要的一些自定义函数的实现:
#include"game.h"
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 < ROWS; j++)
{
board[i][j] = set;
}
}
}
void DisPlay(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("--------扫雷-------\n");
for (i = 0; i <= ROW; 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");
}
void Set_mine(char ms[ROWS][COLS], int row, int col)
{
int count = set_count;
while (count)
{
int x = rand() % ROW + 1;
int y = rand() % COL + 1;
if (ms[x][y] == '0')
{
ms[x][y] = '1';
count--;
}
}
}
int GetMine(char ms[ROWS][COLS], int x, int y)
{
return ms[x - 1][y - 1] +
ms[x - 1][y] +
ms[x - 1][y + 1] +
ms[x][x - 1] +
ms[x][y + 1] +
ms[x + 1][y - 1] +
ms[x + 1][y] +
ms[x + 1][y + 1] - 8 * '0';
}
void Disapper(char ms[ROWS][COLS], char dp[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int c = GetMine(ms, x, y);
if (ms[x][y] == '0')
{
if (c == 0)
{
dp[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (dp[i][j] == '*'&&ms[i][j]!='1')
{
Disapper(ms, dp, i, j, c);
}
}
}
}
else
{
dp[x][y] = c + '0';
}
}
}
void Findmine(char ms[ROWS][COLS], char dp[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win<ROW*COL- set_count)
{
printf("请输入你要排查雷的坐标\n");
scanf_s("%d %d", &x, &y);
system("cls");
int count = 0;
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
if (ms[x][y] == '1')
{
count++;
if (count == 1 && dp[x][y] == '*' && ms[x][y] == '1')
{
ms[x][y] = '0';
int a = rand() % ROW + 1;
int b = rand() % COL + 1;
if ((a != x || b != y) && ms[a][b] == '0')
{
ms[a][b] = '1';
DisPlay(dp, ROW, COL);
continue;
}
}
printf("很遗憾,你被炸死了");
DisPlay(ms,dp, ROW, COL);
break;
}
else
{
Disapper(ms,dp, x, y);
//递归
DisPlay(dp, ROW, COL);
win++;
}
}
else
{
printf("输入的坐标非法,请重新输入坐标\n");
}
}
if (win == ROW*COL - set_count)
{
printf("恭喜你,排雷成功");
DisPlay(ms, ROW, COL);
}
}
test.c中存放的是我们游戏的大概流程:
#include"game.h"
void menu()
{
printf("****************\n");
printf("**** 1.play ****\n");
printf("**** 0.exit ****\n");
printf("****************\n");
}
void game()
{
char ms[ROWS][COLS] = { 0 };
char dp[ROWS][COLS] = { 0 };
InitBoard(ms, ROWS, COLS, '0');
InitBoard(dp, ROWS, COLS, '*');
DisPlay(dp, ROW, COL);
//放雷
Set_mine(ms, ROW, COL);
//DisPlay(ms, ROW, COL);
//排查雷
Findmine(ms, dp, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input;
do
{
menu();
printf("请输入你的选择:>\n");
scanf_s("%d", &input);
system("cls");
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
二、递归展开
代码如下:
void Disapper(char ms[ROWS][COLS], char dp[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int c = GetMine(ms, x, y);
if (ms[x][y] == '0')
{
if (c == 0)
{
dp[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (dp[i][j] == '*'&&ms[i][j]!='1')
{
Disapper(ms, dp, i, j, c);
}
}
}
}
else
{
dp[x][y] = c + '0';
}
}
}
先看扫雷游戏中的展开:
当我点击一个位置时,这个位置左右直接爆炸性的展开,这是为什么呢?
由图我们可以发现,当我们点开一个位置时,这个位置周围没有雷,这些位置周围也没有雷的时候,就会自动展开。进而再去对它周围雷重复进行这样的操作。
所以我们由此可以知道这个位置展开的首要条件就是:
1、第一个位置不是雷。
2、这个位置周围没有雷。
既然它周围没有雷的时候才会展开,那么反过来说,当它周围有雷的时候这个位置就不会展开咯。
所以当排查到这个位置左右有雷的时候,我们就不会将其展开,而是显示它周围雷的个数。
我们用空格字符来代替展开,当它符合展开条件时,我们就会将这个位置原来的字符替换成空格字符,以表示它已被展开。
运用递归方式做的时候,我们都知道是要给递归一个限制条件的,不然就会成为死循环。那么这里的限制条件是什么呢?
1、这个位置不是空格字符(防止排查其它位置时,重复排查到这个位置,进入死循环)
2、这个地方不是雷
展示:
当我们输入坐标9 1时,可以看到,已经有了爆炸性展开的效果。
三、第一个位置不是雷
在游戏的过程中,第一次输入坐标,一不小心就会第一发就中雷,所以我们编写这一串代码,来防止我们第一次踩雷。
具体内容就是:假如我第一次踩到了雷,那我们就将这个雷换了,换成不是雷,把这颗雷换到另外的地方,并且,这个地方也不能与其它雷的位置重合。
代码如下:
if (count == 1 && dp[x][y] == '*' && ms[x][y] == '1')
{
ms[x][y] = '0';
int a = rand() % ROW + 1;
int b = rand() % COL + 1;
if ((a != x || b != y) && ms[a][b] == '0')
{
ms[a][b] = '1';
DisPlay(dp, ROW, COL);
continue;
}
}
这个代码要放在Findmine函数里,具体位置看我的放在game.c文件中的Findmine()函数。
展示:
我们先将带雷的棋盘打印出来 方便大家理解
可以看出第一行第三列的位置有一个雷,所以第一次我们输入坐标1 3来查看它是否会将这个雷移除
可以看出,原先的雷已被移走。