目录
前言
程序设计思路
程序设计
? ? ? ? 创建棋盘
? ? ? ? 初始化棋盘
? ? ? ? 设置地雷
? ? ? ? 打印棋盘
? ? ? ? 玩家排雷
? ? ? ? 胜利判定
? ? ? ? 胜利判定(展开功能)
总结
前言
? ? ? ? 扫雷这款游戏是我最喜欢的小游戏之一了,也是伴随了我的童年的一款小游戏之一。而扫雷同时是编程的入门小游戏之一,那么今天让我来教教你如何在自己的电脑上制作并运行扫雷游戏吧。
程序设计思路
? ? ? ? 在实现任何项目时,有一个清晰的设计思路非常重要。
而扫雷游戏需要有以下的设计思路:
- 创建并初始化棋盘
- 设置地雷
- 打印棋盘
- 玩家排雷
程序设计
? ? ? ? 创建棋盘
?在创建棋盘前,我们先要了解扫雷棋盘的构造。
首先,棋盘得有地方放置地雷,但是又不能给玩家发现,我们可以创建两个数组,一个数组存放地雷的信息,另一个数组存放玩家的扫雷情况就可以解决这个问题。
接着,棋盘该设置多大呢?我们知道正常的扫雷游戏简单模式为9*9,那么我们可以在面向玩家的数组创建一个9*9的数组,但是地雷信息呢?我们来看看下面图:
因为最上面和最下面加上左右第一列都会往外面访问多一行或者一列,所以我们可以在四边分别再加上一行或一列,即9*9的棋盘,我们要在地雷信息的数组上创建11*11的大小。
为了让扫雷游戏更具有可玩性,我们可以对棋盘的大小进行宏定义
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
这里顺便解释以下为什么两个数组都用了+2的大小,因为在之后玩家的输入以及对棋盘的处理较为方便,无需对玩家输入的值进行预处理就可以在数组中直接应用。
? ? ? ? 初始化棋盘
因为两组数组存放的内容都不一样,所以在这次的初始化棋盘的函数中加多了一个set参数,目的是为了使棋盘初始化成想要的内容。
void init_board(char(*bp)[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++)
{
(* (bp + i))[j] = set;
}
}
}
因此,地雷信息的数组初始化成‘0’,而显示出来的棋盘初始化为‘*’。?
init_board(mine, ROWS, COLS, '0');
init_board(show, ROWS, COLS, '*');
? ? ? ? 设置地雷
初始化棋盘后,我们就应该对地雷进行设置。在设置地雷时我们又需要获取随机值,需要使用到rand函数,所以我们需要在main函数中设置srand函数以启用rand函数。有关rand函数的细节,大伙可以参考以下网址进行深究。https://legacy.cplusplus.com/reference/cstdlib/rand/?kw=rand
srand((unsigned int)time(NULL));
注意:使用srand和time需要引用头文件
#include<stdlib.h>
#include<time.h>
设置完成后就可以获取随机数了。
void set_mine(char board[ROWS][COLS], int row, int col)
{
int ret = SetEasy;//可以直接输入地雷的数目
while (ret)
{
int x = rand() % 9 + 1;//1~9
int y = rand() % 9 + 1;//1~9
if (board[x][y] == '0')
{
board[x][y] = '1';
ret--;
}
}
}
这里说明以下SetEasy是我对于游戏难度进行设置而做的宏定义。
? ? ? ? 打印棋盘
以上步骤完成后就可以打印棋盘让玩家进行游玩了。打印棋盘的过程较为简单,所以我就直接放出代码让大家学习了。
void display_board(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");
}
}
? ? ? ? 玩家排雷
到了这一步就是整个游戏的精髓了,如果看不明白可以多看几遍了解下过程和逻辑,或者私信作者进行解答。
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = row * col;//第一种:0
while (win > SetEasy)//第一种:(win < (row*col-SetEasy))
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= col)//检查输入的坐标是否在范围内
{
if (show[x][y] == '*')//检查该坐标是否已经排雷
{
if (mine[x][y] == '0')//检查该坐标是否是雷
{
//第一种(没有展开功能)
//int count = num_mine(mine, x, y);
//show[x][y] = count + '0';
//win++;
//第二种(包含展开功能)
exten_board(mine, show, x, y);
display_board(show, row, col);
win = is_win(show, row, col);
}
else
{
printf("踩到雷了,游戏结束\n");
display_board(mine, ROW, COL);
break;
}
}
else
{
printf("请输入正确的坐标\n");
}
}
else
{
printf("请输入正确的坐标\n");
}
}
if (win == SetEasy)//第一种:(win == (row*col-SetEasy))
{
printf("恭喜成功排雷\n");
}
由于代码中包涵了两种胜利方式,若使用第一种胜利方式请根据注释更换成相应的代码!!!?
? ? ? ? 胜利判定
玩家排雷之后又如何判断玩家胜利呢?
首先可以参考第一种(较简单):
//计算坐标周围的地雷数
char num_mine(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');
}
int is_win(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
只需要检查玩家正确排雷的次数,如果次数为棋盘总大小减去地雷数量,即玩家排雷成功。
每次成功排雷都会调用num_mine函数以检查该坐标周围的地雷数量。
? ? ? ? 胜利判定(展开功能)
第二种(较难):
第二种方法为递归方法,也是包涵了展开功能。
//展开处理
void exten_board(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = num_mine(mine, x, y);
if (count != 0)
{
show[x][y] = count + '0';
}
else if (count == 0 && show[x][y] == '*')
{
show[x][y] = ' ';
exten_board(mine, show, x - 1, y - 1);
exten_board(mine, show, x - 1, y);
exten_board(mine, show, x - 1, y + 1);
exten_board(mine, show, x, y - 1);
exten_board(mine, show, x, y + 1);
exten_board(mine, show, x + 1, y - 1);
exten_board(mine, show, x + 1, y);
exten_board(mine, show, x + 1, y + 1);
}
}
递归的展开条件:
- 坐标不是地雷
- 坐标周围没有地雷
- 坐标没有被排查
总结
? ? ? ? 以上就是制作扫雷游戏主要的代码,如果有疑问欢迎大家在评论区留言或者私信作者哦。
希望大家也能够制作出自己的扫雷游戏,下面我也会放出本人的源代码供大家查阅。
文件:test.c
#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 };
//初始化棋盘
init_board(mine, ROWS, COLS, '0');
init_board(show, ROWS, COLS, '*');
//打印棋盘
display_board(show, ROW, COL);
//设置地雷
set_mine(mine, ROW, COL);
//玩家排雷
//display_board(mine, ROW, COL);
find_mine(mine, show, ROW, COL);
}
int main()
{
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);
return 0;
}
文件:game.c
#include "game.h"
void init_board(char(*bp)[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++)
{
(* (bp + i))[j] = set;
}
}
}
void display_board(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");
}
}
void set_mine(char board[ROWS][COLS], int row, int col)
{
int ret = SetEasy;
while (ret)
{
int x = rand() % 9 + 1;//1~9
int y = rand() % 9 + 1;//1~9
if (board[x][y] == '0')
{
board[x][y] = '1';
ret--;
}
}
}
//计算坐标周围的地雷数
char num_mine(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 exten_board(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = num_mine(mine, x, y);
if (count != 0)
{
show[x][y] = count + '0';
}
else if (count == 0 && show[x][y] == '*')
{
show[x][y] = ' ';
exten_board(mine, show, x - 1, y - 1);
exten_board(mine, show, x - 1, y);
exten_board(mine, show, x - 1, y + 1);
exten_board(mine, show, x, y - 1);
exten_board(mine, show, x, y + 1);
exten_board(mine, show, x + 1, y - 1);
exten_board(mine, show, x + 1, y);
exten_board(mine, show, x + 1, y + 1);
}
}
//检查是否获胜
int is_win(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = row * col;//第一种:0
while (win > SetEasy)//第一种:(win < (row*col-SetEasy))
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= col)//检查输入的坐标是否在范围内
{
if (show[x][y] == '*')//检查该坐标是否已经排雷
{
if (mine[x][y] == '0')//检查该坐标是否是雷
{
//第一种(没有展开功能)
//int count = num_mine(mine, x, y);
//show[x][y] = count + '0';
//win++;
//第二种(包含展开功能)
exten_board(mine, show, x, y);
display_board(show, row, col);
win = is_win(show, row, col);
}
else
{
printf("踩到雷了,游戏结束\n");
display_board(mine, ROW, COL);
break;
}
}
else
{
printf("请输入正确的坐标\n");
}
}
else
{
printf("请输入正确的坐标\n");
}
}
if (win == SetEasy)//第一种:(win == (row*col-SetEasy))
{
printf("恭喜成功排雷\n");
}
}
文件:game.h
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define SetEasy 10
//初始化数组
void init_board(char(*bp)[COLS], int rows, int cols, char set);
//打印棋盘
void display_board(char board[ROWS][COLS], int row, int col);
//设置地雷
void set_mine(char board[ROWS][COLS], int row, int col);
//玩家排雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
|