?先赞后看,不足指正!
这将对我有很大的帮助!
项目专栏:游戏~
阿哇旭的主页:Awas-Home page
目录
????????相信大家小时候都玩过有趣的扫雷小游戏吧!在这里,我们可以利用自己学习过的知识来实现这个游戏项目。
????????可能用到的知识:
????????集成开发环境(IDE):Visual Studio 2022,若要使用scanf函数,请添加如下注释:
#define _CRT_SECURE_NO_WARNINGS
//忽略scanf函数的安全警告
????????那么,话不多说,我们一起来看看吧!?
1. 玩家通过游戏菜单选择开始游戏(继续玩)或者退出游戏
2. 扫雷的棋盘是默认为 9*9 的格子
3. 默认随机布置10个雷
4. 可以排查雷
(1)如果排查方块不是雷,就会继续排查周围的方块,直到排到有雷的方块(显示周围有几个雷)或到达边界为止;
(2)如果排查方块是雷,就炸死,游戏结束 ;
(3)玩家可以对认为是雷的方块进行标记处理;
(4)把除10个雷方块之外的所有非雷方块都找出来,排雷成功,游戏结束;
????????1. 我们需要在9*9的棋盘上进行操作,通过创建一个9*9的数组来存放信息,布置雷时,有雷存放“1”,无雷存放“0”;
? ? ? ? 2. 在排查雷过程中,如果排查方块无雷,就在该方块处显示周围八个方块中雷的数量;
? ? ? ? 为了防止数组越界的情况,在设计的时候,我们可以给棋盘(数组)扩大一圈,雷还是布置在中间的9*9的坐标上,周围一圈不去布置雷就行,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11 是比较合适。
? ? ? ? ?3. 在游戏中,我们不能把布置信息展示给玩家。因此,我们要创建两个数组,一个数组(mine)用于存放布置雷的信息,另一个数组(show)用于向玩家展示;
????????4. 若排查方块没有雷,就统计显示周围八个方块中雷的数量;
????????5. 布置雷棋盘全部初始化为 '0' 并随机布雷,展示给玩家的棋盘全部初始化为 '*',为未排查状态。
????????使用多个文件来组织和管理项目。将代码分散到多个文件中有助于提高游戏代码的可读性、可维护性和可扩展性。具体步骤:
? ? ? ? 1. 创建头文件 game.h ,用于存放数据类型以及功能函数的声明;
? ? ? ? 附:(其他源文件引用即可,#include "game.h")
? ? ? ? 2. 创建源文件 test.c?,用于文件中游戏的测试逻辑;
? ? ? ? 3. 创建源文件 game.c ,用于文件中游戏功能函数的实现。
(1)功能
1. 玩家通过输入选择 1. 开始游戏,0. 退出游戏;
2. 若输入错误,则会提醒玩家重新选择。
(2)代码实现?
? ? ? ? 我们可以利用简单的 do-while,switch 结构来实现功能。
// 菜单
void menu()
{
printf("*********************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("*********************\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); // 使用当前时间作为随机数种子
do
{
menu(); // 菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls"); // 清空屏幕指令,头文件<stdlib.h>
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input); // 选择0,退出游戏
return 0;
}
#define ROW 9 // 行
#define COL 9 // 列
#define ROWS ROW+2 // 扩展行
#define COLS COL+2 // 扩展列
#define BOMB_COUNT 10 // 布置雷个数
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
????????记得在其他源文件中引用(#include "game.h")。?
(1)功能
1. mine数组全部初始化为 '0'。
2. show数组全部初始化为 '*'。
(2)代码实现
????????我们可以 for 的双循环结构,来实现 mine 与 show 棋盘的初始化。
// 初始化棋盘
void InitBoard(char arr[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++)
{
arr[i][j] = set;
}
}
}
(1)功能
打印向玩家展示的游戏棋盘,美化棋盘,使其更加整洁。
(2)代码实现?
? ? ? ? 利用 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++)
{
if (i == 0)
{
printf(" "); // 四空格
}
else
{
printf(" %d ", i); //三空格一数字
}
}
printf("\n"); // 换行
for (i = 0; i <= row; i++)
{
if (i == 0)
{
printf(" |"); // 三空格一竖线
}
else
{
printf("---|"); // 三短横一竖线
}
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf(" %d |", i); // 打印行号
for (j = 1; j <= col; j++)
{
printf(" %c |", Board[i][j]); // 棋盘字符
}
printf("\n");
for (j = 0; j <= col; j++)
{
if (j == 0)
{
printf(" |");
}
else
{
printf("---|");
}
}
printf("\n");
}
printf(" ---------------------------------------\n"); // 结束线
}
(3)效果展示
(1)功能
在mine数组中随机布置雷,
(2)代码实现
? ? ? ? ?利用随机数来确定布雷坐标,以达到在mine数组棋盘中随机布雷的目的。
// 布置雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
int count = BOMB_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
(3)效果展示?
(1)功能
1. 玩家排雷或标记雷,若排查位置是雷,则递归展开,直至排查到雷;
2 .对认为是雷的位置进行标记处理。
(2)代码实现
// 统计排查坐标周围雷的数量-(法1)
static int GetBombCount(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');
}
// 统计排查坐标周围雷的数量-(法2)
//static int GetBombCount(char mine[ROWS][COLS], int x, int y)
//{
// int i = 0;
// int count = 0;
// for (i = x - 1; i <= x + 1; i++)
// {
// int j = 0;
// for (j = y - 1; y <= y + 1; j++)
// {
// if (mine[i][j] == '1')
// {
// count += (mine[i][j] - '0');
// }
// }
// }
//}
// 递归排雷
void SpreadBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
if (x<1 || x>ROW || y<1 || y>COL)
{
return;
}
if (show[x][y] != '*')
{
return;
}
int count = GetBombCount(mine, x, y);
if (count == 0) // 无雷继续扩展
{
show[x][y] = ' '; // 扩展为' '
SpreadBoard(mine, show, x, y + 1);
SpreadBoard(mine, show, x, y - 1);
SpreadBoard(mine, show, x + 1, y - 1);
SpreadBoard(mine, show, x + 1, y + 1);
SpreadBoard(mine, show, x + 1, y);
SpreadBoard(mine, show, x - 1, y + 1);
SpreadBoard(mine, show, x - 1, y - 1);
SpreadBoard(mine, show, x - 1, y);
}
else
{
show[x][y] = '0' + count; // 有雷输出个数
}
}
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
printf("请输入要排查雷的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了!\n");
strcpy(gameStatus, "DEFEAT");
}
else
{
SpreadBoard(mine, show, x, y); // 递归排雷扩展
sum--;
if (sum == 0)
{
strcpy(gameStatus, "VICTORY"); // 用于将一个字符串的内容复制到另一个字符串中
}
}
}
else
{
printf("该坐标已被排查\n");
}
}
else
{
printf("坐标无效,重新输入\n");
}
}
// 标记雷
void MarkMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int input = 0;
printf("请输入要标记雷位置的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
show[x][y] = '!';
sum--;
if (sum == 0)
{
strcpy(gameStatus, "VICTORY"); // 用于将一个字符串的内容复制到另一个字符串中
}
}
else
{
printf("该坐标已被排查或标记\n");
}
}
else
{
printf("坐标无效,重新输入\n");
}
}
(1)功能
设置一个全局变量 sum = 布雷数量、游戏状态变量?gameStatus,排雷后变量 sum 减一,标记雷后变量sum 减一,直到 sum 为零,然后?strcmp(gameStatus, "VICTORY") ,再进行判断。
(2)代码实现
1.strcmp
是C语言中的一个字符串比较函数,用于比较两个字符串是否相等。函数原型如下:
int strcmp(const char *str1, const char *str2);
2.strcpy
是C语言中的一个字符串复制函数,用于将一个字符串的内容复制到另一个字符串中。函数原型如下:
char *strcpy(char *dest, const char *src);
?
if (strcmp(gameStatus, "VICTORY") == 0)
{
DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
printf("恭喜你,扫雷成功!");
break;
}
else if (strcmp(gameStatus, "DEFEAT") == 0)
{
DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
printf("很遗憾,扫雷失败!");
break;
}
(1)运行顺序
1. 初始化棋盘
2. 随机布置雷
3. 玩家选择标记雷或排查雷
4. 判断是否被雷炸死
5. 判断是否扫雷成功
6. 若步骤4或5均未发生,就继续执行步骤 1、2、3,循环往复。
(2)逻辑框架
#include<stdio.h>
#include"game.h" //包含游戏的头文件
// 菜单
void menu()
{
printf("*********************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("*********************\n");
}
// 功能选择
void choose()
{
printf(" ****************************************\n");
printf(" ************** 1. 排查雷 ***************\n");
printf(" ************** 2. 标记雷 ***************\n");
printf(" ****************************************\n");
}
// 游戏
void game()
{
// 完成游戏
// 存放布置雷信息
char mine[ROWS][COLS] = { 0 }; // 全部初始化为'0'
// 存放排查雷信息
char show[ROWS][COLS] = { 0 }; // 全部初始化为'*'
// 初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 布置雷
// 在9x9的棋盘上随机布置若干个雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL); // 打印布置雷信息的棋盘
//DisplayBoard(show, ROW, COL); // 打印游戏棋盘
// 1.排查雷
//FindMine(mine, show, ROW, COL);
// 2.标记雷
//MarkMine(mine,show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); // 使用当前时间作为随机数种子
do
{
menu(); // 菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls"); // 清空屏幕指令,头文件<stdlib.h>
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input); // 选择0,退出游戏
return 0;
}
(1)game.h
#define ROW 9 // 行
#define COL 9 // 列
#define ROWS ROW+2 // 扩展行
#define COLS COL+2 // 扩展列
#define BOMB_COUNT 10 // 布置雷个数
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
// 变量声明
extern int sum; // 剩余雷的数量
extern char gameStatus[20]; // 标记游戏状态变量
// 函数声明
// 初始化棋盘函数
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set);
// 打印棋盘函数
void DisplayBoard(char Board[ROWS][COLS], int row, int col);
// 布置雷函数
void SetMine(char arr[ROWS][COLS], int row, int col);
// 排查雷函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
// 标记雷函数
void MarkMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
(2)test.c
#include<stdio.h>
#include"game.h" //包含游戏的头文件
// 菜单
void menu()
{
printf("*********************\n");
printf("******* 1.play ******\n");
printf("******* 0.exit ******\n");
printf("*********************\n");
}
// 功能选择
void choose()
{
printf(" ****************************************\n");
printf(" ************** 1. 排查雷 ***************\n");
printf(" ************** 2. 标记雷 ***************\n");
printf(" ****************************************\n");
}
// 游戏
void game()
{
// 完成游戏
// 存放布置雷信息
char mine[ROWS][COLS] = { 0 }; // 全部初始化为'0'
// 存放排查雷信息
char show[ROWS][COLS] = { 0 }; // 全部初始化为'*'
// 初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 布置雷
// 在9x9的棋盘上随机布置若干个雷
SetMine(mine, ROW, COL);
//DisplayBoard(mine, ROW, COL); // 打印布置雷信息的棋盘
//DisplayBoard(show, ROW, COL); // 打印游戏棋盘
// 1.排查雷
//FindMine(mine, show, ROW, COL);
// 2.标记雷
//MarkMine(mine,show, ROW, COL);
while (1)
{
if (strcmp(gameStatus, "FIGHTING")!= 0) // 用于比较两个字符串是否相等
{
break;
}
int input = 0;
DisplayBoard(show, ROW, COL);
choose();
printf("请选择功能(1/2):>");
scanf("%d", &input);
switch (input)
{
case 1:
FindMine(mine, show, ROW, COL); // 排查雷
Sleep(1000);
system("cls");
break;
case 2:
MarkMine(mine, show, ROW, COL); // 标记雷
Sleep(1000);
system("cls");
break;
default:
printf("输入错误,重新输入!\n");
Sleep(1000);
system("cls");
break;
}
if (strcmp(gameStatus, "VICTORY") == 0) // 用于比较两个字符串是否相等
{
DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
printf("恭喜你,扫雷成功!");
break;
}
else if (strcmp(gameStatus, "DEFEAT") == 0) // 用于比较两个字符串是否相等
{
DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
printf("很遗憾,扫雷失败!");
break;
}
}
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); // 使用当前时间作为随机数种子
do
{
menu(); // 菜单
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls"); // 清空屏幕指令,头文件<stdlib.h>
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input); // 选择0,退出游戏
return 0;
}
(3)game.h
#include"game.h"
int sum = BOMB_COUNT; // 剩余雷的数量
char gameStatus[20] = "FIGHTING"; // 标记游戏状态变量
// 初始化棋盘
void InitBoard(char arr[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++)
{
arr[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++)
{
if (i == 0)
{
printf(" "); // 四空格
}
else
{
printf(" %d ", i); //三空格一数字
}
}
printf("\n"); // 换行
for (i = 0; i <= row; i++)
{
if (i == 0)
{
printf(" |"); // 三空格一竖线
}
else
{
printf("---|"); // 三短横一竖线
}
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf(" %d |", i); // 打印行号
for (j = 1; j <= col; j++)
{
printf(" %c |", Board[i][j]); // 棋盘字符
}
printf("\n");
for (j = 0; j <= col; j++)
{
if (j == 0)
{
printf(" |");
}
else
{
printf("---|");
}
}
printf("\n");
}
printf(" ---------------------------------------\n"); // 结束线
}
// 布置雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
int count = BOMB_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
// 统计排查坐标周围雷的数量-(法1)
static int GetBombCount(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');
}
// 统计排查坐标周围雷的数量-(法2)
//static int GetBombCount(char mine[ROWS][COLS], int x, int y)
//{
// int i = 0;
// int count = 0;
// for (i = x - 1; i <= x + 1; i++)
// {
// int j = 0;
// for (j = y - 1; y <= y + 1; j++)
// {
// if (mine[i][j] == '1')
// {
// count += (mine[i][j] - '0');
// }
// }
// }
//}
// 递归排雷
void SpreadBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
if (x<1 || x>ROW || y<1 || y>COL)
{
return;
}
if (show[x][y] != '*')
{
return;
}
int count = GetBombCount(mine, x, y);
if (count == 0) // 无雷继续扩展
{
show[x][y] = ' '; // 扩展为' '
SpreadBoard(mine, show, x, y + 1);
SpreadBoard(mine, show, x, y - 1);
SpreadBoard(mine, show, x + 1, y - 1);
SpreadBoard(mine, show, x + 1, y + 1);
SpreadBoard(mine, show, x + 1, y);
SpreadBoard(mine, show, x - 1, y + 1);
SpreadBoard(mine, show, x - 1, y - 1);
SpreadBoard(mine, show, x - 1, y);
}
else
{
show[x][y] = '0' + count; // 有雷输出个数
}
}
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
printf("请输入要排查雷的坐标:");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (show[x][y] == '*')
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了!\n");
strcpy(gameStatus, "DEFEAT");
}
else
{
SpreadBoard(mine, show, x, y); // 递归排雷扩展
sum--;
if (sum == 0)
{
strcpy(gameStatus, "VICTORY");/* 用于将一个字符串的内容复制到另一
个字符串中*/
}
}
}
else
{
printf("该坐标已被排查\n");
}
}
else
{
printf("坐标无效,重新输入\n");
}
}
// 标记雷
void MarkMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int input = 0;
printf("请输入要标记雷位置的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
show[x][y] = '!';
sum--;
if (sum == 0)
{
strcpy(gameStatus, "VICTORY"); /* 用于将一个字符串的内容复制到另一个
字符串中*/
}
}
else
{
printf("该坐标已被排查或标记\n");
}
}
else
{
printf("坐标无效,重新输入\n");
}
}
????????希望这篇文章对大家有所帮助,如果你有任何问题和建议,欢迎在评论区留言,这将对我有很大的帮助。
????????完结!咻咻~~