扫雷小游戏等级

扫雷游戏

介绍:回想年少时,在琢磨困难版扫雷(100 X 100),现在居然开始琢磨如何写出这样的代码来,真是时间岁月不饶人啊(微笑),好了,让我们继续吧;

  • 现在我们需要从头梳理一遍实现过程。
  • 首先我们要扫雷,首先需要两个棋盘,一个棋盘用来展示扫雷的页面,另一个棋盘用来存放“ 雷 ”,这两个棋盘间最好有联系,所以这两个棋盘用字符二维数组,棋盘初始化最好不用 (空格),这样打印的棋盘啥也看不到,所以封面的棋盘打印‘*’,另一个棋盘,不是雷用‘0’,是雷用‘1’表示;
    如图:
    这段时程序开始的设计:
int main()
{
    
    
//如果要埋雷,那肯定需要随机值的,随机埋雷。
	srand((unsigned int)time(NULL));
	int i = 0;
	do
	{
    
    
		menu();
		printf("请选择:");
		scanf("%d", &i);
		switch (i)
		{
    
    
		case 1:
			printf("进入游戏\n");
			game();//进入扫雷的游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (i);

	return 0;
}

有menu()

void menu()
{
    
    
	printf("***********************\n");
	printf("***********************\n");
	printf("**** 1.play  0.exit****\n");
	printf("***********************\n");
}

创建出两个二维数组,设置棋盘

然后game()函数:
需要两组数组,注意:如果我们用简单模式的话也就是(9X9)的数组,我们可以直接#define定义出数组的行和列,如

#define BOW 9 //是定义行
#define COL 9 //是定义列

#define BOWS BOW+2 //这里在原有的行上向外在多打印一行
#define COLS COL+2 //在原来的列上向外在多打印一列,在这里的作用在后续慢慢讲解

	char mine[BOWS][COLS] = {
    
     0 };
	char show[BOWS][COLS] = {
    
     0 };
	//初始化棋盘
	Inboard(mine,BOWS,COLS,'0');
	Inboard(show,BOWS,COLS,'*');//函数

初始化棋盘

Inboard()函数代码实现

void  Inboard(char board[BOWS][COLS], int bows, int cols,char set)
{
    
    
	int i = 0;
	int j = 0;
	for (i = 0; i < bows; i++)
	{
    
    
		for (j = 0; j < cols; j++)
		{
    
    
			board[i][j] = set;//直接将接收的字符进行初始化
		}
	}
}

既然初始化完成,在打印出两个棋盘;
用这个函数

打印出棋盘

Display();//函数

	Display(show, BOW, COL);
	Display(mine, BOW, COL);

代码实现:

void Display(char board[BOWS][COLS], int bow, int col)
{
    
    
	int i = 0;
	int j = 0;
	for (i = 0; i <= col; i++)
	{
    
    
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= bow; i++)
	{
    
    
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
    
    
			
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

在这里插入图片描述

埋雷

既然棋盘已经初始化完成,那就开始在mine()进行埋雷,需要mine数组,行和列,这里只需要在9 X 9 的数组里进行埋雷,在原有的字符‘0’上改成‘1’,这里需要随机值;

扫描二维码关注公众号,回复: 16138510 查看本文章

Setray(mine,BOW,COL);

void Setray(char board[BOWS][COLS], int bow, int col)
{
    
    
	
	int count = ray;
	printf("--------扫雷游戏--------\n");
	while (count)
	{
    
    
		int x = rand() % bow + 1;
		int y = rand() % col + 1;
		if(x>=1 && x<=bow && y>=1 && y<=col)
		{
    
    
			if (board[x][y] == '0')
			{
    
    
				board[x][y] = '1';
				count--;
			}
		}
	}
}

在将埋好的雷在把棋盘打印出来:
在这里插入图片描述
在这里埋了10个雷,在这里注意,我们这只是打印出来看一下,不是真的打印出来,在游戏测试的时候不需要打印出来,还有在埋雷的时候注意要行在(1-9)之间,列也在(1-9)之间。
在这里需要说明一下为什么需要#define定义出两个不同的行和列,因为是如图下所示:
我们初始化棋盘是这整个棋盘,但是在如图红圈内随机埋雷,因为如果你要计算一个位置的周围的雷的个数,就需要如此。
在这里插入图片描述

排雷

埋好了雷,就可以开始排雷了,如果要排雷,就需要输入要排查的坐标,排查的用mine棋盘,在计算出其周围的雷的个数在打印在show棋盘上,如果这个坐标在mine棋盘上是‘1’则挖到雷了,如果不是,则打印出该坐标周围一圈的雷的个数的字符。
想要赢的条件,则是排完所有的雷,则下完 9X9的数组减去地雷。

int Countrow(char mine[BOWS][COLS], int x, int y)
{
    
    
	
	return (mine[x - 1][y] + mine[x - 1][y - 1] 
		+ mine[x - 1][y + 1] + mine[x][y - 1] + 
		mine[x][y + 1] + mine[x + 1][y] + mine[x + 1][y + 1] + 
		mine[x + 1][y - 1])-8*'0';

}

void  Rowray(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col)
{
    
    
	
		int x = 0;
		int y = 0;
		int win = 0;

		while (win< bow*col - ray)
		{
    
    
			printf("请输入一个要排查的坐标:");
			scanf("%d%d", &x, &y);
			if (x >= 1 && x <= bow && y >= 1 && y <= col)
			{
    
    
				
				 if (mine[x][y] == '1')
				 {
    
    
					printf("不好意思,你被炸死了;\n");
					Display(mine, BOW, COL);
					break;
				 }
				 else
				 {
    
    
					 //统计周围的雷的个数
					 int ret = Countrow(mine, x, y);
					 show[x][y] = ret + '0';
					 Display(show, BOW, COL);
					 win++;
				 }
			}
			else
				printf("输入坐标错误,请重新输入:");
			
		}

		if (win == bow * col - ray)
		{
    
    
			printf("排雷成功\n");
			Display(show, BOW, COL);
		}

}

优化版排雷

虽然这样可以完成扫雷,但是在排雷的可以优化成和扫雷小游戏一样,该函数实现的功能是:若排查的位置周围没有雷,则向外四周爆炸式展开直至遇到地雷的坐标时停下来。
类似于一个递归函数;
优化版:炸金花式展开。
总有几点注意:
1.在该点的坐标不是雷。
2.在该点的坐标周围没有雷。
3.还有该坐标不是‘ ’。
以上几点都满足则,将该点改成‘ ’;

在这里插入图片描述
那如何实现该递归函数?当排查的位置没有雷且该位置周围没有雷,则向外展开一圈的8个坐标,然后看着8个坐标是否可以逐个向其自身再向外展开一圈,这一次就用到递归调用。

具体实现:若每次判断一个坐标上没有雷且周围一圈也没有雷,则向我们展示的show棋盘上未知的字符‘’,改成空格,然后再之后的递归调用前给一个判断,如果这个将要被调用递归的坐标存放的是‘’,才能进行下一步,否则将跳出此次递归。
当在进行操作是有一点注意:非法访问内存空间,出现该问题的原因:数组的越界访问,当向外扩张的递归,向外矿张时,当最外一圈的坐标,无法起到限制作用,无法阻止其向外展开。所以会出现越界访问!!!如何解决:在进行下一次递归调用时加一个限定条件:要再次进行递归调用必须要在原来位置存放的是‘*’的条件下:
下面有具体代码展示:

void explod(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col, int x, int y)
{
    
    
	if (x >= 1 && x <= bow && y >= 1 && y <= col)
	{
    
    
		int count = Countrow(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] == '*')
					{
    
    
						explod(mine, show, bow, col, i, j);
					}
				}
			}
		}
		else
		{
    
    
			show[x][y] = count + '0';
		}
	}
}


