想必很多人都玩过扫雷游戏,现在的我们踏入了C语言的学习中,我想你可能有想法自己来制作一个简单的扫雷游戏,接下来我将手把手的带你制作它。(注意用的是VS软件)
为了方便我们搞清楚每一部分是实现哪个功能的,我们可以在VS上创建一个头文件和两个源文件:就像这样。头文件我们用来定义、声明一些函数、涵盖头文件。于是我们现在“main.c”文件中进行一些操作。
一、
我们需要考虑来设计一个游戏的开始与推出的一个选项或者界面,我们就可以写一个不需要返回值的函数:
void menu()
{
printf("************************\n");
printf("****** 1.play ******\n");
printf("****** 2.exit ******\n");
printf("************************\n");
}
其中有两个选项供我们选择,即“1”玩和“2”退出,这个只是一个形式上的函数,并不能进行任何的处理。
二、
我们需要将这个函数嵌套进入另外一个函数,才能进入下一步操作,我们就将运行的函数命名为test吧。在test函数中目前是不需要有返回值的,继续使用void即可。在test函数中我们可以定义一个变量“input”并赋值为0,我们都知道扫雷游戏是很容易输的游戏,不能只是说输了胡哦这硬了程序就直接终止了,于是我们需要将程序进行循环,并且当我们选择退出游戏时将游戏终止。
1.此时我们可以使用do-while循环,将循环条件设为input,当input为“0”时便可以直接退出游戏了,先将我们上一个函数menu放入test中以有一个初始界面。
2.下一步我们需要表达我们的需求。即我们需要输入“1”或者“0”来决定游戏的走向,即输入input的值。
3.对于input是“1”或者“0”都有对应的下一步,此时我们就需要有条件语句进行分支,于是我们可以用switch语句理进行分类:对于“1”,我们就进入游戏,对于“0”,我们就输出“退出游戏”并退出即可,对于不是“1”也不是“2”的我们可以使用default输出“输入错误请重新输入”。具体代码如下:
void test()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
想必已经看到了截图中的game函数了吧,这是咱们接下来要讲的游戏内容。
三、用创建的game函数来进行整体的操作(需要与后面结合一起看)
于是我们创建game函数,先给出我们的game函数代码:
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);
}
四、棋盘的布置
我们所玩的扫雷游戏在开始都是看不见雷的,并且我们需要布置一个棋盘,于是我们就需要创建二维数组了,其中show数组用来进行玩游戏的看不见的棋盘,mine数组用来设置雷的位置,在此我们进行一个9*9的简单扫雷,那我们的ROWS与COLS是否直接写9就性了呢,显然不是,不知道你有没有想过在棋盘边界的格子周围的雷怎么算呢。为了解决这个问题,我们可以在头文件中进行如下定义:
#define ROW 9
#define COL 9
#include<stdio.h>
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
我们可以定义一个ROW与COL,在定义一个ROWS与COLS,后者的值为前者加二,这样我们如果想要改变棋盘的大小就可以直接调整前者的值即可。
现在我们创建了这两个数组,我们该如何给他们赋值呢?由于show数组的雷是看不见的,我们可以直接将其内部全部去赋值为'*',不过边缘处将标上序号:于是我们得到了如下的开始:
那怎样才能赋值成这样呢?我们可以设计一个函数将数组内部全部变为'*',即函数Initboard(于game.c文件中编写):
void Initboard(char arr[ROWS][COLS], int rows, int cols,char ret)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
arr[i][j] = ret;
}
}
}
但是仅仅凭这个函数我们是无法将边界处的格子化为编号的,于是我们就需要再写一个函数Displayboard来进行调整:
void Displayboard(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
printf("--------扫雷游戏-------\n");
for (i = 0; i <= col; 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 ", arr[i][j]);
}
printf("\n");
}
}
五、雷的布置
游戏过程的一部分我们完成了,但是我们现在还没有布置雷,接下来我们进行这个操作,但是雷肯定不能可以为之,不然就缺少了随机性与趣味性,这时我们就需要用到随机函数rand,不过这个并不是真正的随机,当rand()后括号中的值固定是它就有一定的固定性了,有疑虑同学的可以重复的输出几次rand(1),可以发现他们的值固定rand(2)也是如此,但是与rand(1)的值不同,那我们可以通过改变rand后的值来使其随机,那有的同学会想,那还不是需要一个随机值,又回去了,但在这时我们只需要一个变化的值即可。一直在变化的值?此时我们就可以想到时间是一直在变化的,我们就可以使用time函数来使rand()变的随机。于是,我们可以在main.c文件的main函数中写下srand((unsigned int)time(NULL));语句来使rand()变得随机,我们算出rand()%row+1这个值将会在棋盘的范围之内,代码可以如下:
void SetMine(char arr[ROWS][COLS], int row, int col)//布置雷
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
我们用SetMine函数布置雷,并且定义一个EASY_COUNT(在game.h中定义)来确定雷的个数,并在mine数组中将设置为雷的格子变为'1',注意此时的'0'和'1'均为字符常量,并不是整型。
六、找出周围雷的个数进一步排雷
如果我们想要排除雷,我们需要知道我们所排除的点的周围的雷的个数,于是我们就可以通过编写如下的函数来帮助我们知晓周围雷的个数了:
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] + mine[x + 1][y - 1]+mine[x+1][y+1] - 8 * '0');
}
后面的-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");
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜您成功排雷\n");
Displayboard(mine, ROW, COL);
}
}
我们用FindMine函数将他们组织起来,这部分相对于简单,看懂即可。接着我们再将其移动到主函数上即可:
int main()
{
srand((unsigned int)time(NULL));
test();
return 0;
}
值得注意的是,所有的函数都需要在game.h文件中声明,并且别的文件使用头文件中的定义与函数是都需要#include"game.h",还有就是很多同学在VS上使用scanf时经常被提醒不安全而需要加上_s,此时我们只需要在开头加上#define _CRT_SECURE_NO_WARNINGS即可。下面我把代码全部放在下面方便同学们使用:
1.game.h:
#pragma once
#define ROW 9
#define COL 9
#include<stdio.h>
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10void Initboard(char arr[ROWS][COLS], int rows, int cols,char set);
void Displayboard(char arr[ROWS][COLS], int row, int col);//打印棋盘
void SetMine(char board[ROWS][COLS], int row, int col);
int GetMineCount(char mine[ROWS][COLS], int x, int y);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);2.game.c:
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
void Initboard(char arr[ROWS][COLS], int rows, int cols,char ret)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
arr[i][j] = ret;
}
}
}
void Displayboard(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
printf("--------扫雷游戏-------\n");
for (i = 0; i <= col; 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 ", arr[i][j]);
}
printf("\n");
}
}
void SetMine(char arr[ROWS][COLS], int row, int col)//布置雷
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
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] + mine[x + 1][y - 1]+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");
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜您成功排雷\n");
Displayboard(mine, ROW, COL);
}
}3.main.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] = { 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);
}
void test()
{
int input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}int main()
{
srand((unsigned int)time(NULL));
test();
return 0;
}