C语言实现递归版扫雷游戏实例

 更新时间:2022年01月25日 10:09:46   作者:绅士·永  
大家好,本篇文章主要讲的是C语言实现递归版扫雷游戏实例,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下

思路

清晰的逻辑

为方便将其分为三个文件:text.c(测试) game.c(函数实现) game.h(头文件声明)

在排雷的时候为了方便,我们需要将每一行每一列对应的行数,列数打印出来。

#define LEI 10
#define ROW 10
#define LOW 10
 
#define ROWS ROW+2
#define LOWS LOW+2
//在定义棋盘的长宽时,特意加上2,便于标记行数列数。

菜单

打印的菜单只需要有开始游戏、退出游戏的选项即可

void menu()
{
	printf("*************************************\n");
	printf("************1.开始游戏***************\n");
	printf("************0.退出游戏***************\n");
	printf("*************************************\n");
 
}

棋盘

1.雷盘

2.棋盘

扫雷需要先记录雷的信息再进行排雷,如果使用一个棋盘太过于复杂,所以我们使用两个棋盘,一个用于布置雷,一个用于玩家排雷。

两个棋盘初始化

布置雷的棋盘初始化,将字符‘0’作为非雷,字符‘1’作为雷。

玩家盘将字符‘*’作为还没有扫的地方

    board(arr1, ROWS, LOWS, '0');//雷盘
	board(arr2, ROWS, LOWS, '*');//玩家盘

因为两个的初始化方式不同,所以我们采用传参ret初始化

//初始化棋盘
void board(char arr1[ROWS][LOWS], int rows, int lows, char ret)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < lows; j++)
		{
			arr1[i][j] = ret;
		}
	}

布置雷

布置的雷放置需要随机,所以采用两个随机数来定位坐标。

 
//布置雷
void Get_lei(char arr1[ROWS][LOWS], int row, int low)
{
	int count = LEI;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % low + 1;
		if (arr1[x][y] == '0')
		{
			arr1[x][y] = '1';
			count--;
		}
	}
	//displayboard(arr1, ROW, LOW);//用于测试
}

排雷

当我们输入一个坐标时,我们需要知道这个坐标周围雷的个数,定义一个Get_num函数来获取雷个数。但此时只能获取一个坐标的信息,我们知道一般的扫雷,如果当前坐标雷的个数为0,就会展开,这个过程较为复杂,所以我们使用递归来实现

//玩家盘
static int Get_num(char arr1[ROWS][LOWS],int x, int y)//获得当前坐标周围雷的个数
{
	int count = 0;
	int i = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
			if (arr1[i][j] == '1')
			{
				count++;
			}
		}
 
	}
	return count;
}
//判断是否展开,实现函数
static void Judge(char arr2[ROWS][LOWS], char arr1[ROWS][LOWS], int x, int y)
{
	if (x > 0 && x <= ROW && y > 0 && y <= LOW)
	{
		int ret = Get_num(arr1, x, y);
		if (ret != 0)
			arr2[x][y] = ret + '0';//记录雷的个数
		//递归散开
		else if (arr1[x][y] != ' ')
		{
			arr2[x][y] = '0';
			arr1[x][y] = ' ';
			int i = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					Judge(arr2, arr1, i, j);
				}
			}
		}
		else
		{
			return;
		}
	}
}

判断输赢

输:即每排一次雷,检查一下雷盘对应的信息,如果是雷,就被炸死,如果不是,就继续排雷。

赢:当玩家将所有的非雷的区域都排查出来时,判断为赢。(这里采用一个计数器,没排一次雷计数器就++一下,当计数器与总的非雷的区域数目相同时,判断为赢)

void Out_lei(char arr2[ROWS][LOWS], int row, int low, char arr1[ROWS][LOWS])
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d,%d", &x, &y);
		if (x >= 1 && x <= ROW && y >= 1 && y <= LOW)
		{
			if (arr1[x][y] == '1')
			{
				arr2[x][y] = '#';
				displayboard(arr2, ROW, LOW);//排雷
				printf("遗憾你输了\n");
				break;
			}
			else
			{
				Judge(arr2, arr1, x, y);
				displayboard(arr2, ROW, LOW);//排雷
			}
		}
		else
		{
			printf("输入错误!\n");
		}
 
		//判断扫雷是否赢
		int i = 0, flag = 0;
		for (i = 1; i <= ROW; i++)
		{
			int j = 0;
			for (j = 1; j <= LOW; j++)
			{
				if (arr2[i][j] != '*')
				{
					flag++;
				}
			}
		}
		if (flag == ROW*LOW - LEI)
		{
			printf("你赢了!\n");
			break;
		}
	}
}

