目录
问题简述
用C语言实现扫雷游戏
一、大概思路
1.在我们开始设计扫雷面板前,要有一个菜单选择开始游戏或退出游戏,玩完一把后仍然有菜单选择。
2.我们可以打开网页版初阶扫雷面板,它是一个9*9的格子面板,这时我们能联想到二维数组。
3.因为扫雷是先有面板选择坐标,选择后再把含有排除雷的数量的面板打印出来,因此要创建两个二维数组,但需要考虑的是,如果在扫雷面板最边上排雷,排雷的坐标可能对于我们创建的数组已经越界,这是不可取的。因此我们还要有行与列各11的扫雷面板(如图所示)。并且将其中一个二维数组都放0,另一个都用‘ * ’号来代表没有排雷的区域。
4.随机设置雷的坐标,因为要随机则涉及到rand与srand函数,并且设置地雷的区域是此前没有设置地雷的区域,坐标不能重复,用‘1’代表雷,‘0’代表没有雷的区域。
5.设置好地雷后要进行排雷,如果在选择的坐标其边上5个的区域内有雷,则必须在选择的区域处将其边上5个区域的雷数显示出来,而且数字1与地雷‘1’要区分开来。
6.判断是否踩雷,如果全部雷都排出来,则判断成功,如果踩到了雷,则直接判断失败,如果既没有排完雷也没有踩雷,则游戏继续。
二、基本流程
1.打印游戏菜单
我们要求的是开始前选择是否开始游戏,游戏结束时不关闭页面重新进入菜单。
void menu()
{
printf("************************\n");
printf("*********1.play*********\n");
printf("*********0.exit*********\n");
printf("************************\n");
}
int main
{
do
{
menu();
printf("请选择:>\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("请重新选择\n");
break;
}
} while (input);
}
2.初始化扫雷面板
先建立两个不同的字符数组,再将其中一个数组都放入字符0,为了函数的可修改性,将数组的行与列在头文件中设置,再在源文件中包含头文件,函数实现前首先要在头文件中声明。
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
头文件处声明与定义:
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
void InitBoard(char board[ROWS][COLS], int rows, int cols);//初始化棋盘
3.打印设置好的扫雷面版
因为一开始我们并没有排雷,因此要将游戏面板上的所有坐标都先用‘ * ’号进行覆盖,选择后再把排出雷的数量或雷打印出来。为了玩家在输入坐标时方便,我们可以在扫雷面板的最上方与最左方将行数与列数标出来。
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf("---------------\n");
for (i = 0; i <= 9; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d ",i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("--------------\n");
}
头文件处进行声明
void DisplayBoard(char Board[ROWS][COLS], int rows, int cols);//打印棋盘
4.随机设置雷的坐标
根据扫雷游戏规则,在初阶扫雷面板中最多能设置10个雷,并且要随机与不重复,因此在主函数中我们设置种子,让电脑随机生成坐标放入地雷。
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;
}
}
}
头文件处进行声明
void SetMine(char board[ROW][COL], int row, int col);//设置地雷
5.进行排雷并判断输赢
当雷已经随机设置好后,我们要开始排雷。选择的坐标(不能输入非法坐标)在围绕其旁边的8个坐标内将雷的个数打印出来。因为一个扫雷面板只有10个雷,而总共有81个坐标,则还有71个坐标没有设置雷,当我们排的雷达到10个后则成功。最后无论成功与失败都要打印扫雷面板上所以雷的位置。
极其需要注意的点:因为我们创建的是字符类型的数组,因此我们在打印排出雷的数量的时候要加上字符0,因为数字个位数加上字符0都会变为字符数,这样才符合我们创建的数组类型。
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
printf("要排查地雷的坐标:\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("你被炸死了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count+'0';
DisplayBoard(show , ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
三、拓展衍生
测试发现代码效果没有问题后,我们可以拓展出扫雷面板更大、如何在第一次排雷时不踩雷、倒计时等问题,如果把这些问题解决对自己的代码逻辑会有很大的提升。
四、总代码展示
1.头文件代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
void InitBoard(char board[ROWS][COLS], int rows, int cols);//初始化棋盘
void DisplayBoard(char Board[ROWS][COLS], int rows, int cols);//打印棋盘
void SetMine(char board[ROW][COL], int row, int col);//设置地雷
void FineMine(char mine[ROW][COL], char show[ROW][COL],int row, int col);//找地雷
int GetMineCount(char mine[ROWS][COLS], int x, int y);
2.游戏实现代码
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf("---------------\n");
for (i = 0; i <= 9; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d ",i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("--------------\n");
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;
}
}
}
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x][y + 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x + 1][y - 1] +
mine[x - 1][y + 1]-8*'0';
}
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
printf("要排查地雷的坐标:\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("你被炸死了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count+'0';
DisplayBoard(show , ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
3.主函数代码
#include "game.h"
void menu()
{
printf("************************\n");
printf("*********1.play*********\n");
printf("*********0.exit*********\n");
printf("************************\n");
}
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS,'*');
DisplayBoard(show, ROW, COL);
SetMine(mine, ROW, COL);
FineMine(mine,show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned)time(NULL));
do
{
menu();
printf("请选择:>\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("请重新选择\n");
break;
}
} while (input);
return 0;
}