扫雷游戏----C语言必写游戏之一
扫雷游戏
背景
扫雷在科技历史上也扮演了相似的角色。这个基于数字的逻辑谜题最早来自20世纪六七十年代,当时Jerimac Ratliff推出的名为“Cube”的游戏已经非常受人欢迎。几十年后的1992年,扫雷游戏被加入了Windows3.1,这并不是为了展示Windows是游戏操作系统专家,而是为了训练用户的鼠标左右键操作能力,让这些动作变得非常自然,并培养鼠标移动的速度和准确性。
该介绍来自百度百科
起源与玩法
起源:
扫雷最原始的版本可以追溯到1973年一款名为“方块”的游戏。
不久,“方块”被改写成了游戏“Rlogic”。在“Rlogic”里,玩家的任务是作为美国海军陆战队队员,为指挥中心探出一条没有地雷的安全路线,如果路全被地雷堵死就算输。两年后,汤姆·安德森在“Rlogic”的基础上又编写出了游戏“地雷”,由此奠定了现代扫雷游戏的雏形。
1981年,微软公司的罗伯特·杜尔和卡特·约翰逊两位工程师在Windows3.1系统上加载了该游戏,扫雷游戏才正式在全世界推广开来。
玩法:
这款游戏的玩法是在一个9*9(初级),16*16(中级),16*30(高级),或自定义大小的方块矩阵中随机布置一定量的地雷(初级为10个,中级为40个,高级为99个)。由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束
该介绍来自百度百科
比如上面这个扫雷盘,当点击那一个方块时,弹出来数字1,即表示在那个方块周围有一个雷,也就是上面标出的黑色矩形所在位置上有一个雷,只要排查出所有的雷,那么就取得了胜利
基本实现思路
涉及语言的主要内容
1.if、switch、for循环、while循环、do...while循环
2.宏定义、break的用法等
3.二维数组的创建、初始化、数组传参等
4.函数的声明、定义、调用等
5.布置雷需要生成随机数、rand()函数的使用等
具体实现步骤
1.基本框架
基本框架十分重要,如果连框架都搭建不起来,那么就更不用去谈论写一个完整的扫雷小游戏代码了。首先我们需要考虑大致实现的功能。
1.程序一运行就弹出菜单页面
2.可以对菜单上的内容进行选择
3.如果不小心选择失误,要能够实现再次选择
因此我们写出如下代码:
int main(void)
{
int input = 0;
do
{
//打印菜单
Meau();
printf("请选择菜单:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("退出游戏!\n");
break;
case 2:
//进入游戏
Game();
break;
case 3:
//游戏玩法介绍
PlayWay();
break;
default:
printf("选择有误,请重新输入\n");
break;
}
} while (input != 1);
return 0;
}
这样,我们的基本框架就已经搭建好了~
2.菜单页面的实现
在菜单的页面需要认真排版,因为菜单就像一家店的门面一样,如果排版不好,那么兴趣也就提不上来。
void Meau()
{
printf("********************************\n");
printf("********** 1.退出游戏 *********\n");
printf("********** 2.开始游戏 *********\n");
printf("********** 3.游戏玩法 *********\n");
printf("********************************\n");
}
3.初始化棋盘
经过上面的步骤我们就要准备开始游戏了。现在游戏里还是一片空白,因此我们要有一个棋盘。对于棋盘,我们要实现的是,创建出一个空棋盘,然后再放置雷。
棋盘是一个二维的东西,因此我们采用二维数组。
为了增加用户的体验效果,我们还需要能够对棋盘的大小进行修改,此处采用宏定义的方式。为什么呢?
宏定义有两个优点:
(1) 方便程序的修改 使用简单宏定义可用宏代替一个在程序中经常使用的常量,这样在将该常量改变时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时,我们可以用较短的有意义的标识符来写程序,这样更方便一些。
(2) 提高程序的运行效率。
但是这时我们就会发现:我们很难确定边界的坐标周围有多少个‘雷’,因此我们实际的棋盘大小要更大一些,但呈现在玩家面前的只有中间那一块的棋盘。否则实际上会出现数组越界。
在实现思路里我说过我才用分块去写,因此声明和定义存放在头文件中
#pragma once
//行
# define ROW 9
//列
# define COL 9
# define ROWS ROW + 2
# define COLS COL + 2
这里需要创建两个二维数组,一个用来存放雷的信息,另一个用来存放排查出雷的信息
char mine[ROWS][COLS] = { 0 }; //存放布置好的雷的信息
char show[ROWS][COLS] = { 0 }; //存放排查出的雷的信息
这个时候我们要让创建的棋盘内什么东西都不存放,或者存放一致的东西,方便以后在棋盘中布置雷,因此我们要将棋盘初始化。
//初始化棋盘
void Init_Board(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 < cols; ++j)
{
board[i][j] = set; //将棋盘全部初始化为set这个传过来的参数
}
}
}
此处最后一个参数是方便自己来初始化棋盘。
4.在屏幕中显示棋盘
根据上面的初始化棋盘,我们知道呈现在玩家面前的只有中间那一块的棋盘。也就是只需要打印中间绿色的那一块棋盘。
虽然我们只是操作绿色部分9*9的那一块,但我们在传参的时候依旧传过去的是11*11的棋盘。因此在调用时打印棋盘函数时需要注意参数!
//调用
DisplayBoard(mine, ROW, COL); //实参
//定义
void DisplayBoard(char board[ROWS][COLS], int row, int col)//形参
{
}
为了方便用户游戏,我们还需要将棋盘的列和行信息打印出来。于是得到下面的函数。
//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
//实现对棋盘列数的信息提示
for (i = 0; i <= 9; ++i)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; ++i)
{
printf("%d ", i); //实现对棋盘行数的信息提示
for (j = 1; j <= col; ++j) //因为打印的是中间的棋盘,所以下标从1开始
{
printf("%c ", board[i][j]);
}
printf("\n"); //换行
}
printf("-------------------\n");//美化界面
}
5.布置好棋盘中的雷
初始化棋盘之后,棋盘中并没有雷,因此我们要开始在棋盘中布置雷。EASY_COUNT(雷的个数)在头文件中进行了宏定义。布置雷要涉及到随机数的生成
。要使得电脑随机布置雷,这样才能够更好地实现扫雷游戏。在这里我们把字符‘1’当作雷。
//在棋盘中放置雷
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--; //布置了雷才减少一次,因此要放在if里面
}
}
}
6.排查出棋盘中的雷
这里涉及到函数的嵌套调用,请仔细看,不然很容易被绕晕。
//统计查找时方块周围雷的个数
int GetMineCount(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 - EASY_COUNT)
{
printf("请输入要排查的坐标:");
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");
}
}
}
扫雷游戏完整代码
头文件:
# 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, char set);
//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//在棋盘中放置雷
void SetMine(char board[ROWS][COLS], int row, int col);
//排查棋盘中的雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c文件:
# include "game.h"
void Meau()
{
printf("********************************\n");
printf("********** 1.退出游戏 *********\n");
printf("********** 2.开始游戏 *********\n");
printf("********** 3.游戏玩法 *********\n");
printf("********************************\n");
}
void Game()
{
char mine[ROWS][COLS] = { 0 }; //存放布置好的雷的信息
char show[ROWS][COLS] = { 0 }; //存放排查出的雷的信息
//初始化扫雷盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//在屏幕上显示棋盘
DisplayBoard(show, ROW, COL);
//布置雷
SetMine(mine, ROW, COL);
//排查出棋盘中的雷
FindMine(mine, show, ROW, COL);
}
int main(void)
{
int input = 0;
srand((unsigned)time(NULL)); //设置生成起点
do
{
//打印菜单
Meau();
printf("请选择菜单:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("退出游戏!\n");
break;
case 2:
//进入游戏
Game();
break;
case 3:
//游戏玩法介绍
PlayWay();
break;
default:
printf("选择有误,请重新输入\n");
break;
}
} while (input != 1);
return 0;
}
test.c文件:
# 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 < cols; ++j)
{
board[i][j] = set; //将棋盘全部初始化为set这个传过来的参数
}
}
}
//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
//实现对棋盘列数的信息提示
for (i = 0; i <= 9; ++i)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; ++i)
{
printf("%d ", i); //实现对棋盘行数的信息提示
for (j = 1; j <= col; ++j) //因为打印的是中间的棋盘,所以下标从1开始
{
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--; //布置了雷才减少一次,因此要放在if里面
}
}
}
//统计查找时方块周围雷的个数
int GetMineCount(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 - EASY_COUNT)
{
printf("请输入要排查的坐标:");
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");
}
}
}
最后运行结果
这里为了方便演示结果,我在棋盘中放置了40个雷,同志们修改雷的个数请随意就好。
这是选择开始游戏的运行结果:
这里选择选项3的运行结果:
总结
虽然是一个很简单的游戏,但是却能够很巧妙地包含了C语言所涉及的内容。所有想要步入编程之路的小伙伴们,都值得一试!