目录
C语言热门小游戏——扫雷的实现全过程。
一.游戏介绍
一个扫雷盘面由许多方格组成,方格中随机分布着一定数量的雷,一个格子中至多只有1雷。胜利条件是打开所有安全格(非雷格),失败条件是打开了一个雷格(踩雷)。
在我们玩游戏的过程中,每点一个格,会出现一个数字,那个数字代表的是周围八个格子里面雷的数量。
二.游戏实现
这个游戏的实现我们需要创建三个文件,一个头文件game.h,用来声明函数。两个源文件,work.c用来实现主流程;game.c用来具体实现函数。
分文件编写的好处:
- 功能责任划分
- 方便调试
- 主程序简洁
1.总流程
首先,我们需要创建一个目录来供我们选择开始游戏or退出游戏。
void menu() //创建一个menu()函数,用来显示菜单此函数无参数,也不需要有返回值
{
printf("************************\n");
printf("******** 1.play ********\n");
printf("******** 2.exit ********\n");
printf("************************\n");
}
在work.c源文件中,输入游戏的主要流程:
- 显示菜单;
- 选择是否开始游戏;
- 若选择1,运行game.c函数;
- 若选择2,则退出游戏;
- 若选择其他数字,则输入错误,需重新输入。
我们使用do...while循环来实现菜单的每次首先打印;之后用switch语句来实现菜单的选择。
int main()
{
int input = 0;
do
{
menu();
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 2:
printf("退出游戏");
break;
defaut:
printf("请重新输入");
break;
}
} while (input);
return 0;
}
2.初始化棋盘(InitBoard)
接下来开始完善game()函数。
首先第一步是初始化棋盘。我们需要创建一个棋盘,我们可以创建一个二维数组,通过对数组的打印从而得到棋盘。对于棋盘的长和宽,为了方便更改,我们可以创建两个变量ROW和COL来代替。我们暂且先将长和宽设置为9。
不过我们需要提前将变量在头文件(game.h)中声明,确保源文件中的文件能正常使用。
#define ROW 9
#define COL 9
//长和宽
我们在文件(work.c)中创建数组mine和show :
char mine[ROWS][COLS];
char show[ROWS][COLS];
//两个棋盘
由第一点游戏规则可知,数字代表雷的个数。为了方便最外围的数字的计算,我们一般把程序设置两个棋盘,一个是给玩家看的(show),它依旧是9*9的棋盘;一个是后台运行的(mine),一般将原棋盘最外层扩建一圈,这样每个数字的含义就都一样了(扩建的格里面均无雷)。
此时我们需要在头文件(game.h)中再定义一下展示棋盘(show)的长(ROWS)与宽(COLS)的数据。
#define ROWS ROW+2 //ROWS=ROW+2
#define COLS COL+2 //COLS=COL+2
//扩建后的长和宽
前期工作准备完后,开始创建初始化函数,我们取名为 InitBoard。这个函数需要初始化棋盘。
我们先将show棋盘里全部打印为 * ,点开格后*便变成周围雷的数字;先将mine棋盘里全部打印为 0 ,代表没有雷。
在文件(game.c)中创建此函数
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; } } }
在头文件(game.h)中声明初始化函数:
//初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
在文件(work.c)中调用此函数,进行使用:
InitBoard(mine, ROWS, COLS, '0'); InitBoard(show, ROWS, COLS, '*');
3.布置雷(SetMine)
初始化棋盘后,我们便需要进行埋雷的操作了。为实现这一功能,我们创建一个SetMine函数。
这个函数是在mine棋盘中进行操作,所以函数变量为(mine,row,col)。埋雷的本质就是将棋盘中的 0 变为 1 ,我们可以通过确定坐标来确定雷的位置。
在源文件(game.c)中创建此函数。
void SetMine(char board[ROWS][COLS], int row, int col) { int count = Mine; while (count) { int x = rand() % row + 1; //范围是1~9 int y = rand() % col + 1; if (board[x][y] == '0') //埋雷前确保此格无雷 { board[x][y] = '1'; //埋下雷 count--; } } }
由于用了随机数生成函数rand(),所以还要在头文件(game.h)中声明对应头文件,还需在主函数中设置变化的“种子”。(在之前文章中有介绍:【C语言基础】:猜数字游戏-CSDN博客)
在头文件(game.h)中声明相应函数:
#include<stdlib.h> //rand()头文件 #include<time.h> //时间戳头文件 //布置雷 void SetMine(char board[ROWS][COLS], int row, int col);
在源文件(work.c)中调用函数:
SetMine(mine, ROW, COL);
4.打印棋盘(DisplayBoard)
布置好雷的位置后,开始打印棋盘。
在源文件(game.c)中创建此函数。
void DisplayBoard(char board[ROWS][COLS], int row, int col) { printf("*******扫雷*******\n"); int i = 0; for (i = 0; i <= row; i++) //此循环用来打印横坐标 { printf("%d ", i); } printf("\n"); for (i = 1; i <= row; i++) { printf("%d ", i); //用来打印纵坐标 int j = 0; for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); //打印棋盘 } printf("\n"); } }
在头文件(game.h)中声明相应函数:
//打印棋盘 void DisplayBoard(char board[ROWS][COLS], int row, int col);
在源文件(work.c)中调用函数:
DisplayBoard(show, ROW, COL);
5.排查雷(FindMine)
排查雷的第一步是选择想要排查的坐标:
void FindMine(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 - Mine)
{
printf("请输入排查坐标:\n");
scanf("%d %d", &x, &y);
if (x >=1 && x <= ROW && y >=1 && y <= COL) //坐标输入的要求
{
system("cls"); //清屏,美观
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了!\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int word = GetMine(mine, x, y);
show[x][y] = word + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else
printf("非法输入,请重新输入:\n");
}
if (win == row * col - Mine) //当排雷次数=总格数-雷数就赢了
{
printf("恭喜你,排雷成功!");
DisplayBoard(mine, ROW, COL);
}
}
第二步是在选择好坐标后,若不是雷,则需对周围八格里的雷的数量进行相加,得到所选坐标对应数字。
在game.c文件中实现统计雷个数函数,因为在排查雷函数内部,不需在game.h文件中声明。因为坐标内输入的是字符‘0’,所以统计出‘1’的总个数再减去‘0’及为周围雷数
int GetMine(char mine[ROWS][COLS], int x, int y)
{
return (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1])-8*'0';
}
二.全部源码
1.头文件
1.1.game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
#define ROW 9
#define COL 9
#define Mine 10
#define ROWS ROW+2
#define COLS COL+2
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void SetMine(char board[ROWS][COLS], int row, int col);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);
2.源文件
2.1.work.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void menu()
{
printf("*********************\n");
printf("********1.play*******\n");
printf("********2.exit*******\n");
printf("*********************\n");
}
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
SetMine(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
FindMine(mine,show, ROW, COL);
}
int main()
{
int input = 0;
do
{
srand((unsigned int)time(NULL));
menu();
printf("请输入数字:\n");
scanf("%d", &input);
system("cls");
switch (input)
{
case 1:
game();
break;
case 2:
printf("游戏结束!\n");
break;
defaut:
printf("请重新输入:\n");
break;
}
} while (input);
return 0;
}
2.2.game.c
#define _CRT_SECURE_NO_WARNINGS
#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)
{
printf("*******扫雷*******\n");
int i = 0;
for (i = 0; i <= row; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
int j = 0;
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = Mine;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int GetMine(char mine[ROWS][COLS], int x, int y)
{
return (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1])-8*'0';
}
void FindMine(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 - Mine)
{
printf("请输入排查坐标:\n");
scanf("%d %d", &x, &y);
if (x >=1 && x <= ROW && y >=1 && y <= COL)
{
//system("cls");
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了!\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int word = GetMine(mine, x, y);
show[x][y] = word + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else
printf("非法输入,请重新输入:\n");
}
if (win == row * col - Mine)
{
printf("恭喜你,排雷成功!");
DisplayBoard(mine, ROW, COL);
}
}