void  Rowray(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col)
{
    
    

	int x = 0;
	int y = 0;
	int win = 0;

	while (win < bow * col - ray)
	{
    
    
		printf("请输入一个要排查的坐标:");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= bow && y >= 1 && y <= col)
		{
    
    

			if (mine[x][y] == '1')
			{
    
    
				printf("不好意思,你被炸死了;\n");
				Display(mine, BOW, COL);
				break;
			}
			else
			{
    
    
				explod(mine, show, bow, col, x, y);//炸金花式展开;
				/*system("cls");*/
				Display(show, bow, col);
			}
		}
		else
			printf("输入坐标错误,请重新输入:");

	}

	if (win >= bow * col - ray)
	{
    
    
		printf("排雷成功\n");
		Display(show, BOW, COL);
	}
	else
		printf("排雷失败\n");

}

最后整体的代码如下:

shaolei.h

#pragma once

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


#define ray 10

#define BOW 9
#define COL 9

#define BOWS BOW+2
#define COLS COL+2

//初始化棋盘
void  Inboard(char board[BOWS][COLS], int bows, int cols,char set);

//打印棋盘
void Display(char board[BOWS][COLS], int bow, int col);

//布置雷、
void Setray(char board[BOWS][COLS], int bow, int col);

//排雷
void Rowray(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col);

