前言:
为了巩固C语言,我运用所学的知识,写了一篇关于扫雷游戏的博客。如果有大佬看到这篇文章,如有不足之处,请你一定要指出来。
游戏的规则:
我们在棋盘格中任意点开一个格子(输入行和列确认这个格子),若这个格子不是雷就排除了这个位置,排除后这个格子会显示将它围起来的几个格子中有几颗雷,如果点开的格子埋有雷则为游戏失败。
一、模块化编程
模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。
这里我创建了三个文件:game.h文件用来写自定义函数的声明;game.c文件用来写定义;test.c文件用了写整个程序的实现。(这里c文件中只需#include “game.h”,就能使头文件和源文件连接起来)
如下:
二、游戏的思路
1.游戏的设计:
1).扫雷游戏需要存储布置好的雷的信息,需要一个二维数组
2).给两个9* 9的二维数组:
一个存放雷的信息,另一个存放布置好雷的信息
3).为了防止在统计坐标周围的雷的个数的时候数组越界,我们把数组设计成11* 11的二维数组
4).数组是11*11,并且是字符数组(这里我们把‘0’表示非雷,‘1’表示雷)
2.游戏的逻辑:
1).创建菜单函数选择 进入游戏 以及 退出游戏
2).创建游戏函数,初始化棋盘(游戏在走的过程中要进行数据的存储,可以使用11*11的二维数组char board[ROWS][COLS];)
3).打印棋盘
4).布置雷
5).排查雷
三、实现游戏步骤/过程
1…创建菜单函数选择进入游戏以及退出游戏
1).创建菜单
void menu()
{
printf("**********************\n");
printf("**** 1.开始游戏 ****\n");
printf("**** 0.退出游戏 ****\n");
printf("**********************\n");
}
测试:
2).选择进入游戏以及退出游戏(实现玩一把不过瘾就会在来一次用到do while)
#include "game.h"
void menu()
{
printf("**********************\n");
printf("**** 1.开始游戏 ****\n");
printf("**** 0.退出游戏 ****\n");
printf("**********************\n");
}
int main()
{
int input;
do
{
menu();
scanf("%d", &input);
switch (input)//用来进行一次操作
{
case 0:
break;
case 1:
{
printf("扫雷游戏\n");//可以进入游戏
break;
}
default:
{
printf("笨蛋输错了,重新输入:\n");
break;
}
}
}while (input);//可以进行多次游戏
return 0;
}
测试:
2.创建游戏函数,初始化棋盘
1).创建游戏函数:
创建两个相同的数组:
因为用户在玩游戏的时候看到的是被覆盖起来的棋盘,看不到哪些地方都埋着雷的,这是显示棋盘。我们还需要一个埋雷的棋盘,这样才能实现玩家在玩游戏时通关或者未通关,将埋雷的点位呈现给玩家。
创建11*11数组的原因:
在扫雷中,我们排查的一个位置上要是显示了数字,就说明在此位置的周围 8 个格子中存在这个数字个雷,那么我们在排四个边上的位置时候格子是不够 8 个的,但是我们的功能在实现的时候还是会去找这 8 个格子的,这样就会造成数组的越界访问的问题
为了方便后期我们能更改棋盘的大小和雷的个数,使用宏定义define
#define ROW 9// 显示棋盘的行数
#define COL 9// 显示棋盘的列数
#define ROWS ROW+2// 实际棋盘的行数
#define COLS COL+2// 实际棋盘的列数
#define mine_count 10//设置雷的数量
2).初始化棋盘
void InitBoard(char board[][COLS], int rows, int cols, char set)//初始化棋盘
{
int i, j;
for(i=0;i<rows;i++)//遍历的方法,给每个数组元素赋值
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
printf("\n");
}
3.打印棋盘
void DisPlayBoard(char board[][COLS], int row, int col)
{
int i, j;
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");
}
}
测试两个棋盘:
测试:
存放雷的棋盘
用户操作的棋盘
4.布置雷
void SetMine(char mine[][COLS], int row, int col)
{
int i=mine_count,x,y;
while(i)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';//字符1就是雷
i--;
}
}
}
测试(10个雷放数组mine_1里):
5.排查雷
首先我们对玩家输入的坐标位置进行判断,是否合法,坐标必须在 1~row/col 之间(包含 1 和 row/col),如果不在此范围,代码会走第一个 else 语句,提醒玩家“笨蛋,坐标输错了,请重新输入:”的字样。如果合法就会走第一个 if 语句,进去之后我们对该位置进行判断,看是否该位置为字符 ‘1’,如果是字符 ‘1’ 就是踩到了雷,就会提醒玩家“很遗憾,你被炸死了”的字样,并将雷的分布图给玩家呈现出来如果是字符 ‘0’ 就走 else 语句,对该位置的周围 8 个格子的雷的个数进行计算并标注出来,这时我们就调用GetMine()函数来计算:
void FindMine(char mine_1[][COLS],char mine_2[][COLS],int row,int col)
{
int x,y,count;
while (1)
{
printf("请输入排查雷的坐标:\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标输入正确
{
if (mine_1[x][y] == '1')//踩雷了
{
printf("你被炸死了\n");
DisPlayBoard(mine_1, ROW, COL);
break;
}
else//没踩雷
{
count = GetMine(mine_1, x, y);//统计雷的四周有几个雷
mine_2[x][y] = '0' + count;
//if (count == 0)
// expend(mine_1, mine_2, row, col, x, y);
DisPlayBoard(mine_2, ROW, COL);
}
}
else
printf("笨蛋,坐标输错了,请重新输入:\n");
}
}
测试结果:
6.判断输赢
mine_2数组中没有被排查的坐标为‘ * ’,所以只要统计剩下的‘ * ’是否等于雷的个数,等于则游戏胜利。
lswin(char mine_2[][COLS], int row, int col)//判断有几个符号‘*’然后返回
{
int i,j,c;
for(i=0;i<row;i++)
for (j = 0; j < col; j++)
{
if (mine_2[i][j] == '*')
c++;
}
return c;
}
7.扩展
说明:扩展坐标的范围应该加以限制,如果我要排查的坐标有雷,那么直接游戏结束,没雷就要向周围8个坐标扩展,并且没雷就把这个坐标设置为空格,只有设置了才能防止被重复递归!然后要限制坐标范围,如果到了边界,就没必要向边界外再继续递归,会越界的,递归出后就是找到了周围有雷,显示雷的个数就可以了文章来源:https://www.toymoban.com/news/detail-536783.html
void expend(char mine_1[][COLS], char mine_2[][COLS], int row, int col, int x, int y)
{
int count = GetMine(mine_1, x, y);//统计雷的四周有几个雷
if (count == 0)
{
mine_1[x][y] = ' ';//如果周围地雷数为零则把这个位置赋值为空格
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (mine_2[i][j] == '*' && i > 0 && i <= row && j > 0 && j <= col)
{
expend(mine_1, mine_2, row, col, i, j);
}
}
}
}
else
{//递归的出口就是我统计到周围有几个雷
mine_2[x][y] = count + '0';
}
}
四、总代码量
1.测试游戏test.c文件的代码
#include "game.h"
void menu()
{
printf("**********************\n");
printf("**** 1.开始游戏 ****\n");
printf("**** 0.退出游戏 ****\n");
printf("**********************\n");
}
void game()//创建游戏
{
system("cls");//每次玩游戏,都把前面的记录全部清掉
srand((unsigned)time(NULL));
//创建雷的数组(mine_1)显示数组(mine_2)两个数组的类型,大小相同
char mine_1[ROWS][COLS];//存放布置的雷的信息
char mine_2[ROWS][COLS];//存放排查出雷的信息
//初始化棋盘
InitBoard(mine_1, ROWS, COLS,'0');//初始化棋盘里全部赋‘0’
InitBoard(mine_2, ROWS, COLS, '*');//初始化棋盘里全部赋‘*’
//打印棋盘
//DisPlayBoard(mine_1, ROW, COL);//打印存放雷信息的9*9的棋盘
DisPlayBoard(mine_2, ROW, COL);//打印排查出雷信息的9*9的棋盘
//布置雷
SetMine(mine_1, ROW, COL);
//排查雷
FindMine(mine_1,mine_2, ROW, COL);
}
int main()
{
int input;
do
{
menu();
scanf("%d", &input);
switch (input)//用来进行一次操作
{
case 0:
{
printf("退出游戏\n");
break;
}
case 1:
{
game();//可以进入游戏
break;
}
default:
{
printf("笨蛋输错了,重新输入:\n");
break;
}
}
}while (input);//可以进行多次游戏
return 0;
}
2.游戏的实现game.c文件的代码
#include "game.h"
void InitBoard(char board[][COLS], int rows, int cols, char set)//初始化棋盘
{
int i, j;
for(i=0;i<rows;i++)
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
printf("\n");
}
void DisPlayBoard(char board[][COLS], int row, int col)
{
int i, j;
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");
}
}
void SetMine(char mine[][COLS], int row, int col)
{
int i=mine_count,x,y;
while(i)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';//字符1就是雷
i--;
}
}
}
int GetMine(char mine_1[][COLS], int x, int y)
{
return mine_1[x - 1][y - 1] + mine_1[x - 1][y] + mine_1[x - 1][y + 1]
+ mine_1[x][y - 1] + mine_1[x][y + 1] + mine_1[x + 1][y - 1]
+ mine_1[x + 1][y] + mine_1[x + 1][y + 1] - 8 * '0';
}
void FindMine(char mine_1[][COLS],char mine_2[][COLS],int row,int col)
{
int x,y,count;
while (1)
{
printf("请输入排查雷的坐标:\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标输入正确
{
if (mine_1[x][y] == '1')//踩雷了
{
printf("你被炸死了\n");
DisPlayBoard(mine_1, ROW, COL);
break;
}
else//没踩雷
{
expend(mine_1, mine_2, row, col, x, y);
DisPlayBoard(mine_2, ROW, COL);
int a=lswin(mine_2, row, col);//判断是否排查完
if (a == ROW * COL - mine_count)//不需要排查了,赢啦
{
printf("你赢了,真棒!\n");
break;
}
}
}
else
printf("笨蛋,坐标输错了,请重新输入:\n");
}
}
void expend(char mine_1[][COLS], char mine_2[][COLS], int row, int col, int x, int y)
{
int count = GetMine(mine_1, x, y);//统计雷的四周有几个雷
if (count == 0)
{
mine_1[x][y] = ' ';//如果周围地雷数为零则把这个位置赋值为空格
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (mine_2[i][j] == '*' && i > 0 && i <= row && j > 0 && j <= col)
{
expend(mine_1, mine_2, row, col, i, j);
}
}
}
}
else
{//递归的出口就是我统计到周围有几个雷
mine_2[x][y] = count + '0';
}
}
lswin(char mine_2[][COLS], int row, int col)//判断有几个符号‘*
{
int i,j,c=0;
for(i=0;i<row;i++)
for (j = 0; j < col; j++)
{
if (mine_2[i][j] == '*')
c++;
}
return c;
}
3.游戏函数的声明game.h头文件代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#define ROW 9// 显示棋盘的行数
#define COL 9// 显示棋盘的列数
#define ROWS ROW+2// 实际棋盘的行数
#define COLS COL+2// 实际棋盘的列数
#define mine_count 10//设置雷的数量
void InitBoard(char board[][COLS], int rows, int cols, char set);//初始化棋盘
void DisPlayBoard(char board[][COLS], int row, int col);//布置棋盘
void SetMine(char mine[][COLS], int row, int col);//布置雷
void FindMine(char mine_1[][COLS], char mian_2[][COLS], int row, int col);//排查雷
int GetMine(char mine_1[][COLS], int x, int y);//统计雷的四周有几个雷
void expend(char mine_1[][COLS], char mine_2[][COLS], int row, int col, int x, int y);//扩展坐标的范围
int lswin(char mine_2[][COLS], int row, int col);//统计有几个字符‘*’
测试
文章来源地址https://www.toymoban.com/news/detail-536783.html
到了这里,关于[C语言][游戏][扫雷]的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!