text.c实现

#define  _CRT_SECURE_NO_WARNINGS 1
 
#include "game.h"
//菜单
void menu()
{
	printf("*************************************\n");
	printf("************1.开始游戏***************\n");
	printf("************0.退出游戏***************\n");
	printf("*************************************\n");
 
}
 
 
void game()
{
	//初始化棋盘
	char arr1[ROWS][LOWS] = { 0 };//雷盘
	char arr2[ROWS][LOWS] = { 0 };//玩家盘
	board(arr1, ROWS, LOWS, '0');
	board(arr2, ROWS, LOWS, '*');
	//打印棋盘
	//displayboard(arr1, ROW, LOW);//布置雷
	displayboard(arr2, ROW, LOW);//排雷
	//布置雷
	Get_lei(arr1,ROW,LOW);
	//排雷
	Out_lei(arr2,ROW,LOW, arr1);
 
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
		{
			printf("扫雷\n");
			game();
			break;
		}
		case 0:
		{
			printf("退出游戏\n");
			break;
		}
		default:
		{
			printf("选择错误\n");
			break;
		}
		}
	} while (input);
 
	return 0;
}

game.c实现

#define  _CRT_SECURE_NO_WARNINGS 1
 
#include "game.h"
 
//初始化棋盘
void board(char arr1[ROWS][LOWS], int rows, int lows, char ret)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < lows; j++)
		{
			arr1[i][j] = ret;
		}
	}
}
 
//打印棋盘
void displayboard(char arr1[ROWS][LOWS], int row, int low)
{
	printf("<———扫雷游戏———>\n");
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		if (i == 1)
		{
			for (j = 0; j <= low; j++)
			{
				printf("%2d ", j);
			}
			printf("\n");
		}
 
		for (j = 1; j <= low; j++)
		{
			if (j == 1)
			{
				printf("%2d ", i);
			}
			
			printf("%2c ", arr1[i][j]);
		}
		printf("\n");
	}
	printf("<———扫雷游戏———>\n");
 
}
 
//布置雷
void Get_lei(char arr1[ROWS][LOWS], int row, int low)
{
	int count = LEI;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % low + 1;
		if (arr1[x][y] == '0')
		{
			arr1[x][y] = '1';
			count--;
		}
	}
	//displayboard(arr1, ROW, LOW);
}
//玩家盘
static int Get_num(char arr1[ROWS][LOWS],int x, int y)
{
	int count = 0;
	int i = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
			if (arr1[i][j] == '1')
			{
				count++;
			}
		}
 
	}
	return count;
}
//判断是否展开,实现函数
static void Judge(char arr2[ROWS][LOWS], char arr1[ROWS][LOWS], int x, int y)
{
	if (x > 0 && x <= ROW && y > 0 && y <= LOW)
	{
		int ret = Get_num(arr1, x, y);
		if (ret != 0)
			arr2[x][y] = ret + '0';
		//递归散开
		else if (arr1[x][y] != ' ')
		{
			arr2[x][y] = '0';
			arr1[x][y] = ' ';
			int i = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					Judge(arr2, arr1, i, j);
				}
			}
		}
		else
		{
			return;
		}
	}
}
void Out_lei(char arr2[ROWS][LOWS], int row, int low, char arr1[ROWS][LOWS])
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d,%d", &x, &y);
		if (x >= 1 && x <= ROW && y >= 1 && y <= LOW)
		{
			if (arr1[x][y] == '1')
			{
				arr2[x][y] = '#';
				displayboard(arr2, ROW, LOW);//排雷
				printf("遗憾你输了\n");
				break;
			}
			else
			{
				Judge(arr2, arr1, x, y);
				displayboard(arr2, ROW, LOW);//排雷
			}
		}
		else
		{
			printf("输入错误!\n");
		}
 
		//判断扫雷是否赢
		int i = 0, flag = 0;
		for (i = 1; i <= ROW; i++)
		{
			int j = 0;
			for (j = 1; j <= LOW; j++)
			{
				if (arr2[i][j] != '*')
				{
					flag++;
				}
			}
		}
		if (flag == ROW*LOW - LEI)
		{
			printf("你赢了!\n");
			break;
		}
	}
}
 
 

game.h实现

#pragma once
 
#include <stdio.h>
#include <stdlib.h>
 