void Disshow(char show[BOWS][COLS], int bow, int col, int ret);

saolei.c

#define _CRT_SECURE_NO_WARNINGS 1 

#include "saolei.h"


void  Inboard(char board[BOWS][COLS], int bows, int cols,char set)
{
    
    
	int i = 0;
	int j = 0;
	for (i = 0; i < bows; i++)
	{
    
    
		for (j = 0; j < cols; j++)
		{
    
    
			board[i][j] = set;
		}
	}
}

void Display(char board[BOWS][COLS], int bow, int col)
{
    
    
	int i = 0;
	int j = 0;
	for (i = 0; i <= col; i++)
	{
    
    
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= bow; i++)
	{
    
    
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
    
    
			
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	
}
void Setray(char board[BOWS][COLS], int bow, int col)
{
    
    
	
	int count = ray;
	printf("--------扫雷游戏--------\n");
	while (count)
	{
    
    
		int x = rand() % bow + 1;
		int y = rand() % col + 1;
		if(x>=1 && x<=bow && y>=1 && y<=col)
		{
    
    
			if (board[x][y] == '0')
			{
    
    
				board[x][y] = '1';
				count--;
			}
		}
	}
}

int Countrow(char mine[BOWS][COLS], int x, int y)
{
    
    
	
	return (mine[x - 1][y] + mine[x - 1][y - 1] 
		+ mine[x - 1][y + 1] + mine[x][y - 1] + 
		mine[x][y + 1] + mine[x + 1][y] + mine[x + 1][y + 1] + 
		mine[x + 1][y - 1])-8*'0';

}

void explod(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col, int x, int y)
{
    
    
	if (x >= 1 && x <= bow && y >= 1 && y <= col)
	{
    
    
		int count = Countrow(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] == '*')
					{
    
    
						explod(mine, show, bow, col, i, j);
					}
				}
			}
		}
		else
		{
    
    
			show[x][y] = count + '0';
		}
	}
}


void  Rowray(char mine[BOWS][COLS], char show[BOWS][COLS], int bow, int col)
{
    
    

	int x = 0;
	int y = 0;
	int win = 0;

	while (win < bow * col - ray)
	{
    
    
		printf("请输入一个要排查的坐标:");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= bow && y >= 1 && y <= col)
		{
    
    

			if (mine[x][y] == '1')
			{
    
    
				printf("不好意思,你被炸死了;\n");
				Display(mine, BOW, COL);
				break;
			}
			else
			{
    
    
				explod(mine, show, bow, col, x, y);//炸金花式展开;
				/*system("cls");*/
				Display(show, bow, col);
			}
		}
		else
			printf("输入坐标错误,请重新输入:");

	}

	if (win >= bow * col - ray)
	{
    
    
		printf("排雷成功\n");
		Display(show, BOW, COL);
	}
	else
		printf("排雷失败\n");

}

game.h

#define _CRT_SECURE_NO_WARNINGS 1 

#include "saolei.h"


void menu()
{
    
    
	printf("***********************\n");
	printf("***********************\n");
	printf("**** 1.play  0.exit****\n");
	printf("***********************\n");
}

void game()
{
    
    
	char mine[BOWS][COLS] = {
    
     0 };
	char show[BOWS][COLS] = {
    
     0 };

	Inboard(mine,BOWS,COLS,'0');
	Inboard(show,BOWS,COLS,'*');

	//Display(show, BOW, COL);
	/*Display(mine, BOWS, COLS);*/

	//布置雷
	Setray(mine,BOW,COL);
	Display(show, BOW, COL);
	Display(mine, BOW, COL);
	

	//排雷
	Rowray(mine,show,BOW,COL);
	//将雷打印在show棋盘上
	
	
}


int main()
{
    
    
	srand((unsigned int)time(NULL));
	int i = 0;
	do
	{
    
    
		menu();
		printf("请选择:");
		scanf("%d", &i);
		switch (i)
		{
    
    
		case 1:
			printf("进入游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (i);

	return 0;
}

最后收尾 ^ _ ^ ;
写完我知道自己还有很多不足,我会继续努力,请大家小小支持一波。
如果还有哪里不足的地方,还请大家斧正。

猜你喜欢

转载自blog.csdn.net/m0_66780695/article/details/130586061