C语言——扫雷之童年的回忆

31 篇文章 2 订阅

目录

导语: 

1. 游戏界面和流程

2. 代码实现

2.1 思维导图

2.2 游戏菜单 

2.3 雷盘及展示区

2.31 初始化

2.32 打印显示区

2.4 置雷

2.5 排雷

2.6 赢的判定

3. 完整代码

头文件game.h --> 函数声明

test.c文件 --> 逻辑测试

game.c文件 --> 功能实现

4.结语


导语: 

  在大家的记忆里是否有扫雷、蜘蛛纸牌、植物大战僵尸的这样的游戏呢?玩过这些游戏的小屁孩,想必也都长大了吧。还记得当时玩扫雷这个游戏的时候,不知道游戏逻辑,全凭运气,那结果肯定是,踩雷小王子。
  那这次,我们就用代码来实现这个扫雷游戏,刨析内部原理,重拾童年回忆吧(当然,拾起过程会有点艰难,要做好准备哦)

1. 游戏界面和流程

我们先来看我们要实现的结果(如图):

2. 代码实现

2.1 思维导图

2.2 游戏菜单 

初始逻辑与上一篇文字三子棋一样

链接:三子棋https://blog.csdn.net/Dirty_artist/article/details/127780924?spm=1001.2014.3001.5502

我们在test.c文件中,设置main函数,将逻辑测试函数放入test()函数里面

int main()
{
	test();
	return 0;
}

然后我们开始设置菜单,将此功能写一个函数完成,函数之前,最好是问一下自己三个问题

①函数用来干嘛?

②是否需要传参?

③是否需要返回值?

void menu()
{
	printf("|==========================|\n");
	printf("|========= 1.play =========|\n");
	printf("|========= 0.exit =========|\n");
	printf("|==========================|\n");
}
void test()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		if (input == 1)
		{
			printf("扫雷开始!\n");
			//break;
		}
		else if (input == 0)
		{
			printf("退出游戏!\a\n");
			break;
		}
		else
		{
			printf("选择错误,重新选择!\n");
		}
	} while (input);
}

2.3 雷盘及展示区

2.31 初始化

我们怕排雷的时候,数组越界,所以就将棋盘扩大一圈,这样就不用担心在判定的时候数组越界了

 所以我们在game.h文件里面先声明雷盘大小

#define Row 9
#define Col 9
#define Rows Row+2
#define Cols Col+2

然后,将其初始化,雷区里面先全部初始化为'0',展示区初始化为'*'

void init_board(char board[Rows][Cols], int rows, int cols, char set)
{
	int i = 0;
	for (i = 1; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

2.32 打印显示区

因为扫雷游戏的时候,我们看不到雷区,所以,只显示展示区

//展示棋盘
void display_board(char board[Rows][Cols], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("---------------------\n");
	for (i = 0; i <= row; 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");

}

效果如图:

 2.4 置雷

置雷要求:

①生成的雷要有随机性;

②设置的雷要在9*9的格子里面;

③一个格子只能置一个雷。

我们先去将雷盘全部初始化为'0',那我们置雷,就设为'1'。

//置雷
void set_mine(char board[Rows][Cols], int row, int col)
{
	
	int count = Easy;
	while (count != 0)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

2.5 排雷

整体思想如图:

代码如下:

//排雷
void fine_mine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (1)
	{
		printf("输入查找的坐标: ");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该坐标已排查\n");
				continue;
			}
			if (mine[x][y] == '1')
			{
				printf("踩雷,游戏结束\n");
				display_board(mine, Row, Col); 
				Sleep(2500);
				system("cls");//清空屏幕
				break;
			}
			else
			{
				//排空
				find_safe_place(mine, show, x, y,row,col,&win);
				system("cls");
				display_board(show, Row, Col); 
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}	
	}
}

 这里唯一有难点的地方在于递归的排空。

 我们将查找过的区域,标记为C,防止无限查找,如果周围无雷,将show区域标记为'  '(空格)

有雷,那我们统计周围雷的数量。

递归排空:

//排空
void find_safe_place(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)
	{
		mine[x][y] = 'C';//排查标记
		if (get_mine_count(mine, x, y))//是否有雷
		{
			show[x][y] = get_mine_count(mine, x, y) + '0';//显示雷的数量
		}
		else
		{
			show[x][y] = ' ';
			int i = 0;
			for (i = x-1; i <= x+1; i++)
			{
				int j = 0;
				for (j = y-1; j <= y+1; j++)
				{
					if(show[i][j] == '*')
					find_safe_place(mine, show, i, j,row,col,win);
				}
			}
		}
		(*win)++;
	}
}

统计雷的数量:

因为我们里面放的是char类型的字符,所以返回的时候,还需要减去8*'0'换算。

 代码如下:

//周围雷的数量
int get_mine_count(char mine[Rows][Cols], int x, int y)
{
	int i = 0;
    int count = 0;
    for (i = -1; i <= 1; i++)
    {
	int j = 0;
		for (j = -1; j <= 1; j++)
		{
			if (mine[x + i][y + j] == '1')
			{
			count++;
			}
		}
	}
	return count;
}

2.6 赢的判定

失败很简单,只要踩雷,就是失败;那么赢,如何计算?

比如说,我们的地图是9*9,设置了9个9个雷,那么,如果我们最后地图还剩9个格子,那么,游戏就是胜利了,因为剩下的9个都是雷。

if (win == row * col - Easy)
			{

				printf("胜利\n");
				display_board(mine, Row, Col);
				Sleep(2500);
				system("cls");
				break;
			}

3. 完整代码

头文件game.h --> 函数声明

#pragma once

#define Row 9
#define Col 9
#define Rows Row+2
#define Cols Col+2
#define Easy 3
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<Windows.h>

//初始化棋盘
void init_board(char board[Rows][Cols],int rows,int cols,char set);

//展示棋盘
void display_board(char board[Rows][Cols],int row,int col);

//置雷
void set_mine(char board[Rows][Cols], int row, int col);

//排雷
void fine_mine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col);

