一.扫雷的游戏规则
扫雷是一款单人益智游戏,玩家需要通过翻开方块来避免触雷并揭示所有的非雷方块。以下是扫雷的基本规则:
-
游戏开始时,玩家面对一个由方块组成的方形网格,每个方块都可以是以下三种状态之一:
- 未揭示的方块:初始状态下,所有方块都是未揭示的。
- 揭示的方块:当玩家翻开一个方块时,会显示方块中的内容。
- 标记的方块:玩家可以将一个未揭示的方块标记为潜在的雷区,以帮助记忆。
-
方块的内容可以是以下之一:
- 雷:一些方块中隐藏着雷。如果玩家翻开了一个雷方块,游戏失败。
- 数字:在非雷方块中,数字表示该方块周围八个相邻方块中的雷数。
- 空白:如果一个方块周围没有雷,那么它将显示为空白方块,并自动揭示周围的相邻方块。
-
玩家的目标是揭示所有非雷方块,而不触雷。玩家可以通过以下操作来达到目标:
- 翻开方块:玩家选择一个未揭示的方块翻开。如果方块是雷,游戏失败。如果方块是数字,玩家可以根据数字推断周围的雷的位置。
- 标记方块:玩家可以将未揭示的方块标记为潜在的雷区,以帮助记忆。标记的方块不会被翻开,但可以在后续的操作中取消标记。
-
如果玩家成功地揭示了所有非雷方块,游戏胜利。
接下来所讲述的,是简易版的扫雷,仅包含基础。
二.分文件编写
test.h--用来声明所使用函数的声明,game.c--包含所有函数的实现,test.c--是整个主体。
三.扫雷内容的具体实现
我们接下来要实现的是简易版的扫雷游戏,功能有以下说明:
1.创建菜单
用menu函数来完成游戏的界面,接着使用do...while循环语句对menu函数进行使用,switch语句来完成菜单的选择。
void menu(void)
{
printf("******************\n");
printf("****0.退出游戏****\n");
printf("****1.开始游戏****\n");
printf("******************\n");
}
#include"test.h"
int main()
{
int i = 0;
do
{
menu();
if (scanf("%d", &i) != EOF)
{
switch (i)
{
case 1:
game();
break;
case 0:
printf("已退出游戏。\n");
break;
default:
printf("输入错误,请重新输入。\n");
}
}
} while (i);
return 0;
}
接下来要讲述的是游戏中的较难点,并且是整个游戏的核心,希望大家认真阅读。
2.创建棋盘
由于会对行列多次使用,所以需要宏定义。
#define size 9
#define SIZE 11
char board[SIZE][SIZE];
char revealed[SIZE][SIZE];
initializeBoard(board,revealed);
首先创建两个数组,并初始化。创造一个initializeBoard函数,利用for循环语句将board数组全转化为'0',将revealed数组全转化'*'。
void initializeBoard(char board[SIZE][SIZE], char revealed[SIZE][SIZE])
{
for ( int i = 0; i < SIZE; i++)
{
for ( int j = 0; j <SIZE; j++)
{
board[i][j] = '0';
}
}
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
revealed[i][j] = '*';
}
}
}
你可能会疑惑,为什么是棋盘大小是11*11,而不是9*9?这个问题会在计算雷数的步骤上为你解答。
3.打印棋盘
玩家需要看到游戏的进行,若看不到游戏的界面,就如丈二和尚摸不着头脑,不知道该怎么玩。所以接下来要打印棋盘,指引玩家方向。
创建printrevealed函数来实现对键盘的打印。利用for循环语句将revealed数组部分打印。
void printrevealed(char revealed[SIZE][SIZE])
{
for (int i = 0; i <=size; i++)
{
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <=size; i++)
{
printf("%d ", i);
for (int j = 1; j <=size; j++)
{
printf("%c ", revealed[i][j]);
}
printf("\n");
}
}
想必,你一定会困惑为什么部分打印呢?这个答案在后面计算周围雷数的步骤会告诉你。希望你能继续耐心的看下去。
4.放置雷
对行和列进行了宏定义,我们也可以对炸弹个数进行宏定义。
#define bombs 10
创建placebombs函数来实现放置雷的功能。首先在board数组中使用随机数生成雷的位置坐标。接下来防止重复设置,用if判断位置是否已为雷后设置。最后雷数计数器递减来控制随机设置雷的总数。
char board[SIZE][SIZE];
char revealed[SIZE][SIZE];
srand((unsigned int)time(NULL));
initializeBoard(board,revealed);
printrevealed(revealed);
placebombs(board);
void placebombs(char board[SIZE][SIZE])
{
int x,y;
int j = bombs;
while(j)
{
x = rand() % size + 1;
y = rand() % size + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
j--;
}
}
}
5.排查雷
输入坐标,然后进行if语句来判断,符合条件的会展开对应的单元格。
创建revealcell函数根据输入展开对应的单元格和连锁反应区域。输出邻居雷数或继续展开邻居为0的单元格。设置revealed数组对应位置为数字字符,展示给玩家。使用递归可以一次性展开一片区域。
while (!gameover)
{
printf("请输入坐标\n");
if (scanf("%d%d", &row, &col) != EOF)
{
if (row<0 || row>size || col < 0 || col>10)
{
printf("输入错误,请重新输入\n");
continue;
}
if (board[row][col] == '1')
{
printf("你碰到雷了,游戏结束\n");
printrevealed(board);
gameover = 1;
}
else
{
if (revealed[row][col] != '*')
{
printf("已探查此位,请重新输入\n");
continue;
}
revealcell(board, revealed, row, col);
printrevealed(revealed);
if (isgameover(board, revealed))
{
printf("恭喜你闯关成功\n");
gameover = 1;
}
}
}
}
void revealcell(char board[SIZE][SIZE], char revealed[SIZE][SIZE], int row, int col)
{
if (revealed[row][col] == '*')
{
revealed[row][col]= '0'+countAdjacentBombs(board, row, col);
if (revealed[row][col] == '0')
{
for (int i = row - 1; i <= row + 1; i++)
{
for (int j = col - 1; j <= col + 1; j++)
{
if (i > 0 && i <= size && j > 0 && j <= size)
revealcell(board, revealed, i, j);
}
}
}
}
}
6.计算雷数
对于为何是11*11以及为何是部分打印,我均一一回答。
首先为什么是11*11的棋盘?在计算雷数的时候,特别是在地图的边界位置,会出现越界的问题。为了防⽌越界,在设计的时候,给数组扩⼤⼀圈,雷还是布置在中间的9*9的坐标上,周围⼀圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11。
为什么是部分打印?很简单,因为玩家玩的是中间9*9的棋盘,所以需要给玩家呈现出9*9的棋盘,而不是11*11的棋盘。
在第五步当中涉及到计算雷数的函数countAdjacentBombs会计算指定位置的8个相邻单元格中有几个是雷。使用双重for循环遍历8个方向,来计数满足条件的雷位置。
int countAdjacentBombs(char board[SIZE][SIZE], int row, int col)
{
int count = 0;
for (int i = row - 1; i <= row + 1; i++)
{
for (int j = col - 1; j <= col + 1; j++)
{
if (i >= 0 && i < SIZE && j >= 0 && j < SIZE && board[i][j] == '1')
{
count++;
}
}
}
return count;
}
7.游戏结束判断
结束游戏有两种方式1.是碰到了雷,你失败了。2.你把除10个雷之外的方格全探查了,你胜利了。第一种方式代码已在上面呈现。
检查isgameover函数检查是否满足结束条件,如全部展开非雷区域成功。使用双重for循环遍历检查每一个单元格状态。
int isgameover(char board[SIZE][SIZE], char revealed[SIZE][SIZE])
{
for (int i = 1; i <= size; i++)
{
for (int j = 1; j <= size; j++)
{
if (board[i][j] != '1' && revealed[i][j] == '*')
{
return 0;
}
}
}
return 1;
}
8.game函数的实现
创建game函数调用以上各函数组成游戏主循环体。获取玩家输入、更新游戏数据、判断输赢、展开格子、打印输出。循环直到结束,完成一个完整的游戏流程。
void game(void)
{
char board[SIZE][SIZE];
char revealed[SIZE][SIZE];
int row = size;
int col = size;
int gameover = 0;
srand((unsigned int)time(NULL));
initializeBoard(board,revealed);
printrevealed(revealed);
placebombs(board);
while (!gameover)
{
printf("请输入坐标\n");
if (scanf("%d%d", &row, &col) != EOF)
{
if (row<0 || row>size || col < 0 || col>10)
{
printf("输入错误,请重新输入\n");
continue;
}
if (board[row][col] == '1')
{
printf("你碰到雷了,游戏结束\n");
printrevealed(board);
gameover = 1;
}
else
{
if (revealed[row][col] != '*')
{
printf("已探查此位,请重新输入\n");
continue;
}
revealcell(board, revealed, row, col);
printrevealed(revealed);
if (isgameover(board, revealed))
{
printf("恭喜你闯关成功\n");
gameover = 1;
}
}
}
}
}
以上所包含函数均需要在test.h文件声明
四.扫雷代码的实际实现
游戏界面供选择。
输入1,开始游戏。
输入坐标,碰到雷的呈现。
输入坐标,未碰到雷的呈现。
输入超出范围错误的坐标。
输入已探查过的坐标。
闯关成功。
开始界面的不规范输入。
退出游戏。
五.全部源码
game.c文件
#include "test.h"
void menu(void)
{
printf("******************\n");
printf("****0.退出游戏****\n");
printf("****1.开始游戏****\n");
printf("******************\n");
}
void initializeBoard(char board[SIZE][SIZE], char revealed[SIZE][SIZE])
{
for ( int i = 0; i < SIZE; i++)
{
for ( int j = 0; j <SIZE; j++)
{
board[i][j] = '0';
}
}
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
revealed[i][j] = '*';
}
}
}
void printrevealed(char revealed[SIZE][SIZE])
{
for (int i = 0; i <=size; i++)
{
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <=size; i++)
{
printf("%d ", i);
for (int j = 1; j <=size; j++)
{
printf("%c ", revealed[i][j]);
}
printf("\n");
}
}
void placebombs(char board[SIZE][SIZE])
{
int x,y;
int j = bombs;
while(j)
{
x = rand() % size + 1;
y = rand() % size + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
j--;
}
}
}
int countAdjacentBombs(char board[SIZE][SIZE], int row, int col)
{
int count = 0;
for (int i = row - 1; i <= row + 1; i++)
{
for (int j = col - 1; j <= col + 1; j++)
{
if (i >= 0 && i < SIZE && j >= 0 && j < SIZE && board[i][j] == '1')
{
count++;
}
}
}
return count;
}
void revealcell(char board[SIZE][SIZE], char revealed[SIZE][SIZE], int row, int col)
{
if (revealed[row][col] == '*')
{
revealed[row][col]= '0'+countAdjacentBombs(board, row, col);
if (revealed[row][col] == '0')
{
for (int i = row - 1; i <= row + 1; i++)
{
for (int j = col - 1; j <= col + 1; j++)
{
if (i > 0 && i <= size && j > 0 && j <= size)
revealcell(board, revealed, i, j);
}
}
}
}
}
int isgameover(char board[SIZE][SIZE], char revealed[SIZE][SIZE])
{
for (int i = 1; i <= size; i++)
{
for (int j = 1; j <= size; j++)
{
if (board[i][j] != '1' && revealed[i][j] == '*')
{
return 0;
}
}
}
return 1;
}
void game(void)
{
char board[SIZE][SIZE];
char revealed[SIZE][SIZE];
int row = size;
int col = size;
int gameover = 0;
srand((unsigned int)time(NULL));
initializeBoard(board,revealed);
printrevealed(revealed);
placebombs(board);
while (!gameover)
{
printf("请输入坐标\n");
if (scanf("%d%d", &row, &col) != EOF)
{
if (row<0 || row>size || col < 0 || col>10)
{
printf("输入错误,请重新输入\n");
continue;
}
if (board[row][col] == '1')
{
printf("你碰到雷了,游戏结束\n");
printrevealed(board);
gameover = 1;
}
else
{
if (revealed[row][col] != '*')
{
printf("已探查此位,请重新输入\n");
continue;
}
revealcell(board, revealed, row, col);
printrevealed(revealed);
if (isgameover(board, revealed))
{
printf("恭喜你闯关成功\n");
gameover = 1;
}
}
}
}
}
test.c文件
#include"test.h"
int main()
{
int i = 0;
do
{
menu();
if (scanf("%d", &i) != EOF)
{
switch (i)
{
case 1:
game();
break;
case 0:
printf("已退出游戏。\n");
break;
default:
printf("输入错误,请重新输入。\n");
}
}
} while (i);
return 0;
}
test.h文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define size 9
#define SIZE 11
#define bombs 10
void menu();
void game();
void initializeBoard(char board[SIZE][SIZE],char revealed[SIZE][SIZE]);
void printrevealed(char revealed[SIZE][SIZE]);
void placebombs(char board[SIZE][SIZE]);
int countAdjacentBombs(char board[SIZE][SIZE], int row, int col);
void revealcell(char board[SIZE][SIZE], char reveal[SIZE][SIZE], int row, int col);
int isgameover(char board[SIZE][SIZE], char revealed[SIZE][SIZE]);
感谢你的耐心阅读,希望你的编程能力能够蒸蒸日上。