【C语言】09.扫雷游戏的实现(含递归展开版)

一、游戏的分析与设计

目标:实现扫雷游戏的基本功能

1.可以通过菜单控制继续玩还是退出游戏

2.随即设置棋盘的类型,本文为9*9

3.随随机布置任意的雷,本文为10

4.通过输入坐标进行排雷

  • 如果不是雷显示周围的雷数
  • 是雷则被炸死,结束游戏
  • 直到所有的雷被找出即为胜利

初始界面:                                   排雷界面:                                           排雷失败界面:

                                         

扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些信息。

因为我们需要在9*9的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个9*9的数   组来存放信息。

我们可以让‘1’表示为雷,‘0’表示不是雷。

                           未布置雷的棋盘                                                  布置雷的棋盘

                     

但是如果我们要访问边沿元素有几个雷的时候就很难实现,所以我们可以将棋盘扩大一圈,变成11*11这样访问边缘元素周围雷的个数也很方便了。

如果访问的位置并不是雷,那么我们需要将他周围雷的个数计算并打印出来,那么这个个数要存在哪里呢?

如果存放在布置雷的棋盘中就会导致概念混淆,这样的话我们设置一个数组专门存放雷的信息,而另一个数组专门用来打印给人以参考。       

对应的数组应该是:

char mine[11][11] = {0};//⽤来存放布置好的雷的信息 
char show[11][11] = {0};//⽤来存放排查出的雷的个数信息 

二、代码实现:

2.1 test.c

由于要实现可以多次玩,由菜单控制退出与否,我们使用了一个do~while循环

进入之后要给用户选择的空间,因此调用一个menu函数来显示菜单,接下来通过用户所输入的数字判断下一步要进行的操作,因此用switch语句作为多分支选择。

2.2 mine.c

本文件在用户输入1后进入即可进入。

首先我们需要对main.c中定义的两个数组进行初始化,将对用户所展示的棋盘全部初始化为‘*’,将进行埋雷的棋盘(下文简称为"雷盘”)初始化为‘0’。

接下来我们将雷盘进行埋雷操作,由于我们要保证雷的位置随机,因此我们需要调用rand()赋随机值(具体已在主页随机数游戏中讲过原理)。

再接下来就可以让用户进行找雷了。当找到的不是雷的时候就会显示附近雷的个数。

最后如果用户成功排雷,那么就获得胜利。

2.3 mine.h  

此文件用来存放函数的声明。

2.4 具体实现            

//mine.h
#pragma once

#include <stdio.h>
#include<time.h>
#include<stdlib.h>
#include<windows.h>

#define ROW 9   //可见的行数
#define COL 9//可见列数

#define ROWS ROW+2  //设置的底板,不可见,行数
#define COLS COL+2    //设置的底板,不可见,列数

#define MINE 10  //雷的个数


void init(char board[ROWS][COLS], int row, int col, char ch);//初始化棋盘

void display(char showboard[ROWS][COLS], int row, int col);//打印棋盘

void setmine(char mine[ROWS][COLS], int row, int col);//埋雷

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

int GetMineCount(char mine[ROWS][COLS], int x, int y);//统计个数

void ExplodeBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pwin);//爆炸展开

void Signmine(char board[ROWS][COLS], int row, int col);//标记



//mine.c
#define _CRT_SECURE_NO_WARNINGS 

#include"mine.h"

//初始化棋盘
void init(char board[ROWS][COLS], int row, int col, char ch)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ch;
		}
	}
}
//展示棋盘
void display(char showboard[ROWS][COLS], int row, int col)
{
	printf("  ----开始游戏----\n");
	for (int i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", showboard[i][j]);
		}
		printf("\n");
	}

}


//埋雷
void setmine(char mine[ROWS][COLS], int row, int col)
{
	int count = MINE;//初始化雷的个数
	while (count)
	{
		int x = 0;
		int y = 0;
		x = rand() % row + 1;//1-9
		y = rand() % col + 1;//1-9
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

//找雷
void findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	char ch;
	while (win < row * col - MINE)
	{
		
		printf("请输入坐标来排查;>");
		scanf("%d %d", &x, &y);

		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				display(mine, ROW, COL);
				break;
			}
			else
			{
				//爆炸展开
				win++;
				ExplodeBoard(mine, show, row, col, x, y, &win);
				//打印棋盘
				display(show, ROW, COL);
				printf("需要标注地雷输入:Y,不需要则输入:N\n");
				//清空缓冲区
				while ((getchar()) != '\n');

				scanf("%c", &ch);
				if (ch == 'Y')
				{
					//标记雷的位置
					Signmine(show, ROW, COL);
				}
			}
		}
		else
			printf("输入的数据非法,请重新输入!\n");		
	}
	if (win == row * col - MINE)
	{
		printf("恭喜你,排雷成功\n");
		display(mine, ROW, COL);
	}
}

//统计个数
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] +mine[x - 1][y] +mine[x - 1][y + 1] 
			+mine[x][y - 1] +mine[x][y + 1] +mine[x + 1][y + 1] 
			+mine[x + 1][y] +mine[x + 1][y + 1] 
			- 8 * '0';
}


//爆炸展开
void ExplodeBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pwin)
{
	//限制条件
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		//计算该位置周围雷的个数
		int count = GetMineCount(mine, x, y);
		if (count == 0)
		{
			//把该位置变成空格
			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] == '*')
					{
						ExplodeBoard(mine, show, row, col, i, j, pwin);
						*pwin++;
					}
				}
			}
		}
		else
		{
			show[x][y] = count + '0';
		}
	}
}

//标记雷的函数
void Signmine(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入要标记的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x][y] == '*')
			{
				board[x][y] = '!';
				break;
			}
			else
			{
				printf("该位置不能被标记,请重新输入:\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入:\n");
		}
	}
}

//test.c
#define _CRT_SECURE_NO_WARNINGS 
#include"mine.h"

enum { Exit, Play };
//菜单
void menu()
{
	printf("*******************\n");
	printf("***   1. Play   ***\n");
	printf("***   0. Exit   ***\n");
	printf("*******************\n");
}

//游戏内容
void game(void)
{
	//声明数组
	char show[ROWS][COLS];//可见
	char mine[ROWS][COLS];//埋雷

	//初始化两个数组
	init(show, ROWS, COLS, '*');
	init(mine, ROWS, COLS, '0');

	//展示棋盘
	display(show, ROW, COL);

	//埋雷
	setmine(mine, ROW, COL);

	//找雷
	findmine(mine, show, ROW, COL);

}
int main()
{
	srand((unsigned int)time(NULL));//初始化种子
	int input = 0;
	do
	{
		menu();//菜单
		printf("请选择:>");
		scanf("%d", &input);

		switch (input)
		{
		case Play:
			game();//进入游戏
			break;
		case Exit:
			printf("----游戏结束----\n");
			break;
		default:
			printf("----请重新输入----\n");
			break;
		}
	} while (input);

	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值