简单实现扫雷游戏!
程序员文章站
2022-04-07 13:57:19
...
本来想着可以写出与电脑上扫雷差异不大的东西,但由于技术不精,虽然模拟实现了扫雷,但写出来的东西与真正扫雷还有一定差距,不过这些都不影响整个游戏的体验。话不多说,直入主题:
先附上头文件game.h的代码,在头文件中我定义了游戏界面的大小 R 和 C 。根据游戏的规则,如果输入人一个坐标,那么显示出来的就是这个坐标所在的九宫格的雷数,因此为了方便计算雷数,虽然显示出来的游戏界面是R * C ,但是实际数组的大小为(R + 2) * (C + 2)。如下图:
同时,在头文件中声明了定义的所有函数,函数后面的注释对应它们的功能。
#ifndef __GAME_H__
#define __GAME_H__
#define EASY_COUNT 10 //雷数
#define R 9 //行
#define C 9 //列
#define ROWS R + 2
#define COLS C + 2
#include<stdio.h>
void Choose_Menu(int a[ROWS][COLS]); //选择菜单界面
void Game_Initialize(int a[ROWS][COLS], int row, int col); //初始化
void Set_Mine(int a[ROWS][COLS], int row, int col); //布置雷
void Move_Mine(int a[ROWS][COLS], int row, int col, int x, int y); //移动雷 如果第一次输入的坐标就是雷,则将此处的雷移动至别处,游戏继续。
void Game_Menu_Print(const int a[ROWS][COLS], int row, int col); //游戏中界面输出
void Game_Result_Print(const int a[ROWS][COLS], int row, int col);//游戏结束输出
void Find_Mine(int a[ROWS][COLS], int row, int col); //进行排雷的过程
int Get_Win(const int a[ROWS][COLS], int row, int col); //判断游戏状态 未点开的坐标数
void Get_Mine(int a[ROWS][COLS], int row, int col); //计算雷数
void Play_Game(int a[ROWS][COLS]); //游戏开始调用的总函数
#endif //__GAME_H__
下面附上game.c的代码,它是对game.h中函数的实现。
需要特别说明的是:
- 移动雷函数的作用在于:如果没有移动雷的函数,那么如果在第一次输入坐标时,不幸输入了有雷的坐标,游戏就会立即结束,这样毫无游戏体验。因此,为了提升玩家游戏体验,当第一次输入坐标时,如果输入了有雷的坐标,那么程序就会在后台悄悄地将此处的雷移动至别处。当然这仅限第一次。
- 由于在计算时,传入的是数组名,因此可以直接在调用的函数中修改数组的值,因此,Get_mine不需要返回值。
- 整个代码的最难点在于当输入一个坐标后,由于该坐标所在的九宫格中雷数为0,因此会从此坐标向外蔓延,显示一片,知道边界全为数字为止。在这里我使用了递归的方法由中心点向外蔓延,具体是在函数Get_Mine中实现的。
以下为 game.c中的代码(下面为扫雷的整体模拟思路!):
/*
// 扫雷的整体模拟思路
只定义一个数组
数组类型为整形
数组默认值为-2 // 初始值
进行布置雷,如果此处是雷则值为-1
输入一个位置,会计算此处雷数
如果此处周围雷数为0 显示空 同时向外蔓延炸开
游戏中的输出界面 主要参考下面两个值:
1.如果是-2 说明未排雷 输出‘*’
2.如果是0 或者 大于0的数字,说明排雷已经排过了这些坐标
如果值为-1 不影响游戏中输出界面,只在最后输出游戏结果时,输出’@‘
如果此处周围有雷,那么就只显示该位置,显示数字为周围雷数
每次输入坐标后都检测剩余未点开的坐标数 如果只剩下了雷 则胜利
数组大小为显示界面横纵坐标各加2
*/
#include"game.h"
void Choose_Menu(int a[ROWS][COLS])
{
int i = 1;
while (i)
{
printf("\t\t\t******************************************************\n");
printf("\t\t\t******************************************************\n");
printf("\t\t\t*************请选择进行的操作: *************\n");
printf("\t\t\t*************1.开始游戏 0.退出游戏 *************\n");
printf("\t\t\t******************************************************\n");
printf("\t\t\t******************************************************\n");
printf("\t\t\t输入:>_");
scanf("%d", &i);
switch (i)
{
case 1: Play_Game(a, R, C); break;
case 0: printf("退出成功,请按任意键结束!\n"); break;
default:printf("输入错误,请重新输入!\n"); break;
}
}
}
void Game_Initialize(int a[ROWS][COLS], int row, int col)
{
int i = 0, j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
a[i][j] = -2;
}
}
}
void Set_Mine(int a[ROWS][COLS], int row, int col, int count)
{
int x = 0;
int y = 0;
srand((unsigned int)time(NULL));
while (count)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (a[x][y] == -2)
{
a[x][y] = -1;
count--;
}
}
}
void Move_Mine(int a[ROWS][COLS], int row, int col, int x, int y)
{
int x0 = 0;
int y0 = 0;
srand((unsigned int)time(NULL));
while (1)
{
x0 = rand() % row + 1;
y0 = rand() % col + 1;
if ((x0 != x || y0 != y) && a[x0][y0] != -1)
{
a[x0][y0] = -1;
break;
}
}
}
void Game_Menu_Print(const int a[ROWS][COLS], int row, int col)
{
int i = 0, j = 0;
printf("\n\t\t\tGame_Menu:\n\n");
//输出坐标
printf("\t\t\t\t");
for (i = 1; i <= col; i++)
{
if(i < 10)
printf(" %d ", i);
else printf("%d ", i);
}
printf("\n\n");
//输出棋局
for (i = 1; i <= row; i++)
{
printf("\t\t\t %d ",i);
for (j = 1; j <=col; j++)
{
if (a[i][j] == -2 || a[i][j] == -1)
printf(" * ");
else if(a[i][j] == 0)
printf(" ");
else printf(" %d ", a[i][j]);
}
printf(" %d\n", i);
}
//输出坐标
printf("\n\t\t\t\t");
for (i = 1; i <= col; i++)
{
if (i < 10)
printf(" %d ", i);
else printf("%d ", i);
}
printf("\n\n");
}
void Game_Result_Print(const int a[ROWS][COLS], int row, int col)
{
int i = 0, j = 0;
printf("\n\t\t\tGame_Result:\n\n");
//输出坐标
printf("\t\t\t\t");
for (i = 1; i <= col; i++)
{
if (i < 10)
printf(" %d ", i);
else printf("%d ", i);
}
printf("\n\n");
//输出棋局
for (i = 1; i <= row; i++)
{
printf("\t\t\t %d ", i);
for (j = 1; j <= col; j++)
{
if (a[i][j] == -2)
{
printf(" * ");
}
else if (a[i][j] == -1)
{
printf(" @ ");
}
else if (a[i][j] == 0)
{
printf(" ");
}
else printf(" %d ", a[i][j]);
}
printf(" %d\n", i);
}
//输出坐标
printf("\n\t\t\t\t");
for (i = 1; i <= col; i++)
{
if (i < 10)
printf(" %d ", i);
else printf("%d ", i);
}
printf("\n\n");
}
int Get_Win(const int a[ROWS][COLS], int row, int col)
{
int i = 0, j = 0;
int ret = 0;
for(i = 1; i <= row; i++)
for (j = 1; j <= col; j++)
{
if (a[i][j] == -2)
ret++;
}
return ret;
}
void Get_Mine(int a[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
if ((a[row - 1][col - 1] == -1) + (a[row - 1][col] == -1) + (a[row - 1][col + 1] == -1) + (a[row][col - 1] == -1) + (a[row][col + 1] == -1) + (a[row + 1][col - 1] == -1) + (a[row + 1][col] == -1) + (a[row + 1][col + 1] == -1) != 0)
{
a[row][col] = (a[row - 1][col - 1] == -1) + (a[row - 1][col] == -1) + (a[row - 1][col + 1] == -1) + (a[row][col - 1] == -1) + (a[row][col + 1] == -1) + (a[row + 1][col - 1] == -1) + (a[row + 1][col] == -1) + (a[row + 1][col + 1] == -1);
}
else
{
//只有在棋盘内才递归
if (row >= 1 && row <= R && col >= 1 && col <= C)
{
a[row][col] = ((a[row - 1][col - 1] == -1) + (a[row - 1][col] == -1) + (a[row - 1][col + 1] == -1) + (a[row][col - 1] == -1) + (a[row][col + 1] == -1) + (a[row + 1][col - 1] == -1) + (a[row + 1][col] == -1) + (a[row + 1][col + 1] == -1));
//向上
if (a[row - 1][col] < 0)
Get_Mine(a, row - 1, col);
//向右
if (a[row][col + 1] < 0)
Get_Mine(a, row, col + 1);
//向下
if (a[row + 1][col] < 0)
Get_Mine(a, row + 1, col);
//向左
if (a[row][col - 1] < 0)
Get_Mine(a, row, col - 1);
}
}
}
void Find_Mine(int a[ROWS][COLS], int row, int col)
{
int x = 0, y = 0;
int win = Get_Win(a, row, col);;
while (win)
{
//输入坐标
Game_Menu_Print(a, row, col);
printf("请输入要排查的坐标:");
scanf("%d %d", &x, &y);
//判断有效性
if (x >= 1 && x <= row && y >= 1 && y <= col) //输入正确
{
//
if (a[x][y] == -1)
{
if (win == row * col - EASY_COUNT)
{
Move_Mine(a, row, col, x, y);
Get_Mine(a, x, y);
win = Get_Win(a, row, col);
}
else
{
printf("\nGAME OVER!!!\n\n");
break;
}
}
else
{
if (a[x][y] != -2)
{
printf("坐标输入有误,请重新输入!\n");
continue;
}
Get_Mine(a, x, y);
win = Get_Win(a, row, col);
}
}
else
{
printf("坐标输入有误,请重新输入!\n");
}
}
if (win == 0)
printf("\nYou Win!\n\n");
}
void Play_Game(int a[ROWS][COLS], int row, int col)
{
Game_Initialize(a, ROWS, COLS);
Set_Mine(a, row, col,EASY_COUNT);
Find_Mine(a, row, col);
Game_Result_Print(a, row, col);
}
最后是主函数代码test.c
#include "game.h"
int main()
{
int arr[ROWS][COLS] = { 0 };
Choose_Menu(arr);
return 0;
}
代码整体相比于真正的扫雷还相差很多,需要注意的问题就是:
- 不能标记
- 在炸开时,显示的边界不是连续的,如下图:
不过这都不影响整体的游戏过程,如果有需要可自行修改。
以上即为我模拟实现的扫雷游戏,不足之处还望指正!
下一篇: SEO项目主管,如何打造一个SEO团队?