#define LEI 10
#define ROW 10
#define LOW 10
 
#define ROWS ROW+2
#define LOWS LOW+2
 
//初始化棋盘
void board(char arr1[ROWS][LOWS],int rows,int lows,char ret);
 
//打印棋盘
void displayboard(char arr1[ROWS][LOWS], int row, int low);
 
//布置雷
void Get_lei(char arr1[ROWS][LOWS], int row, int low);
//玩家盘
void Out_lei(char arr2[ROWS][LOWS], int row, int low, char arr1[ROWS][LOWS]);

递归部分详解

递归条件:1.有停止的条件。2.每一次递归都会向这个条件靠拢。

那么这里的停止条件是什么呢?

递归:当返回雷的个数为0时,就符合继续递归的条件,我们需要将当前坐标周围的点全部排除。且需要将已经排查了的坐标做一个标记,否则就会不停的排查下去,就会形成死递归。所以

停止条件:当前这个坐标是已被排查过的,就停止递归。

因为每一次排查都会的一个标记,所以这就是那个不断向停止条件靠拢的过程。

//判断是否展开,实现函数
static void Judge(char arr2[ROWS][LOWS], char arr1[ROWS][LOWS], int x, int y)
{
	if (x > 0 && x <= ROW && y > 0 && y <= LOW)
	{
		int ret = Get_num(arr1, x, y);
		if (ret != 0)
			arr2[x][y] = ret + '0';
		//递归散开
		else if (arr1[x][y] != ' ')
		{
			arr2[x][y] = '0';//玩家盘
			arr1[x][y] = ' ';//雷盘
			int i = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					Judge(arr2, arr1, i, j);
				}
			}
		}
		else
		{
			return;
		}
	}
}

总结

到此这篇关于C语言实现递归版扫雷游戏实例的文章就介绍到这了,更多相关C语言递归版扫雷内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中引用处理的基本方法

    C++中引用处理的基本方法

    引用不是新定义了一个变量,而是给已经存在的变量取了一个别名,编译器不会为引用变量开辟内存空间,他和他引用的变量共用一块内存空间,下面这篇文章主要给大家介绍了关于C++中引用处理的基本方法,需要的朋友可以参考下
    2022-12-12
  • c语言和c++语言中const修饰的变量区别浅析

    c语言和c++语言中const修饰的变量区别浅析

    这篇文章主要给大家介绍了关于c语言和c++语言中const修饰的变量区别的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-02-02
  • 使用C语言实现内存池的示例代码

    使用C语言实现内存池的示例代码

    所谓内存池,顾名思义和线程池的设计原理是一样的,为了减少频繁申请释放内存而带来的资源消耗,减少释放内存后产生的内存碎片,下面我们就来看看如何使用C语言实现内存池吧
    2024-02-02
  • C语言实现密码程序

    C语言实现密码程序

    这篇文章主要为大家详细介绍了C语言实现密码程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-02-02
  • C语言实现BMP图像细化处理

    C语言实现BMP图像细化处理

    这篇文章主要为大家详细介绍了C语言实现BMP图像细化处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • 简要解读C++的动态和静态关联以及虚析构函数

    简要解读C++的动态和静态关联以及虚析构函数

    这篇文章主要介绍了简要解读C++的动态和静态关联以及虚析构函数,析构函数在C++编程中平时并不是太常用,需要的朋友可以参考下
    2015-09-09
  • c++入门必学库函数sort的基本用法

    c++入门必学库函数sort的基本用法

    Sort函数包含在头文件为#include<algorithm>的c++标准库中,调用标准库里的排序方法可以不必知道其内部是如何实现的,只要出现我们想要的结果即可,下面这篇文章主要给大家介绍了关于c++入门必学库函数sort的基本用法,需要的朋友可以参考下
    2022-11-11
  • 关于C++读入数字按位取出与进制转换问题(典型问题)

    关于C++读入数字按位取出与进制转换问题(典型问题)

    这篇文章主要介绍了关于C++读入数字按位取出与进制转换问题,是一个非常典型的问题,本文通过实例举例给大家介绍的非常详细,需要的朋友可以参考下
    2020-02-02
  • C语言求素数的几种方式总结

    C语言求素数的几种方式总结

    这篇文章主要介绍了C语言求素数的几种方式总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • 详解C语言面向对象编程中的封装

    详解C语言面向对象编程中的封装

    这篇文章主要为大家详细介绍了C语言面向对象编程中的封装,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03

最新评论