C经典小游戏之扫雷

编译环境:VS022

目录

1.算法思路

2.代码模块

   2.1 game.h

   2.2 game.cpp

   2.3 test.cpp

3.重点分析

4.金句省身


1.算法思路

         主要采用二维数组进行实现,设置两个二维数组,一个打印结果,即为游戏界面显示的效果,一个用来存放雷的位置信息,在判断玩家想要查看的坐标时,通过对比雷的位置信息数组来在显示的数组里来输出信息,在输出信息时,如果该坐标的八个方向上都没有雷,就会以该坐标为中心,展开附近的所有的安全区(类似于bfs),此处呢,我也是用了普通递归和bfs两种方法来展开安全区,详细思路请见代码。

2.代码模块

   2.1 game.h

//game.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include <bits/stdc++.h>
#include <Windows.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2//空出来两行是为了防止数组越界访问
#define COLS COL+2

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

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

//布置雷
void setmine(char board[ROWS][COLS], int row, int col, int difficulty_selection);

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

//递归遍历展开安全区方法1
void dfs(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);

//递归遍历展开安全区方法2
void bfs(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);

2.2 game.cpp

//game.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
using namespace std;
//初始化棋盘
void init(char board[ROWS][COLS], int rows, int cols,char ch)
{
	memset(board,ch, rows * cols * sizeof(char));
}

//打印棋盘
void display(char board[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 ", board[i][j]);
		}
		printf("\n");
	}
}

//布置雷
void setmine(char board[ROWS][COLS], int row, int col,int  difficulty_selection)
{
	int count = difficulty_selection ? 10 : 25;
	while (count)
	{
		
			int x = rand() % row + 1;
			int y = rand() % col + 1;
			if (board[x][y] == '0')
			{
				board[x][y] = '1';
				count--;
			}
	}
}


//(x,y)坐标上雷的数量
int countmine(char mine[ROWS][COLS], int x, int y)
{

	return (mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] +
		mine[x][y + 1] +
		mine[x - 1][y + 1] - 8 * '0');

}

//对于周围不存在雷的坐标加以递归遍历到边界处(存在雷的地方),也就是展开安全区

void bfs(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	int dx[8] = {-1,0,1,1,1,0,-1,-1};//分别表示八个方向的方向数组
	int dy[8] = {1,1,1,0,-1,-1,-1,0};
	//标记数组,用于判断是否已经被访问
	queue<pair<int, int>> m;
	m.push({ x,y });
	while (!m.empty())
	{
		int tempx = m.front().first;
		int tempy = m.front().second;
		m.pop();
		for (int i = 0; i < 8; i++)
		{
			int xx = tempx + dx[i];
			int yy = tempy + dy[i];
			if (xx >= 1 && xx <= ROW && yy >= 1 && yy <= COL && show[xx][yy] == '*')
			{
				int k = countmine(mine, xx, yy);
				if (!k)
				{
					show[xx][yy] = '0';
					m.push({ xx,yy });
				}
				else
					show[xx][yy] = k + '0';
			}
		}
	}
}
/*
void dfs(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	 int count= countmine(mine, x, y);
	 if (!count)//展开安全区
	 {
		 show[x][y] = '0';
		 for (int i = x - 1; i <= x + 1; i++)
			 for (int j = y - 1; j <= y + 1; j++)
			 {
				 if (i >= 1 && i <= ROW && j >= 1 && j <= COL && show[i][j] == '*' && mine[i][j] != 1)
					 dfs(mine, show, i, j);

			 }
	 }
	 else
		 show[x][y] = '0' + count;
}
*/
//排查雷
void findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x, y;
	while (1)
	{
		printf("请输入要排查的目标坐标:->\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				
				printf("很遗憾,你被炸死了\n");
				printf("\n说明:其中数字\"1\"表示雷,数字\"0\"表示非雷\n");
				display(mine,ROW,COL);
				Sleep(5000);
				break;
			}
			else
			{
				//该坐标不是雷
				int count = countmine(mine, x, y);
				show[x][y] = count + '0';
				if(count==0)
				{ 
					//运用两种方法遍历到边界处,前一种试过的已经加了注释
 					//dfs(mine, show, x, y);
					bfs(mine, show, x, y);
				}
				display(show, ROW, COL);
			}
			
		}
		else
		printf("坐标输入有误,请重新输入\n");
	}
}

2.3 test.cpp

//test.cpp
#define _CRT_SECURE_NO_WARNINGS
#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};//表示显示在控制台上的界面
	int difficulty_selection = 0;
again:
		printf("请选择游戏难度->\n");
		printf("********************\n");
		printf("*****EASY---PLAY INPUT THR NUMBER \"1\"\n");
		printf("*****DIFFICULTY ---PLAY INPUT THE NUMBER \"0\"\n");
		printf("********************\n");
		scanf("%d", &difficulty_selection);
		if (difficulty_selection != 0 && difficulty_selection != 1)
		{
			printf("\n选择错误,请重新选择->\n\n");
			goto again;
		}
	//初始化棋盘
	init(mine, ROWS, COLS,'0');
	init(show, ROWS, COLS,'*');
	//打印棋盘
	//display(mine, ROW, COL);
	display(show, ROW, COL);

	//布置雷的信息
	setmine(mine,ROW,COL, difficulty_selection);
	//display(mine, ROW, COL);

	//排查雷
	findmine(mine,show,ROW,COL);
}
int main()
{
	int input;
	srand((unsigned int)time(NULL));
	do {
		system("cls");
		menu();
		printf("请选择:->");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}


	} while (input);


	return 0;
}

3.重点分析

        bfs和dfs的两种递归,对于学过数据结构或者之前有了解的人比较友好,其实这两者本质上是递归的应用,dfs基本和递归没什么区别,简单说一下bfs,大多数情境下,bfs在配合C++的STL中的栈或者队列来使用(当然dfs是可以直接用c来实现的,在代码中也有体现),通过将初始元素压入队,对队中的元素依次进行判断,再将判断后符合入队条件的元素再次入队,当然,也要将该层的元素出队并做好不再参与入队的标记,当队列为空时,即是递归到达了截止条件,向外层的扩展得以结束。

4.金句省身

        有志少年,先谋生而后谋爱,唯有父母和前程不可辜负,想想你往那些虚拟的世界里扔出的金钱,再想想父母的努力,醒醒吧,如果一单648能让你觉醒的看清资本家的嘴脸,那么这单是值得的,如果你的父母依旧辛苦,那么我们长大的意义又在哪里?变好的过程都不太舒服,试试再努力点。加油,为你也为我...

         

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值