这款游戏相信大家并不陌生,关于它的玩法,在这里就不赘述了,重点来介绍一下如何用C语言完成这个项目。我们先来思考一下,扫雷的棋盘肯定是通过二维数组来实现的,在这个二维数组中既要埋雷,又要扫雷,还得给玩家展示,实在是容易混淆,这里我们不妨通过两个二维数组来实现,一个用来埋雷,另一个用来扫雷并且这个棋盘可以向玩家展示,降低了难度。
首先,我们来屡一下这个游戏的思路。
-
埋雷;这里可以设置难度扫10个雷或者20个雷,相应的雷数多的话棋盘也应该设置大一些,这样才好玩。
但这里我设置的棋盘大小都是一样的,只是增加雷数来改变难度。 -
扫雷 ;通过用户输入坐标来确定要排雷的位置,可以在棋盘周围加上行号和列号,便于玩家确定坐标。这部分可以实现以下三个功能:
①.第一次玩家输入有雷的坐标,把雷挪开,保证第一次不死,增强可玩性;
②.玩家输入的坐标周围没有雷,就利用递归把周围没有雷的地方都展开并且最外圈显示的雷数;
③.玩家觉得哪个位置有雷,可以实现用“ * ”标记雷,便于观察。
有一个问题:在统计某个坐标周围的雷数时,若坐标位于边角处,肯定会出现越界访问。为了避免这种情况,我们可以在原棋盘的基础上多创建一圈。(如下图)
- 检查是否胜利;我的判断方法是玩家无论是否标记雷,只要没展开的格子数和标记的雷数相加等于总的雷数时,玩家获胜。
然后在屡清了思路后,可以开始敲代码了,为了方便起见,我们创建3 个文件来写这个项目:测试文件、游戏文件和头文件。(如下图)
先来写 test.c 文件,写出游戏的逻辑以及需要用到的函数,在 game.c 里来实现具体函数的内容。
(ROW和COL为9,分别代表行和列,在 game.h 中有定义)
#include "game.h"
void menu()
{
printf("*******************************************\n");
printf("* 欢迎来到扫雷小游戏(*^▽^*) *\n");
printf("* 1.play *\n");
printf("* 0.exit *\n");
printf("*******************************************\n");
}
void game()
{
int count = 0;
char mine[ROWS][COLS]={
0};//创建一个埋雷的数组
char show[ROWS][COLS]={
0};//创建一个可以向玩家展示的数组
Init(mine,ROWS,COLS,'0');//初始化埋雷的数组,
Init(show,ROWS,COLS,'\03');//初始化展示的数组,在这里我用的是爱心,也可以自己定义字符
do
{
printf("请选择难度:1.EASY(10个雷) 2.HARD(20个雷)\n");
scanf("%d",&count);
switch(count)
{
case 1:
SetMine(mine,ROW,COL,EASY);//埋雷
//Print(mine,ROW,COL);
Print(show,ROW,COL);//打印棋盘
Safe(mine,show,ROW,COL);//第一次不炸
FindMine(mine,show,ROW,COL,EASY);//排雷
break;
case 2 :
SetMine(mine,ROW,COL,HARD);//埋雷
//Print(mine,ROW,COL);
Print(show,ROW,COL);//打印棋盘
Safe(mine,show,ROW,COL);//第一次不炸
FindMine(mine,show,ROW,COL,HARD);//排雷
break;
default:
printf("选择错误,请重新选择:>\n");
break;
}
}while((count!=1)&&(count!=2));
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));//
do
{
menu();//打印菜单
printf("请选择:>\n");
scanf("%d",&input);
switch(input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
default:
printf("请重新输入\n");
break;
}
}while(input);
}
int main()
{
test();
}
一、初始化棋盘
这里介绍一个C语言的库函数 memset 函数,void * memset(void *s, int value, size_t num);
memset 是内存设置函数,它的功能是将s中当前位置后面的num个字节 (typedef unsigned int size_t )用 value 替换并返回 s 。它的第一个参数为要替换的地址,第二个参数是你想要替换成的字符,第三个参数为替换的字节数。
一个小小的 提醒 提 醒 :可以将数组初始化成全 0,但不能将数组替换成全1,因为 memset 函数是按字节进行替换的。
void Init(char board[ROWS][COLS], int rows, int cols, char str)//初始化棋盘
{
memset(board, str,sizeof(board[0][0])*rows*cols);
}
二、输出棋盘
打印棋盘,顺便打印上行列号,便于观察。
void Print(char board[ROWS][COLS], int row, int col)//输出棋盘
{
int i = 0;
int j = 0;
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");
}
三、埋雷
利用随机数生成雷的坐标,每埋一个雷,雷数减一。rand()
函数虽然是生成随机数的函数,但它并不是实际意义的随机数,每次编译,出现的随机数都是一样的。为了让雷的埋更随机些,需要在 test.c 中的 test 函数中加入srand((unsigned int)time(NULL));
来实现真正意义的随机。
void SetMine(char board[ROWS][COLS], int row, int col, int count)//埋雷
{
int x = 0;
int y = 0;
while(count)
{
x = rand()%9+1;//1-9
y = rand()%9+1;//1-9
if(board[x][y]=='0')
{
board[x][y]='1';
count--;
}
}
}
四、计算某个坐标周围的雷数
通过传递来的一个坐标,来计算周围一圈8个格子中的雷数,由于雷和非雷是字符型的 ‘ 1 ’ 、 ‘ 0 ’,我们要的是数字 1、0,因此需要将字符转换为数字。字符 ‘ 0 ’比数字 0 的 ASCII 码值大48,可以把8个格子的雷数加起来减去 8*48 ,或者可以直接减去 8 *‘ 0 ’也是一样的。
static int CountMine(char board[ROWS][COLS], int x, int y)
{
return (board[x-1][y]+board[x-1][y+1]+
board[x][y+1]+board[x+1][y+1]+
board[x+1][y]+board[x+1][y-1]+
board[x][y-1]+board[x-1][y-1])-8*'0';
//值为数字
}
五、展开函数
通过递归函数实现展开,并且调用计算某个坐标周围雷数的函数来输出雷数。
static void OpenMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row ,int col, int x,int y)
{
int ret = 0;
ret = CountMine(mine, x, y);//计算x,y周围的雷数
if(ret == 0)
{
show[x][y]=' ';//若周围没有雷,输出空格
if(x-1>0 && y>0 && show[x-1][y]=='\03')
OpenMine(mine, show, row, col, x-1, y);
if(x-1>0 && y+1<=col && show[x-1][y+1]=='\03')
OpenMine(mine, show, row, col, x-1, y+1);
if(x>0 && y+1<=col && show[x][y+1]=='\03')
OpenMine(mine, show, row, col, x, y+1);
if(x+1<=row && y+1<=col && show[x+1][y+1]=='\03')
OpenMine(mine, show, row, col, x+1, y+1);
if(x+1<=row && y>0 && show[x+1][y]=='\03')
OpenMine(mine, show, row, col, x+1, y);
if(x+1<=row && y-1>0 && show[x+1][y-1]=='\03')
OpenMine(mine, show, row, col, x+1, y-1);
if(x>0 && y-1>0 && show[x][y-1]=='\03')
OpenMine(mine, show, row, col, x, y-1);
if(x-1>0 && y-1>0 && show[x-1][y-1]=='\03')
OpenMine(mine, show, row, col, x-1, y-1);
}
else
{
show[x][y] = CountMine(mine, x, y)+'0';//若坐标周围有雷,显示雷数
}
}
六、确保第一次不死
当第一次玩家踩到雷时,悄悄地把雷挪走,保证玩家第一次不被炸死,增强可玩性。
void Safe(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = 0;
int ret = 1;
int a = 0;
int b = 0;
printf("请输入要排查位置的坐标:>");
scanf("%d%d",&x,&y);
a = x;
b = y;//保存一下玩家输入的坐标值
if(mine[x][y]=='1')
{
mine[x][y]='0';//把雷变成非雷
count = CountMine(mine, x,y);
show[x][y] = count+'0';
while(ret)//重新设一个雷
{
x = rand()%9+1;
y = rand()%9+1;
if(mine[x][y]=='0')
{
mine[x][y] = '1';
}
ret--;
}
}
OpenMine(mine, show, row, col, a, b);//展开玩家输入坐标的周围
//Print(mine,ROW,COL);
Print(show,ROW,COL);
}
七、检查是否胜利
我的判断方法是玩家无论是否标记雷,只要没展开的格子数和标记的雷数相加等于总的雷数时,玩家就获胜了。
static int CheckWin(char show[ROWS][COLS],int row,int col)
{
int i = 0;
int j = 0;
int c = 0;
for(i=1; i<=row; i++)
{
for(j=1; j<=col; j++)
{
if((show[i][j] == '*') || (show[i][j] == '\03'))
c++;
}
}
return c;
}
八、标记雷
这里我写了标记雷的函数,也可以不写,只是为了方便观察,当玩家已经断定某个位置有雷的时候,可以进行标记。(用 “ * ” 标记雷)
static void SignMine(char show[ROWS][COLS], int row, int col, const int count)
{
int input = 0;
int a = 0;
int b = 0;
do
{
printf("是否需要标记雷: 1.是 0.否\n");
scanf("%d", &input);
switch(input)
{
case 1:
printf("请输入要标记的坐标:>");
scanf("%d%d", &a, &b);
if(show[a][b]=='\03')
{
show[a][b]='*';
Print(show,row,col);//标记后打印一下棋盘
}
else if(show[a][b]=='*')
{
printf("该坐标已经被标记\n");
}
break;
case 0:
break;
}
}while(input);
}
九、排雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col, const int count)//排雷
{
int x = 0;
int y = 0;
while(1)
{
printf("请输入要排查位置的坐标:>");
scanf("%d%d",&x,&y);
if(x>=1 && x<=row && y>=1 && y<=col)//检查输入的坐标是否合理
{
if(mine[x][y]=='1')
{
printf("很遗憾,你输了\n");
Print(mine,ROW,COL);
printf("\a\a\a");
break;
}
else
{
int c = CountMine(mine,x,y);
show[x][y]=c+'0';
OpenMine( mine, show,row,col,x,y);//展开函数
//Print(mine,ROW,COL);
Print(show,ROW,COL);//打印棋盘
if(CheckWin(show, row, col) == count)//检查是否胜利
{
break;
}
}
}
else
{
printf("坐标非法\n");
}
SignMine(show, row, col, count);//标记雷
}
if(CheckWin(show, row, col) == count)
{
printf("恭喜你,胜利了!\n");
Print(mine,ROW,COL);
}
}
将上述函数写全部在 game.c 中,源文件如下:
#include "game.h"
void Init(char board[ROWS][COLS], int rows, int cols, char str)//初始化棋盘
{
memset(board, str,sizeof(board[0][0])*rows*cols);
}
void Print(char board[ROWS][COLS], int row, int col)//输出棋盘
{
int i = 0;
int j = 0;
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 SetMine(char board[ROWS][COLS], int row, int col, int count)//埋雷
{
int x = 0;
int y = 0;
//1-9
while(count)
{
x = rand()%9+1;//%9-> 0-8
y = rand()%9+1;
if(board[x][y]=='0')
{
board[x][y]='1';
count--;
}
}
}
static int CountMine(char board[ROWS][COLS], int x, int y)//计算某个坐标周围的雷数
{
return (board[x-1][y]+board[x-1][y+1]+
board[x][y+1]+board[x+1][y+1]+
board[x+1][y]+board[x+1][y-1]+
board[x][y-1]+board[x-1][y-1]) -8*'0';//数字
}
static void OpenMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row ,int col, int x,int y)
{
int ret = 0;
ret = CountMine(mine, x, y);//计算x,y周围的雷数
if(ret == 0)
{
show[x][y]=' ';//若周围没有雷,输出空格
if(x-1>0 && y>0 && show[x-1][y]=='\03')
OpenMine(mine, show, row, col, x-1, y);
if(x-1>0 && y+1<=col && show[x-1][y+1]=='\03')
OpenMine(mine, show, row, col, x-1, y+1);
if(x>0 && y+1<=col && show[x][y+1]=='\03')
OpenMine(mine, show, row, col, x, y+1);
if(x+1<=row && y+1<=col && show[x+1][y+1]=='\03')
OpenMine(mine, show, row, col, x+1, y+1);
if(x+1<=row && y>0 && show[x+1][y]=='\03')
OpenMine(mine, show, row, col, x+1, y);
if(x+1<=row && y-1>0 && show[x+1][y-1]=='\03')
OpenMine(mine, show, row, col, x+1, y-1);
if(x>0 && y-1>0 && show[x][y-1]=='\03')
OpenMine(mine, show, row, col, x, y-1);
if(x-1>0 && y-1>0 && show[x-1][y-1]=='\03')
OpenMine(mine, show, row, col, x-1, y-1);
}
else
{
show[x][y] = CountMine(mine, x, y)+'0';//若坐标周围有雷,显示雷数
}
}
void Safe(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = 0;
int ret = 1;
int a = 0;
int b = 0;
printf("请输入要排查位置的坐标:>");
scanf("%d%d",&x,&y);
a = x;
b = y;//保存一下玩家输入的坐标值
if(mine[x][y]=='1')
{
mine[x][y]='0';//把雷变成非雷
count = CountMine(mine, x,y);
show[x][y] = count+'0';
while(ret)//重新设一个雷
{
x = rand()%9+1;
y = rand()%9+1;
if(mine[x][y]=='0')
{
mine[x][y] = '1';
}
ret--;
}
}
OpenMine(mine, show, row, col, a, b);//展开玩家输入坐标的周围
//Print(mine,ROW,COL);
Print(show,ROW,COL);
}
static int CheckWin(char show[ROWS][COLS],int row,int col)//检查有没有赢,计算所有未展开的数量
{
int i = 0;
int j = 0;
int c = 0;
for(i=1; i<=row; i++)
{
for(j=1; j<=col; j++)
{
if((show[i][j] == '*') || (show[i][j] == '\03'))
c++;
}
}
return c;
}
static void SignMine(char show[ROWS][COLS], int row, int col, const int count)
{
int input = 0;
int a = 0;
int b = 0;
do
{
printf("是否需要标记雷: 1.是 0.否\n");
scanf("%d", &input);
switch(input)
{
case 1:
printf("请输入要标记的坐标:>");
scanf("%d%d", &a, &b);
if(show[a][b]=='\03')
{
show[a][b]='*';
Print(show,row,col);//标记后打印一下棋盘
}
else if(show[a][b]=='*')
{
printf("该坐标已经被标记\n");
}
break;
case 0:
break;
}
}while(input);
}
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col, const int count)//排雷
{
int x = 0;
int y = 0;
while(1)
{
printf("请输入要排查位置的坐标:>");
scanf("%d%d",&x,&y);
if(x>=1 && x<=row && y>=1 && y<=col)//检查输入的坐标是否合理
{
if(mine[x][y]=='1')
{
printf("很遗憾,你输了\n");
Print(mine,ROW,COL);
printf("\a\a\a");
break;
}
else
{
int c = CountMine(mine,x,y);
show[x][y]=c+'0';
OpenMine( mine, show,row,col,x,y);//展开函数
//Print(mine,ROW,COL);
Print(show,ROW,COL);//打印棋盘
if(CheckWin(show, row, col) == count)//检查是否胜利
{
break;
}
}
}
else
{
printf("坐标非法\n");
}
SignMine(show, row, col, count);//标记雷
}
if(CheckWin(show, row, col) == count)
{
printf("恭喜你,胜利了!\n");
Print(mine,ROW,COL);
}
}
再然后把用到的头文件、函数以及宏定义全部写到 game.h 中,源文件如下:
#ifndef _GAME_H__
#define _GAME_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY 10
#define HARD 20
void Init(char board[ROWS][COLS], int rows, int cols, char str);//初始化棋盘
void Print(char board[ROWS][COLS], int row, int col);//打印棋盘
void SetMine(char board[ROWS][COLS], int row, int col, int count);//埋雷
void FindMine(char board[ROWS][COLS],char show[ROWS][COLS], int row, int col,const int count);//排雷
void Safe(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);//第一次不炸
#endif //_GAME_H__
最后测试程序,这一步可以先将埋雷的棋盘打印出来,便于测试,测试完无误后,记得将埋雷的棋盘屏蔽掉,不要让玩家看到我们埋雷的棋盘哦~
END
今天的文章小游戏——扫雷(可以标记)分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/24952.html