C语言扫雷小游戏 递归自动拆解作业

刚入门C,以下内容可能用词不太妥当,有错误的地方请直接留言指正 捶 我!谢谢!

看b站鹏哥的视频,具体大差不差,写了递归自动拆解

目录

主函数 test.c / main()

Game 函数 test.c / game()

Game.h 文件 (各种操作的函数声明,头文件声明用)

Game.c (存放扫雷各操作 初始化/打印/布雷/排雷/递归)

Initboard 初始化棋盘

Displayboard 展示棋盘

SetMine 埋雷(随机埋雷)

FindMine 扫雷

        AotoFind(扫雷 可以递归自己实现展开)

                get_mine_count() 计算周围炸弹数量并返回 为int型


创建2个棋盘(一个给用户看show,一个给埋雷mine)

为了防止 get_mine_count() 计算周围炸弹数量 边缘溢出,创建的棋盘需要大一圈(行列都+2)

 

需要一个主函数来做框架,再增加menu显示菜单

进入到game函数内正式开始游戏的判断

主函数 test.c / main()

先写了一个main

srand是后续要用到的取随机值

Sleep是延时 单位是毫秒 需要引用头文件Windows

完成菜单显示,菜单选择,退出游戏等其他选项

do while函数保证能至少进入一次菜单,判断方式为当退出游戏时input为0则不再循环

switch正好和do while组合 修改input值

#include "game.h" //引用game.h头文件,后面会讲到

int main()//主程序 菜单
{
	srand((unsigned int)time(NULL));// 随机值种子 后面会用到
	int input = 0;//记录输入的数字
	do
	{
		menu(); //打印欢迎菜单
		printf("请输入选项前的数字>>");
		scanf_s("%d", &input);
		switch (input)
		{
			case 1:
				game();//进入游戏本体
				break;//别忘记break了,不然会继续执行下去
			case 0:
				printf("正在退出...\n");
				Sleep(1000);//暂停1秒钟,需要结合windows.h
				break;//别忘记break了,不然会继续执行下去
			default:
				printf("输入错误,请重新输入...\n");
				Sleep(1000);
		}
	} while (input);
	return 0;
}

下面是menu()的欢迎菜单,这部分主要是输出界面,自行发挥啦!

注意不要忘记\n(反斜杠n)换行!!!

menu()//菜单
{
	printf("******************************\n");//不要忘记最后\n
	printf("******************************\n");
	printf("********** 扫雷游戏 **********\n");
	printf("**********  1.play  **********\n");
	printf("**********  0.exit  **********\n");
	printf("**********  BA7LGJ  **********\n");
	printf("******************************\n");
	printf("******************************\n");
}

Game 函数 test.c / game()

game函数正式开始进入游戏,但是game函数还是存放在test.c文件里(也可以放在game.h但是后续写起来很麻烦)

void game()
{
	//棋盘初始化(布置雷 一个存放雷的棋盘mine,一个展示的棋盘show) 
	char mine[ROWS][COLS] = { 0 }; //创建二维数字mine来存放雷的值
	char show[ROWS][COLS] = { 0 };//创建二维数组show来显示待拆的未知区域
	Initboard(mine, ROWS, COLS, '0');//数组初始化,把数组mine全部设定为0
	Initboard(show, ROWS, COLS, '*');//数组初始化,把数组show全部设定为*

	//布雷
	SetMine(mine, ROW, COL, MINE_COUNT); //布置地雷,在mine数组的ROW*COL范围内布置MINE_COUNT数量的雷

	//打印棋盘
	Displayboard(show, ROW, COL);//打印show棋盘ROW*COL范围内的情况

	//排雷
	FindMine(mine, show, ROW, COL); //给玩家寻找雷的过程

}


Game.h 文件 (各种操作的函数声明,头文件声明用)

stdio.h 就不说了printf什么的都要用

stdlib.h 用于srand,rand函数 (随机值)

time.h  用于(time函数) 与srand结合用

windows.h 用于Sleep延时 不一定要用 将Sleep删掉就行 只是一个延时函数

ROW行 COL列 棋盘大小

ROWS行 COL列 前文已经讲过了防止寻找值的时候溢出二维函数

MINE_COUNT 地雷数量设置

剩下的就是各操作的声明了,后续会细讲

注意在 二维数组 后的[][]用的是ROWS/COLS将整个扩大一圈的棋盘都要传过去否则有可能会溢出

#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 1

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 mine[ROWS][COLS], int row, int col, int mine_count);//布雷

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//排雷


Game.c (存放扫雷各操作 初始化/打印/布雷/排雷/递归)

Initboard 初始化棋盘

棋盘初始化是直接将两个扩大一圈的棋盘全部初始化 所以在此都用加大一圈后的参数

Initboard 无返回值 所以在前面用void 

注意分号的使用,有好几次都是卡在一个弱智点不小心在后面加了个分号导致初始化失败

 for(xxxx); <<后面这个分号

#include "game.h" //引用game.h里面的参数以及头文件

void Initboard(char board[ROWS][COLS], int rows, int cols,char set) //棋盘初始化 mine都改为0 show都改为*
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

Displayboard 展示棋盘

打印棋盘

这一部分比较简单,就是需要慢慢调试在不同棋盘下的指引标和列数有没有对齐

对齐小技巧,可在打印行列的标识数字时,可以在%d中间加入3改为(%3d)这样让1位数的标和2位数的标还有3位数的标能更好的对齐 不明白什么意思的可以 锤这里!

注意不要忘记 \n 换行哦

void Displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("\n******************** 扫雷游戏 ********************\n");
	printf("       ");
	for (i = 1; i <= row; i++)//打印列数
	{
		printf("%-3d ", i);
	}
	printf("\n");

	printf("    ");
	for (i = 1; i <= row; i++)//打印列数的指引标
	{

		printf("   v");
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf("%-3d >", i);//打印行数以及指引标
		for (j = 1; j <= col; j++)
		{
			printf("  %c ", board[i][j]);
		}
		printf("\n\n");
	}
	printf("******************** 扫雷游戏 ********************\n\n");

SetMine 埋雷(随机埋雷)

利用while条件为雷的数量,埋好一个减一个

注意埋雷只在mine里面埋 且要限制数值在1 - row 和 1-col 不然有可能埋在棋盘外部!

void SetMine(char mine[ROWS][COLS], int row, int col, int mine_count) 
//埋雷 随机在mine内储存设定地雷数量 将地雷点改为字符1
{
	while (mine_count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;

		if(mine[x][y] == '0')
		{
			mine[x][y] = '1';
			mine_count--;
		}
	}
}

FindMine 扫雷

(因为要用递归所以真正扫雷不是这个函数,而是这个函数嵌套下的AutoFind)

这一段函数主要作用是接收用户输入的数值 进行扫雷 在AutoFind函数之前就先检测一次函数合法性 (autofind里面也会再检测) 顺便判断游戏状态有没有赢

设置了一个win来记录游戏是否胜利,只要扫过一个则win++

当win等于棋盘大小减去雷的数量则为成功,即win == (row * col) - MINE_COUNT)

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{


	int x = 0;
	int y = 0;
	int win = 0;//拆弹次数
	while (1)
	{
		printf("请输入排查的坐标地址\n先行再列_中间空格 例如(1 2)\n>>>");
		scanf_s("%d %d", &x, &y);//接收排查的坐标 
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')//判断x,y这个点有没有扫过
			{
				if (mine[x][y] == '1')//检测是不是地雷
				{
					Displayboard(show, ROW, COL);
					printf("oh,no!踩到地雷被炸飞啦!\n");
					printf("oh,no!踩到地雷被炸飞啦!\n");
					printf("!!!!!!!!!!!!!!!!oh,no!踩到地雷被炸飞啦!!!!!!!!!!!!!!!!!\n");
					break;
				}
				else
				{
                    //进入真正的扫雷阶段
					AutoFind(mine,show,x,y,row,col,&win);//扫雷
					Displayboard(show, ROW, COL);
				}
			}
			else
			{
				printf("坐标已经找过啦,请重新输入...\n");
			}
		}
		else
		{
			printf("坐标输入错误,请重新输入...\n");
		}


		if (win == (row * col) - MINE_COUNT)//拆弹次数满显示的方格总数减去雷则获胜
		{
			printf("!!!!!!!!!!!!!!!!恭喜你取得胜利!!!!!!!!!!!!!!!!\n");
			break;
		}
	}
}

AotoFind(扫雷 可以递归自己实现展开)

函数内出现的get_mine_count是返回到x,y坐标周围的炸弹数量 为整型

 当返回周边有雷 即get_mine_count返回值不等于0

 则将棋盘show中 x,y坐标 的点换为 get_mine_count的 返回值

 若周边没有雷

 则(*win)++ 并递归自己

 只用递归四个角即可,四个角的autofind又会搜索各自周围八个点的雷数量,会包含中间   的,不用将八个点都进行递归

void AutoFind(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int row, int col, int* win)
{

	if (x >= 1 && x <= row && y >= 1 && y <= col)//重复写的原因是为了自动排雷的时候能判断雷的合法性
	{
		if (show[x][y] == ' ' || show[x][y] != '*')//排除掉 已拆 和 已经标记周围炸弹数量
		{
			return;
		}

		if (get_mine_count(mine, x, y) != 0)
		{
			(*win)++;
			show[x][y] = get_mine_count(mine, x, y) + '0';
		}
		else
		{
			(*win)++;
			show[x][y] = ' ';
			AutoFind(mine, show, x - 1, y, row, col, win); //只需要寻找四角的,中间的会被四角的扫描,所以不需要中间的
			AutoFind(mine, show, x + 1, y, row, col, win);
			AutoFind(mine, show, x, y - 1, row, col, win);
			AutoFind(mine, show, x, y + 1, row, col, win);
		}
	}
}

get_mine_count() 计算周围炸弹数量并返回 为int型

static 可用可不用 加了会使这个函数只能在game.c文件内使用

有两种方法

第一种

将包括自己在内9个点判断是不是1(1为雷)

是则count++

第二种 暴力

将周围8个点的内容的ASCII相加最后减去 8 * '0' 才返回int值

static int get_mine_count(char mine[ROWS][COLS], int x, int y) 
//扫雷步骤其中之一 得到x,y点周围八个格子地雷数量并返回整型
{

	int i = 0;
	int j = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{

		for (j = -1; j <= 1; j++)
		{
			if (mine[x + i][y + j] == '1')
			{
				count++;
			}
		}
	}
	 	return count;

//========================================= 以下另一种方法
	//return mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] +
	//	mine[x - 1][y] + mine[x + 1][y] +
	//	mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] - 8 * '0'; 
//========================================= 以上另一种方法

}

到这大概就结束拉

后续可以增加地雷标记啥的

多练习多排故

刚入门C,以上内容可能用词不太妥当,有错误的地方请直接评论区指正 捶 我!谢谢拉!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值