test.c文件 --> 逻辑测试

#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
#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 };
	//初始化
	init_board(mine, Rows, Cols, '0');
	init_board(show, Rows, Cols, '*');
	//打印
	display_board(show, Row, Col);
	//置雷
	set_mine(mine, Row, Col);
	//display_board(mine, Row, Col);
	//排雷
	fine_mine(mine, show, Row, Col);
}
void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		if (input == 1)
		{
			printf("扫雷开始!\n");
			game();
			//break;
		}
		else if (input == 0)
		{
			printf("退出游戏!\a\n");
			break;
		}
		else
		{
			printf("选择错误,重新选择!\n");
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

game.c文件 --> 功能实现

#define _CRT_SECURE_NO_WARNINGS 1
#pragma warning(disable:6031)
#include"game.h"

//初始化棋盘
void init_board(char board[Rows][Cols], int rows, int cols, char set)
{
	int i = 0;
	for (i = 1; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

//展示棋盘
void display_board(char board[Rows][Cols], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("---------------------\n");
	for (i = 0; i <= row; 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 set_mine(char board[Rows][Cols], int row, int col)
{
	
	int count = Easy;
	while (count != 0)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

//周围雷的数量
int get_mine_count(char mine[Rows][Cols], int x, int y)
{
	int i = 0;
    int count = 0;
    for (i = -1; i <= 1; i++)
    {
	int j = 0;
		for (j = -1; j <= 1; j++)
		{
			if (mine[x + i][y + j] == '1')
			{
			count++;
			}
		}
	}
	return count;
}

//排空
void find_safe_place(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)
	{
		mine[x][y] = 'C';//排查标记
		if (get_mine_count(mine, x, y))//是否有雷
		{
			show[x][y] = get_mine_count(mine, x, y) + '0';//显示雷的数量
		}
		else
		{
			show[x][y] = ' ';
			int i = 0;
			for (i = x-1; i <= x+1; i++)
			{
				int j = 0;
				for (j = y-1; j <= y+1; j++)
				{
					if(show[i][j] == '*')
					find_safe_place(mine, show, i, j,row,col,win);
				}
			}
		}
		(*win)++;
	}
}

	//排雷
	void fine_mine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col)
	{
		int x = 0;
		int y = 0;
		int win = 0;
		while (1)
		{
			printf("输入查找的坐标: ");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				if (show[x][y] != '*')
				{
					printf("该坐标已排查\n");
					continue;
				}
				if (mine[x][y] == '1')
				{
					printf("踩雷,游戏结束\n");
					display_board(mine, Row, Col); 
					Sleep(2500);
					system("cls");//清空屏幕
					break;
				}
				else
				{
					//排空
					find_safe_place(mine, show, x, y,row,col,&win);
					system("cls");
					display_board(show, Row, Col); 
				}
			}
			else
			{
				printf("坐标非法,重新输入\n");
			}
			if (win == row * col - Easy)
			{

				printf("胜利\n");
				display_board(mine, Row, Col);
				Sleep(2500);
				system("cls");
				break;
			}
		}
	}

4.结语

  关于扫雷,重难点在于递归排空,如果想象不出,可以尝试拿笔自己在草稿纸上推演。过程中可能会出现程序能运行但没按逻辑来(自己写了三遍,出现最多的问题就是,不报错,但是没按自己的逻辑来,这是最难受的),这就需要自己慢慢调试,看看程序的执行流程,然后一步一步改bug。这其实还有优化的空间,可以自己添加一些趣味项目,这可以下去再拓展拓展。
  好了,本次分享就到这里,有帮助的话,不妨点个赞再走。
  下机,再见!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加法器+

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值