在大家的电脑里一定会有一个扫雷小游戏,那么它是如何通过编程来实现的呢,现在就由我带领大家编写一个简单的扫雷小游戏。
一.文件的创建
对于编写一个像扫雷这样游戏的程序,代码全部堆积到一个文件里面,难免会造成我们出现错误。所以,当我们需要完成扫雷这个游戏时,我们可以选择在同一个文件夹里面创建多个.C文件来同时完成扫雷游戏。
如上图,对于这两个文件的命名,需要我们在创立文件时就要构思好如何去实现我们的程序。拿我自己来举例,我创造一个game.c和一个test.c文件,我想把我需要的功能(比如菜单页面)放在我的test.c文件里,而我的game.c文件需要实现我在test.c文件里面我所设计的能。
当然仅仅到这一步显然是不够的,当我们编写C语言程序的时候我们开头都需要包含头文件,对于一个.C文件还好说,但是需要多个文件的程序时单单一个.C文件就显得心有余而力不足了。
这个时候我们就可以再创建一个头文件,把test.c文件和game.c文件同时需要的数据库全部放在我们创建的头文件里面。这样就能大大提高我们编写效率。
现在我们就可以编写我们的扫雷程序了。
二.编写具体功能
如上文所说,我们需要在test.c文件里面完成我们想要的功能。
#include"game.h"
int main()
{
test();//创建test函数使用void完成具体功能
return 0;
}
如上图我们创建了一个test函数,这时我们需要使用void函数在外部实现test函数的具体功能。而在上文中我们提到了,对于一些包含数据库的函数我们都可以把它放在头文件里面。
所以#include<stdio.h>我们就可以把他放入头文件里面在开头使用包含我们创造的头文件(#include"game.h")来替代。
现在我们需要去实现test函数功能。我们需要使用void函数实现菜单的选择
#include"game.h"
void test()
{
}
int main()
{
test();
return 0;
}
好了,现在我们就要在test函数里面完成我们功能的设计了。先设想一下,当我们打开程序的时候,我们第一眼看到的就是菜单页面,那么这个时候我们就需要编写一个menu(菜单)函数了。
相同的我们也需要使用void函数对menu函数进行功能上的实现。
#include"game.h"
void menu()
{
printf("****************\n");
printf("**** 1.play ****\n");
printf("**** 2.exit ****\n");
printf("****************\n");
}
void test()
{
menu();
}
int main()
{
test();
return 0;
}
现在,主页面的设计已经完成。当我们玩游戏时我们需要进行选择游戏开始和退出,单单是完成了主界面的设计是不够的,我们还需要它在接收到例如1或者2的时候能够出现我们需要的功能。而test函数正是我们设计功能的地方。
当我们选择1或者2的时候程序可以运行我们想要的结果,但是当我们输入其他数字使,我们想要先去判断它是错误的再去阻止它进入循环。并且我们也是需要多次进行游戏,所以我们选择使用do......while循环。
#include"game.h"
void menu()
{
printf("****************\n");
printf("**** 1.play ****\n");
printf("**** 2.exit ****\n");
printf("****************\n");
}
void test()
{
int a = 0;
do
{
menu();
printf("请选择");
scanf("%d ",&a);
}while();
}
int main()
{
test();
return 0;
}
现在我们就可以开始界面和结束界面,在这里使用scanf函数。我们需要先定义一个变量a。判断我们输入的数字去选择相应的功能。
对于选择那种数字所对应的结果我们可以使用switch来解决
#include"game.h"
void menu()
{
printf("****************\n");
printf("**** 1.play ****\n");
printf("**** 2.exit ****\n");
printf("****************\n");
}
void test()
{
int a = 0;
do
{
menu();
printf("请选择");
scanf("%d ",&a);
switch (input)
{
case 1:
game();
break;
case 2:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}while();
}
int main()
{
test();
return 0;
}
如上图,我们switch函数里面有一个game函数,而这个game函数就是游戏具体功能实现,现在我们需要完成对雷区的布置以及棋盘的创建。
如果我们需要创建一个9×9的游戏规格,那么我们需要使用两个二维数组。一个数组用来存放雷,另外一个数组存放我们所需的排除雷的信息。
当我们把雷放进9×9的数组之中,为了防止越界(当程序越界访问时,程序可能会崩溃)需要我们在另外一个数组为了方便显示显示我们在排除雷的信息的数组的规格需要是11×11。
如果我们两个数组都选择9×9的规格时,当我们选择边缘行时,他会出现我们上文所说的越界的情况,程序就会随机出现任何数目。所以为了避免这种情况,我们显示雷信息里的数组的规格需要比放置雷的数组要大。(为了统一两个数组的类型两个数组我们统一使用字符数组)
现在我们在开始在game函数里布置棋盘和雷区,同样的我们也是需要使用void函数
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);
//DisplayBoard(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
如上图我们把雷和排查出雷的信息都放在规格大的棋盘里,而在规格小的棋盘里面放置雷。同样的我们在game函数里面进行棋盘的初始化以及排查雷的功能的设置。
如上图,我们在数组里面使用了我们之前从未使用的函数。所以我们必须对数组进行声明,那么我们可以把存放雷和排除出的信息所需的行(row)和列(col)使用define(定义)函数全都放进头文件里面。
我们需要先定义row和col,而更大范围的行和列仅仅需要在函数后面加上s并且用行或者列后面加上我们需要的数目。
当我们开始游戏时,我们需要初始化棋盘。初始化棋盘的意思就是开始布置雷和排查雷的信息。在初始化之后,我们没有进行任何指令时,我们对未知的位置使用“*”相同的我们也可以把雷设置为“0”。
而我们存放雷和排查雷全部都在9×9的数组规格里面所以我们把雷定义为mine函数。相同的布置雷和排查雷的函数我们也可以设置为SetMine和FindMine。
当我们需要开始游戏时,我们需要打印在屏幕上,而根据我们所设计的那样我们把雷都放在了9×9的数组里面,所以当我们打印时也需要打印9×9的数组。(打印棋盘DisplayBoard(show, ROW, COL);)
就如同上文所说,以上函数都没有进行声明。所以我们需要在头文件里面对以上函数进行声明。
#pragma once
#include <stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//为了给两个数组初始化不同的字符。我们需要设置一个set参数,这样就可以初始化两个字符
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
好了,到现在为止我们所需要的具体的功能已经完成了。现在我们需要完成game函数里面具体的功能。而布置、排除等一些功能我们需要在game.c文件里面完成。
三.实现游戏功能
相同的,我们的game.c文件里面实现的是game函数里的功能,我们也需要调用头文件。
第一步我们开始初始化棋盘,在初始化期盼中我们是不需要返回任何值的。所以我们需要使用void函数。
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函数。
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
printf("--------扫雷------\n");
int i = 0;
for (i = 0; i <= col; 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");
}
}
打印完棋盘之后,我们就可以埋雷了。所谓的埋雷就是编写SetMine函数。同样的我们埋雷的函数也是不需要返回任何值的,所以我们还是使用void函数。
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
int x = 0;
int y = 0;
while (count)
{
x = rand()%row+1;
y = rand()%col+1;
if (mine[x][y] != '1')
{
mine[x][y] = '1';//布置一个雷
count--;
}
}
}
在我们布置雷的时候,我们需要运用坐标来进行布置。所以我们布置雷的范围实在9×9这个数组里面随机布置。当我们需要从1到9之间来选择坐标而x = rand()%n恰好代表着生成一个介于0到n-1的随机整数。
如上图我们使用了我们在此之前从未声明的EASY_COUNT函数以及rand函数所以我们要在头文件里面进行声明。
#pragma once
#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 mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
在rand函数是我们必须要使用srand((unsigned int)time(NULL));而srand((unsigned int)time(NULL));是程序的具体功能。所以我们必须把他放进void test函数里。
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
}
布置完棋盘之后,我们来到了最后的排查雷的环节,也就到了最后一步扫雷的环节了。
而排查雷需不需要返回值有两种不同的情况。一种是不小心排查到雷,游戏直接结束;另外一种是没有点到雷游戏继续并且显示出周围雷的个数。
同样的,在排查雷的函数里面我们并不需要返回任何数,而我们却需要显示出周围雷的个数,所以我们需要在外部设计一个需要返回值的函数在排查雷的内部显示出来就能解决问题。
但是我们定义的不是数字而是字符,所以我们计算的是对应做表里面字符的个数,而不单单是数字的大小。
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y] +
mine[x - 1][y - 1] +
mine[x][y - 1] +
mine[x + 1][y - 1] +
mine[x + 1][y] +
mine[x + 1][y + 1] +
mine[x][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<col*row-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("输入的坐标有误x(1~9),y(1~9),重新输入");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}
四.代码呈现
1.头文件
#pragma once
#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 mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
2.test文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("*************************\n");
printf("******* 1. play *****\n");
printf("******* 0. 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);
//DisplayBoard(mine, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
void test()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择\n");
break;
}
} while (input);
}
int main()
{
test();
return 0;
}
3.game.c文件
#define _CRT_SECURE_NO_WARNINGS 1
#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 <= col; 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");
}
}
//布置雷是在棋盘上随机的找10个坐标布置的
//x: 1~9
//y: 1~9
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
int x = 0;
int y = 0;
while (count)
{
x = rand()%row+1;
y = rand()%col+1;
if (mine[x][y] != '1')
{
mine[x][y] = '1';//布置一个雷
count--;
}
}
}
//int GetMineCount(char mine[ROWS][COLS], int x, int y)
//{
// return mine[x - 1][y] +
// mine[x - 1][y - 1] +
// mine[x][y - 1] +
// mine[x + 1][y - 1] +
// mine[x + 1][y] +
// mine[x + 1][y + 1] +
// mine[x][y + 1] +
// mine[x - 1][y + 1] - 8*'0';
//}
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int count = 0;
for (i = -1; i <= 1; i++)
{
int j = 0;
for (j = -1; j <= 1; j++)
{
count += (mine[x + i][y + j] - '0');
}
}
return count;
}
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<col*row-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("输入的坐标有误x(1~9),y(1~9),重新输入");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, ROW, COL);
}
}