当前位置:主页 > 软件编程 > C代码 >

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

时间:2023-01-31 09:04:33 | 栏目: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;
		}
	}
}

总结

您可能感兴趣的文章:

相关文章