制作一个简易扫雷游戏(c语言萌新编写,或许写的很拙劣,还望大佬们见谅,如果出了什么错误或者问题可以在评论里发)
ps:文章最后有全部代码
1.从结果逆向构思需要的基本的代码结构
程序结果呈现
是否进行游戏页面(图1)
游戏进行页面(图2)
用*代表未知的地方,有的地方有雷有的地方没雷
所以初步想法就是:
这里要建两个二维数组,一个存放这个我们可以看到的页面(类似客户端),另一个存放我们看不到的放置雷,检查雷的页面(类似服务端)
游戏进行页面(图3)
游戏结束页面(失败)(图4)
初步思考扫雷游戏逻辑:
扫雷游戏,首先需要有游戏页面,其次需要产生雷,然后玩家输入坐标,程序判断该坐标有无雷,无雷则告诉周围一圈有几个雷,然后重复进行该过程直至踩雷或完成排雷
而检查雷的时候,因为需要检查周围一圈,所以这里就会出现一个问题,如果已经在最边上,周围一圈会溢出数组
这样的话,就只能让它不溢出,所以我们建数组时需要再扩大一圈建立,也就是行和列数+2,但在展示的页面不展示出多出的部分
2.一步一步思考各个部分的代码
(1)是否进行游戏初始页面
第一步也就是开始页面(图1)
很容易得到如下代码(绿色的是代码的注释,加粗的是一些自己的见解,倾斜的是代码内容)
int input = 0;
do
{
printf("**************************\n");
printf("**********1.play**********\n");
printf("**********0.exit**********\n");
printf("**************************\n");//菜单页面
printf("是否开始游戏\n");
printf("请输入(1/0):");
scanf("%d", &input);//选择进行游戏
switch (input)//是否进行游戏,进行分支情况。这里用switch进行分支情况,也可以用if,不管由于是否进行游戏是输入数字进行选择,用switch更方便好看
{
case 1:
game();//游戏程序
break;
case 0:
printf("退出游戏,游戏结束\n");
break;
default:
printf("输入错误,请重新输入\n");
}
}while(input)//同时因为游戏进行完或者输入错误,还可以再玩,这里用do while进行循环,因为输入0退出游戏,输入其他继续进行,所有这里可以直接用输入值(input)作为判断条件
也可以直接设一个函数来表示这个初始页面,使main函数主体更简单明了
如:
void menu()//初始页面
{
printf("**************************\n");
printf("**********1.play*********\n");
printf("**********0.exit**********\n");
printf("**************************\n");
printf("是否开始游戏\n");
printf("请输入(1/0):");
}
int main()
{
int input = 0;
do
{
menu();
scanf("%d", &input);//选择进行游戏
switch (input)
{
case 1:
game();//游戏程序
break;
case 0:
printf("退出游戏,游戏结束\n");
break;
default:
printf("输入错误,请重新输入\n");
}
}
return 0;
}
扫雷的初始页面和游戏是否进行的逻辑已经完成,接下来就是扫雷游戏程序的编写(即代码中game()函数的具体实现)
(2)扫雷游戏初始页面
从我们想要的扫雷游戏呈现结果入手,我们需要新建两个二维数组,先设定行和列,这里先弄9*9的扫雷游戏,所以声明(define)行和列的大小
#define ROW 9
#define COL 9 //这些声明可以放进一个头文件,然后开头包含这个文件,这样更简洁,助于阅读,而且会常用到,声明无法更改大小,所有这里用声明,不用int
#define ROWS ROW+2
#define COLS COL+2//防止查雷时溢出
//创建初始棋盘
char put[ROWS][COLS] = { 0 };// 放置雷的信息,看不见
char show[ROWS][COLS] = { 0 };//排查雷的信息,看得见
board_show(put, '0');
board_show(show, '*');//创建好两个数组
print_board(show);//传show,put的数组不需要打印展示出来
void board_show(char board[ROWS][COLS], char set)//注意,函数主体要放在main前面,这里 方便阅读理解就放后面了
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;//将创建的数组每个元素都存进set,put的棋盘存的字符‘ 0 ’,show的棋盘存的字符‘ * ’
}
}
}
void print_board(char board[ROWS][COLS])//打印扫雷棋盘
{
int i = 0;
int j = 0;
printf("******扫雷游戏******\n\n");
for (i = 1; i <= ROW; i++)
{
for (j = 1; j <= COL; j++)
{
printf("%c ", board[i][j]);//打印出每个位置是‘ * ’的扫雷棋盘
}
printf("\n");//每一行一个换行符
}
printf("\n*******************\n\n");
}
这时运行程序会得到该图
(3)扫雷内部处理情况
1) 产生雷
所以接下来需要产生雷,也就是要在put棋盘中产生字符‘ 1 ’(其他字符也可以,1最方便)
先声明雷的个数
#define TNT 9//可以改,不超过棋盘个数就行
然后产生雷,因为要生成随机位置的雷,我们可以用rand和time(时间戳)产生随机数,注意头文件要包含<stdlib.h><time.h>
srand((unsigned)time(FULL));//产生随机数,要放在main函数主体的开头
put_TNT(put);
void put_TNT(char board[ROWS][COLS])
{
int count = TNT;
while (count)//用for循环不好,有可能会出现雷在一个位置,因为该while循环中利用count判断结束,只有在雷位置不重复的条件下count才变化,如果用for会很麻烦
{
int i = rand() % (TNT - 1) + 1;
int j = rand() % (TNT - 1) + 1;//创建雷位置,雷位置要在1到行数,列数之间,因为数组的序列从零开始
if (board[i][j] == '0')
{
board[i][j] = '1';
count--;
}
}
}
2)玩家排雷,检查周围雷的数量
void Player_check(char put[ROWS][COLS], char show[ROWS][COLS])//玩家行动,检查周围雷需要put的数组,给出玩家该信息需要show数组,所以传两个数组。
{
//玩家输入坐标
int x = 0;
int y = 0;
int a = ROW * COL - TNT;//即a为没有雷的格子数
while (a > 0)
{
printf("玩家请输入坐标:");
scanf("%d%d", &x, &y);
if (x > 0 && x <= ROW && y>0 && y <= COL)
{
if (put[x][y] == '1')//踩雷
{
printf("\n\n\n你被炸死,游戏结束\n\n\n");
DisplayBoard(put);//打印放置雷的页面,让玩家在踩雷后知道雷都在哪些位置。
break;
}
else if (put[x][y] == '0')//得出周围雷的信息,put数组中没雷是字符‘ 0 ’,有雷是字符‘ 1 ’,所有将周围八个元素相加就可以得到雷的数量,如果不用字符0,1表示会麻烦一点,可以创建一个新变量,然后用循环加条件判断,得出雷数量。
{
char c = put[x - 1][y - 1] + put[x - 1][y] +
put[x - 1][y + 1] + put[x][y + 1] +
put[x][y - 1] + put[x + 1][y] +
put[x + 1][y - 1] + put[x + 1][y + 1] - 8 * '0';//必须是减8*‘ 0 ’,减去一个不行,因为是八个元素都要变成字符来储存,所有要减去八个字符‘ 0 ’,少的话雷的数量会无法显示,系统会打印出‘ ?’
show[x][y] = c + '0';
DisplayBoard(show);//在玩家看到的show页面打印出雷信息
a--;//用while循环进行判断雷是否排完
if (a == 1)
printf("游戏胜利\n");
}
}
else
{
printf("输入坐标错误,请重新输入\n");
}
}
}
综上扫雷游戏各个部分都已经写好,只需要进行整合即可。
3.将各个部分按顺序整理
最后上述所有代码归纳集合:
注意:该代码将所有代码放进一个源文件中,也可以将game()函数放进其他源文件,建一个game.h头文件来传数据,不过该方法在传数据时需要多传行和列,并在两个源文件中包含该头文件,也可以将所有include,define放进头文件,这样更方便看懂各个部分的代码,条理更清晰。
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2//防止查雷时溢出
#define TNT 10
void menu()
{
printf("**************************\n");
printf("**********1.play**********\n");
printf("**********0.exit**********\n");
printf("**************************\n");
printf("是否开始游戏\n");
printf("请输入(1/0):");
}
void board_show(char board[ROWS][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;
}
}
}
void DisplayBoard(char board[ROWS][COLS])//打印扫雷棋盘
{
int i = 0;
int j = 0;
printf("******扫雷游戏******\n\n");
for (i = 1; i <= ROW; i++)
{
for (j = 1; j <= COL; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("\n*******************\n\n");
}
void c_TNT(char board[ROWS][COLS])//产生雷
{
int count = TNT;
while (count)//用for循环不好,有可能会出现雷在一个位置
{
int i = rand() % (TNT - 1) + 1;
int j = rand() % (TNT - 1) + 1;//创建雷位置
if (board[i][j] == '0')
{
board[i][j] = '1';
count--;
}
}
}
void Player_check(char put[ROWS][COLS], char show[ROWS][COLS])//玩家行动和检查周围雷数量
{
//玩家输入坐标
int x = 0;
int y = 0;
int a = ROW * COL - TNT;
while (a > 0)
{
printf("玩家请输入坐标:");
scanf("%d%d", &x, &y);
if (x > 0 && x <= ROW && y>0 && y <= COL)
{
if (put[x][y] == '1')
{
printf("\n\n\n你被炸死,游戏结束\n\n\n");
DisplayBoard(put);
break;
}
else if (put[x][y] == '0')
{
//得出周围雷的信息
char c = put[x - 1][y - 1] + put[x - 1][y] +
put[x - 1][y + 1] + put[x][y + 1] +
put[x][y - 1] + put[x + 1][y] +
put[x + 1][y - 1] + put[x + 1][y + 1] - 8 * '0';//必须是减8*,减去一个数不行
show[x][y] = c + '0';
DisplayBoard(show);
a--;
if (a == 1)
printf("游戏胜利\n");
}
}
else
{
printf("输入坐标错误,请重新输入\n");
}
}
}
void game()//game()函数主体,以该函数的顺序进行代码
{
//创建初始棋盘
char put[ROWS][COLS] = { 0 };// 放置雷的信息
char show[ROWS][COLS] = { 0 };//排查雷的信息,玩家看到的棋盘信息
board_show(put, '0');
board_show(show, '*');
//放置雷
c_TNT(put);
//DisplayBoard(put, ROW, COL);不打印,可以在编写时打印看是否程序出问题
DisplayBoard(show);
//玩家行动
Player_check(put, show);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//创建随机数,用于创建随机位置的雷
do
{
menu();//初始页面
scanf("%d", &input);//选择进行游戏
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏,游戏结束\n");
break;
default:
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}