文章目录
扫雷游戏说明
扫雷游戏是一款经典的单人计算机游戏。游戏的目标是在一个区域内,通过推理和猜测,找出所有没有地雷的方格,同时避免触碰到地雷。游戏内容如下:
游戏界面由9x9的方格区域组成,每个方格可能包含一个地雷或者没有地雷。玩家通过点击方格来探索,玩家点击方格会显示一个数字,表示其周围地雷的数量。如果点击到一个地雷,游戏结束。玩家可以通过观察已揭示的数字来推测哪些方格可能包含地雷,并且在排雷后可以进行坐标标记记录自己的推断和猜测。
游戏胜利的条件是揭示所有没有地雷的方格。当所有方格都被揭示时,游戏胜利,显示一个恭喜信号。
游戏程序架构
那么如何通过C语言怎样完成一款简易的扫雷游戏呢?这里将扫雷整体程序需要实现的功能模块化,需要实现功能如下:
游戏界面的创建、游戏区域的创建、初始化游戏区域、打印游戏区域、放置地雷,排查地雷,标记功能。
综上所述,将扫雷游戏的整体程序分为三个文件,分别是主函数程序main.c,头文件game.h和实现各种功能的game.c。将游戏界面的创建和各功能的函数调用放在主程序main.c文件中,程序整体的参数定义和函数声明放置在game.h文件中,游戏功能程序的实现则放进game.c文件中。
游戏程序详解
主函数程序main.c
游戏界面的创建
游戏在进入的时候,都需要有一个菜单界面显示进行开始游戏或者是退出游戏的操作,这里创建函数menu( )使用printf来实现游戏菜单界面的显示。
代码如下(示例):
void menu()
{
//游戏界面
printf("*******************\n");
printf("*******1.play******\n");
printf("*******0.exit******\n");
printf("*******************\n");
}
实现游戏界面上的开始游戏或者退出游戏的功能,除非选择退出游戏否则改程序不会退出,这里就可以使用switch分支语句进行选择, do while语句来循环执行,创建输入变量input,当input为0时退出游戏,程序的循环也会中止,当input为 1的时候,开始游戏,进入到执行游戏函数game( )中,当输入变量为其他值时,则输入错误,重新输入。srand函数生成的随机数是用于后续放置地雷的坐标使用。
代码如下(示例):
int main()
{
int input = 0;
srand((unsigned int)time(NULL));//随机数的生成
do
{
menu();//游戏界面函数调用
printf("请玩家进行选择:>");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出游戏\n");
break;
case 1:
printf("开始游戏\n");
game();//游戏功能程序的调用
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
创建游戏区域的创建
游戏程序函数game( )包含了游戏区域的定义,初始化和其他功能函数调用,传递各功能函数game.c所需的参数传递。这里关键的就是需要定义两个游戏区域,由于游戏需要的元素众多,所以定义两个游戏区域,一个展示给玩家,一个则存放地雷和非地雷的一些元素,当游玩的时候,可以将mine区域的数值赋给show区域,相比较在一个游戏区域中进行数值变换,定义两个游戏区域能让程序更加简便。
代码如下(示例):
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(show, mine, ROW, COL);
}
头文件程序game.h
头文件程序game.h里面包含的内容:整体程序所需头文件,全局变量和功能函数game.c的函数声明。这里全局变量中定义的ROWS和COLS主要是为了防止排查周边区域的时,计算周围地雷个数则会产生越界访问的问题,但是游玩的区域范围还是ROW和COL。
代码如下(示例):
//程序的头文件
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//全局变量
#define ROW 9 //游戏区域的行
#define COL 9//游戏区域的列
#define ROWS ROW+2//防止扫雷时出现越界访问
#define COLS COL+2//防止扫雷时出现越界访问
#define COUNT 10//地雷个数
#define MARK 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 board[ROWS][COLS], int row, int col);
//排查地雷
void FindMine(char show[ROWS][COLS],char mine[ROWS][COLS], int row, int col);
功能函数程序game.c
初始化游戏区域
使用for循环将游戏区域初始化赋值。这里展示给玩家的游戏区域初始值为“*”,另外一个游戏区域的初始值为“0”。
代码如下(示例):
void InitBoard(char board[ROWS][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++)
{
board[i][j] = set;//将主函数传递过来的的符号赋值
}
}
}
打印游戏区域
利用for循环打印二维数组的游戏区域,并且为游戏区域增加行与列的坐标,以便于观察行与列的坐标数值。
代码如下(示例):
void DisPlayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("----- 扫雷游戏 -----\n");
for (i = 0; i <= col; 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");
}
printf("----- 扫雷游戏 -----\n");
}
放置地雷
通过rand( )函数来生成随机地雷的坐标数值,用符号“1”来表示地雷放置到mine游戏区域中。
代码如下(示例):
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = COUNT;
while (count)
{
int i = rand() % row + 1;//生成行的随机数
int j = rand() % col + 1;//生成列的随机数
//防止地雷坐标放置重复
if (mine[i][j] == '0')
{
mine[i][j] = '1';//地雷的用“1”来表示
count--;
}
}
}
排查地雷
排查的地雷的函数程序里面,主要是判断玩家输入的坐标有没有踩到地雷,当踩到地雷后游戏结束,当玩家探险的坐标不是雷则会执行扩散和标记功能。游戏的获胜条件是当游戏区域内的* 和#的总和等于地雷的个数则表示玩家排雷成功。
代码如下(示例):
int win_show(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] == '*' || show[i][j] == '#')
count++;//计算*和#的个数
}
}
return count;
}
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int win = ROW * COL - COUNT;
while (win)
{
printf("请玩家输入坐标:>");
scanf("%d%d", &i, &j);
//玩家输入坐标不能越界
if (i > 0 && i <= row && j > 0 && j <= col)
{
//判断输入的坐标是否雷,不是雷就计算周围雷的个数
//否则炸死打印棋盘
if (mine[i][j] == '0')
{
//实现扩散功能:
diffusion(show, mine, i, j);
DisPlayBoard(show, ROW, COL);
//实现标记的功能
MarkShow(show, ROW, COL);
DisPlayBoard(show, ROW, COL);
win = COUNT - win_show(show, ROW, COL);
}
else
{
printf("很遗憾,你已经被炸死了!\n");
DisPlayBoard(mine, ROW, COL);
break;
}
}
else
{
printf("坐标输入错误,请重新输入!\n");
}
}
if (win == 0)
{
printf("恭喜,扫雷成功!\n");
DisPlayBoard(mine, ROW, COL);
}
}
扩散功能实现
首先,要先确定做玩家探索的坐标周围个数是没有地雷的才能开始执行扩散功能,否则显示周围的地雷个数总和的个数。当扩散的时候,扩散的坐标也不能超过显示的游戏区域范围,因为在之前设计之初为了防止周边区域出现越界访问的问题,所以不限制扩散坐标的区域则会出现BUG,会将周围一圈都进行扩散。如下图所示:
代码如下(示例):
int ADD(char mine[ROWS][COLS], int x, int y)//计算坐标周围地雷个数
{
int sum = 0;
int i = 0;
for (i = -1; i <= 1; i++)
{
int j = 0;
for (j = -1; j <= 1; j++)
{
if (mine[x + i][y + j] == '1')
{
sum++;
}
}
}
return sum;
}
void diffusion(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
//判断扩散的坐标不能超出范围,不然会出现bug
if ((x + i) >= 1 && (x + i) <= ROW && (y + i) >= 1 && (y + i) <= COL)
{
//判断输入坐标周围没有雷,才可以扩散
if (ADD(mine, x, y) == 0)
{
//没有雷周围的各个坐标
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
//判断是否已经重复查找
if (show[x + i][y + j] == '*')
{
show[x + i][y + j] = ' ';
//调用自己进行递归
diffusion(show, mine, (x + i), (y + j));
}
}
}
}
//如果有雷则输出坐标附近雷的个数
else
{
show[x][y] = ADD(mine, x, y) + '0';
}
}
}
标记功能实现
该程序设计的标记功能,是当玩家进行探险操作后选择是否标记,当不执行标记则跳出循环继续探险,当执行标记则可以一直标记,当然标记的个数和范围也是有限的,如果标记错误也可以的取消标记。
代码如下(示例):
void MarkShow(char show[ROWS][COLS], int row, int col)
{
int input = 0;
int i = 0;
int j = 0;
int mark = MARK;
do
{
printf("是否进行坐标标记?(0/1):>");
scanf("%d", &input);
switch (input)
{
case 0:
break;
case 1:
{
printf("请输入标记坐标:>");
scanf("%d%d", &i, &j);
if (i > 0 && i <= row && j > 0 && j <= col)
{
if (show[i][j] == '*' && mark != 0)
{
show[i][j] = '#';//标记
mark--;
}
else if (show[i][j] == '#' && mark != 0)
{
show[i][j] = '*';//取消标记
mark++;
}
else
{
printf("无法标记\n");
}
}
printf("\n");
break;
}
default:
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
printf("\n");
}
注:感谢浏览,程序如有问题欢迎进行指正!
全部代码
main.c
#define _CRT_SECURE_NO_WARNINGS
#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(mine, ROW, COL);
DisPlayBoard(show, ROW, COL);
//放置地雷
SetMine(mine, ROW, COL);
//DisPlayBoard(mine, ROW, COL);
//排查地雷
FindMine(show, mine, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请玩家进行选择:>");
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出游戏\n");
break;
case 1:
printf("开始游戏\n");
game();
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
game.c
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void InitBoard(char board[ROWS][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++)
{
board[i][j] = set;
}
}
}
void DisPlayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("----- 扫雷游戏 -----\n");
for (i = 0; i <= col; 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");
}
printf("----- 扫雷游戏 -----\n");
}
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = COUNT;
while (count)
{
int i = rand() % row + 1;
int j = rand() % col + 1;
//防止放置雷重复
if (mine[i][j] == '0')
{
mine[i][j] = '1';
count--;
}
}
}
int ADD(char mine[ROWS][COLS], int x, int y)
{
int sum = 0;
int i = 0;
for (i = -1; i <= 1; i++)
{
int j = 0;
for (j = -1; j <= 1; j++)
{
if (mine[x + i][y + j] == '1')
{
sum++;
}
}
}
return sum;
}
void diffusion(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
//判断扩散的坐标不能超出范围,不然会出现bug
if ((x + i) >= 1 && (x + i) <= ROW && (y + i) >= 1 && (y + i) <= COL)
{
//判断输入坐标周围没有雷,才可以扩散
if (ADD(mine, x, y) == 0)
{
//没有雷周围的各个坐标
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
//判断是否已经重复查找
if (show[x + i][y + j] == '*')
{
show[x + i][y + j] = ' ';
//调用自己进行递归
diffusion(show, mine, (x + i), (y + j));
}
}
}
}
//如果有雷则输出坐标附近雷的个数
else
{
show[x][y] = ADD(mine, x, y) + '0';
}
}
}
//实现标记功能
void MarkShow(char show[ROWS][COLS], int row, int col)
{
int input = 0;
int i = 0;
int j = 0;
int mark = MARK;
do
{
printf("是否进行坐标标记?(0/1):>");
scanf("%d", &input);
switch (input)
{
case 0:
break;
case 1:
{
printf("请输入标记坐标:>");
scanf("%d%d", &i, &j);
if (i > 0 && i <= row && j > 0 && j <= col)
{
if (show[i][j] == '*' && mark != 0)
{
show[i][j] = '#';//标记
mark--;
}
else if (show[i][j] == '#' && mark != 0)
{
show[i][j] = '*';//取消标记
mark++;
}
else
{
printf("无法标记\n");
}
}
printf("\n");
break;
}
default:
printf("输入错误,请重新选择!\n");
break;
}
} while (input);
printf("\n");
}
//计算*或者#的个数
int win_show(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] == '*' || show[i][j] == '#')
count++;
}
}
return count;
}
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int win = ROW * COL - COUNT;
while (win)
{
printf("请玩家输入坐标:>");
scanf("%d%d", &i, &j);
//玩家输入坐标不能越界
if (i > 0 && i <= row && j > 0 && j <= col)
{
//判断输入的坐标是否雷,不是雷就计算周围雷的个数
//否则炸死打印棋盘
if (mine[i][j] == '0')
{
//实现扩散功能:
diffusion(show, mine, i, j);
DisPlayBoard(show, ROW, COL);
//实现标记的功能
MarkShow(show, ROW, COL);
DisPlayBoard(show, ROW, COL);
win = COUNT - win_show(show, ROW, COL);
}
else
{
printf("很遗憾,你已经被炸死了!\n");
DisPlayBoard(mine, ROW, COL);
break;
}
}
else
{
printf("坐标输入错误,请重新输入!\n");
}
}
if (win == 0)
{
printf("恭喜,扫雷成功!\n");
DisPlayBoard(mine, ROW, COL);
}
}
game.h
#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 COUNT 10
#define MARK 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 board[ROWS][COLS], int row, int col);
//排查地雷
void FindMine(char show[ROWS][COLS],char mine[ROWS][COLS], int row, int col);