今天你扫了嘛!!!!!
前言:
“扫雷”游戏想必大家都不陌生:每当电脑没网时,默默的打开Windows自带的扫雷,蜘蛛纸牌.....等小游戏。
该文章以“扫雷”为主题,以"C语言"为例实现一个简简单单的扫雷游戏。
下面进入主题:
一,游戏框架
在游戏设计前,首先要明确游戏的框架,功能
例:扫雷在N*N的棋盘上有x个雷,玩家输入棋盘坐标:1,如果该坐标是雷“游戏结束”2,如果坐标不是雷,显示坐标周围雷的个数。直到棋盘上只剩雷的坐标“游戏胜利”。
模块:
1,屏幕上呈现游戏菜单
2,根据用户输入运行相应的选项
3,游戏具有可玩性
4,设置游戏属性
5,游戏运作
以上就是“扫雷”游戏的大概思路,下面逐一实现完成属于我们自己的“扫雷”游戏!
二,游戏实现
1,基本数据
在头文件中定义好棋盘的大小,雷的个数以及各个头文件的引用还有实现游戏的各个模块函数。
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>
#define ROW 9 //棋盘大小
#define LOW 9 //棋盘大小
#define ROWS ROW+2 //棋盘数组大小
#define LOWS LOW+2 //棋盘数组大小
#define BOOM 10 //雷的个数
void menu(); //菜单
void game(); //游戏运行
void Game_Secondos(); //游戏开始读秒
void InitialPrint(char board[ROWS][LOWS]); //初始化print数组
void InitialInterior(char board[ROWS][LOWS]); //初始化interior数组
void PrintBoard(char board[ROWS][LOWS]); //打印棋盘
int PlayerMove(char board_show[ROWS][LOWS], char board_mine[ROWS][LOWS]); //玩家行动
int board_boom(char board[ROWS][LOWS],int x, int y); //计算雷个数
void bloom(char board_show[ROWS][LOWS], char board_mine[ROWS][LOWS], int x,int y); //开花
int BoomNum(char show[ROWS][LOWS]); //计算还剩多少步
此次扫雷游戏,我将设置两个数组:后台棋盘数组,前台棋盘数组来实现。
后台棋盘:主要分布雷的信息。
前台棋盘:主要输出于屏幕。
根据后台棋盘的当前坐标周围雷的信息展示于前台棋盘上。
注意:计算当前坐标周围雷的个数时,若坐标位于棋盘的边缘则会导致数组的越界访问,则声明的数组要大于的棋盘大小。
那需要大多少呢?
因为:计算的是坐标周围一圈的八个坐标,则我们的数组大小需要大于棋盘大小的整整一圈。
2,游戏菜单
游戏菜单:用printf函数来实现即可。
按可自己风格创建
void menu()
{
printf("--------扫雷游戏-------\n");
printf("***********************\n");
printf("** 1.play **\n");
printf("** 0.over **\n");
printf("***********************\n");
}
3,游戏选择与可玩性
游戏菜单供玩家提供相应的选择,在程序员的角度需要根据玩家输入的数让程序进行相应的运行,当然同时还得考虑如果玩家出现输入其它选择的现象。
🌰:
完成这一实现除了玩家不想玩的情况下,使游戏具有可玩性,玩玩一把还想玩............
很明显这就是一个循环体制
代码展示:
int main()
{
int input = 0;
char ch = 0;
//循环选择“玩/不玩”
do
{
input = 3;
menu(); //菜单
printf("输入选择:");
scanf("%d", &input);
switch (input)
{
case 1:
Game_Secondos();
game();
break;
case 0:
printf("结束游戏\n");
break;
default :
printf("输入错误\n");
break;
}
while (ch = getchar() != '\n');
} while (input);
return 0;
}
do while语句设置循环体制,先让菜单,输入什么的运行起来(个人感觉最合适的循环体制)
这里用switch(if else if也可)语句来实现多分支的情况。
以上就是基本的菜单和选择界面。
4,游戏属性
扫雷之前先构建扫雷的棋盘。
正如上面所说,构建一个前台棋盘和后台棋盘。
#define ROW 9 //棋盘大小
#define LOW 9 //棋盘大小
#define ROWS ROW+2 //棋盘数组大小
#define LOWS LOW+2 //棋盘数组大小
根据棋盘大小决定数组棋盘的大小
char print_board[ROWS][LOWS]; //前台棋盘
char interior_board[ROWS][LOWS]; //后台棋盘
1,接下来定义函数初始化这两个棋盘;
前台棋盘:将数组的内容都初始化为‘*’,展示于屏幕
后台棋盘:将数组的内容都初始为‘0’之后,便随机产生雷的坐标设置为‘1’
代码展示:
前台棋盘初始化
void InitialPrint(char board[ROWS][LOWS])
{
int i = 0;
int j = 0;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < LOWS; j++)
{
board[i][j] = '*';
}
}
}
后台棋盘初始化
void InitialInterior(char board[ROWS][LOWS])
{
int x = 0;
int y = 0;
int i, j, boom = BOOM;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < LOWS; j++)
{
board[i][j] = '0';
}
}
while (boom)
{
x = rand() % ROW + 1;
y = rand() % LOW + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
boom--;
}
}
}
2,打印棋盘
棋盘初始化好后,下面封装一个打印函数用来打印扫雷棋盘。
代码展示:
void PrintBoard(char board[ROWS][LOWS]) //打印棋盘
{
int i = 0;
int x = 0;
int y = 0;
printf("-----扫雷游戏-----\n");
for (i = 0; i <= ROW; i++) //打印列的辅助坐标
{
printf("%d ", i);
}
for (x = 1; x <= ROW ; x++) //打印行的辅助坐标的同时打印棋盘数据
{
printf("\n%d", x);
for (y = 1; y <= LOW ; y++)
{
printf(" %c", board[x][y]);
}
}
printf("\n");
}
因为棋盘比较大,可以用一个辅助坐标来方便用户输入坐标数进行扫雷。
这里要注意好换行哦!
5,游戏运行
基本的游戏属性设置好之后,下面开始规划运行的运行。
接下来封装玩家输入坐标函数:
int PlayerMove(char board_show[ROWS][LOWS], char board_mine[ROWS][LOWS]) //玩家行动
{
int x = 0;
int y = 0;
int boom = 1;
while (boom)
{
printf("输入坐标:");
scanf("%d%d", &x, &y);
if (x > 0 && x <= ROW && y > 0 && y <= LOW)
{
if (board_mine[x][y] == '1')
{
return 0;
}
else if(board_mine[x][y]=='0' && board_show[x][y] =='*')
{
bloom(board_show, board_mine, x, y); //开花
boom = BoomNum(board_show); //行动次数
if (boom == 0)
{
return 2;
}
return 1;
}
else
{
printf("坐标以占\n");
}
}
else
{
printf("坐标输入有误\n");
}
}
return 2;
}
要确保玩家输入的坐标是合法的,利用判断语句根据棋盘的最大值与最小值来限制玩家输入的坐标值。
玩家坐标输入合法后,其先判断该坐标是不是雷,是则游戏结束并打印后台棋盘,不是则根据后台棋盘的玩家坐标计算该坐标周围有多少雷,并在前台棋盘进行显示。这里因为是字符数组在计算周围雷的个数时,当九个坐标字符数都加起来之后要减去八个字符0。
同把计算雷的个数封装成一个函数
int board_boom(char board[ROWS][LOWS], int x, int y) //计算雷个数
{
int i = 0;
int j = 0;
char tmp = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
tmp += board[i][j];
}
}
return tmp - 8 * '0';
}
判断输很简单 ,那如何判断赢呢???
当然根据棋盘未走的坐标减去雷数的坐标就是还需要走的坐标啦!当把不是雷的坐标都走完我们就胜利了👏
同样把需要走的步数封装成函数方便我们利用
int BoomNum(char show[ROWS][LOWS]) //计算还剩多少步
{
int num = 0;
int i = 0, j = 0;
for (i = 1; i <= ROW ; i++)
{
for (j = 1; j <= LOW; j++)
{
if (show[i][j] == '*')
{
num++;
}
}
}
return num-BOOM;
}
以上就是初级的简单扫雷游戏我们就完成好了,当然可能不跟我们平常玩的扫雷游戏不同,因为我们还缺少一个关键步骤:那就是开花~~
什么是开花呢?那如何实现开花呢??
扫雷开花就是:当前坐标以及该坐标周围的八个坐标都不是雷的话,就把这块区域都显示出来并分别延申到该八个坐标上同样进行判断该坐标的周围坐标有没有雷,如果没有就显示进行延申坐标,直到某个坐标周围有雷停止显示以及延申。
实现开花:知道了什么是开花,根据其性质很明显可以看出这可用一个递归形式完成实现。
代码展示:
void bloom(char board_show[ROWS][LOWS], char board_mine[ROWS][LOWS], int x, int y) //开花
{
int i = 0, j = 0;
int a = 0, b = 0;
int flag = 1;
if (x > 0 && x <= ROW && y > 0 && y <= LOW )
{
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (board_mine[i][j] == '1')
{
flag = 0;
break;
}
}
}
if (flag == 1)
{
if (board_mine[x][y] == '0' && board_show[x][y] != ' ')
{
board_show[x][y] = ' ';
for (a = x - 1; a <= x + 1; a++)
{
for (b = y - 1; b <= y + 1; b++)
{
bloom(board_show, board_mine, a, b);
}
}
}
}
else
{
board_show[x][y] = board_boom(board_mine, x, y);
}
}
}
6,游戏实现
把基本的扫雷模块函数都完成好后,把这些模块进行排版实现我们自己的扫雷游戏
代码展示:
void game()
{
int judge = 0;
char print_board[ROWS][LOWS]; //前台棋盘数组
char interior_board[ROWS][LOWS]; //后台棋盘数组
InitialPrint(print_board); //初始化前台棋盘
InitialInterior(interior_board); //初始化后台棋盘,并设好雷
while (1)//游戏开始
{
PrintBoard(print_board); //打印前台棋盘
//PrintBoard(interior_board);
judge = PlayerMove(print_board, interior_board); 玩家行动,根据返回值判断游戏结构
if (0 == judge) //如果返回值为0,说明踩雷
{
printf("排雷失败\n");
//PrintBoard(print_board);
PrintBoard(interior_board);
break;//结束游戏
}
else if (2 == judge)//如果为2 说明除了雷的坐标都以走完
{
printf("扫雷成功!!\n");
PrintBoard(print_board);
PrintBoard(interior_board);
break;//结束游戏
}
//如果不为0也不为2说明还需继续排雷游戏
}
}
效果图:
以上就是我们的扫雷游戏实现,大家进行尝试尝试吧~~~~
✨完结撒花✨