简单介绍扫雷游戏及其代码实现

  1. 介绍扫雷游戏规则
  2. 对扫雷游戏代码实现的思考及解决
  3. 扫雷游戏代码实现
  • ​​​​​​初始化棋盘
  • 打印棋盘
  • 布置雷
  • 排查雷

​     4.扫雷游戏代码

     5.写写我踩过的坑🕳️


介绍扫雷游戏

相信大家都或多或少知道扫雷游戏,接下来让我介绍一下扫雷游戏的规则吧~

0498b469054744ac9f843f54247771b1.png

 游戏目标玩家需要在最短的时间内识别出所有非雷区的格子,同时避免触碰到地雷。游戏区域由多个隐藏格子组成,每个格子可能隐藏着地雷或数字,数字表示周围八个格子中地雷的数量。

游戏模式:扫雷游戏默认三个等级:初级、中级、高级。初级游戏由9x9的平铺网格组成,共有10个地雷;中级游戏由16x16的平铺网格组成,共有40个地雷;高级游戏由16x30个平铺网格组成,共有99个地雷。


对扫雷游戏代码实现的思考及解决

  1. 在检索雷的个数的时候,需要检索周围的8个格子,当在边角时,周围不满8个格子,会出现越界访问,怎么办?
  2. 雷布置为 1,当周边有1个雷时,产生了歧义,怎么办?
  3. 对于已经排查过的坐标怎么办?
  4. 排查的坐标超过表格的范围怎么办?

9c885de95314457c81346e63f999f223.png

解决方法:

本文以9*9为例:

1. 由于一个一个判断情况很麻烦,我们直接把棋盘扩大2行,变成11*11509e3679f8ab48e3a15c872310a10e50.png

 初始化时把11*11初始化,打印时只打印9*9,再排查雷时就不会出现越界访问o(^・x・^)o~

2.由于表示雷的个数“1”与表示雷的“1”会有冲突,我们可以设置两个棋盘(一个为 mine,即自己可以看到的;另一个为 show,即显示在外的)

为了打印和计算方便,我建议大家把 mine 都初始化为'0',把 show 初始化为'*'(也是为了看起来更神秘_(:зゝ∠)_)

3.我们要设计代码,排除已经排查过的坐标

4.规定输入坐标的范围


首先:

我们演示的是9*9的格子,为了以后好改动,我们把行列定义出来,到时候升级时,容易改动~

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2

初始化棋盘:

void initboard(char board[ROWS][COLS], int rows, int cols, char x)
{
	int i, j;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = x;
		}
	}
}

​​​​打印棋盘:

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

结果:

ee03329cc29c49dc8c59fb5212e7758f.png

布置雷:

用我们学过的随机数,随机布置雷,为了以后修改方便,我们也用#define

#define COUNT 10
void setmine(char board[ROWS][COLS],int row,int col)
{
	int count = COUNT;
	while(count)
	{
		int i = rand() % row+1;
		int j = rand() % col+1;
		if (board[i][j] == '0')//防止雷设置重复
		{
			board[i][j] = '1';
			count--;
		}
	}

打印结果

排雷:

要想得知周围雷的数量:

通过周围的坐标-8*0得到值,因为mine上显示的是字符。

int num(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 findmine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col)
{
	int x = 0, y = 0;
	int win = 0;
	while (win < ROW * COL - COUNT)//ROW*COL-COUNT为除雷之外的空格子
	{
		printf("请输入坐标: ");
		scanf("%d %d", &x, &y);
		if ((x > 0 && x <= row) && (y > 0 && y <= col))//判断在棋盘内
		{
			if (show[x][y] != '*')
				printf("这个坐标你已经排查过了~\n");
			else if (mine[x][y] == '1')
			{
				printf("你被炸死了\n");
				displayboard(mine, ROW, COL);
				break;
			}
			else
			{
				show[x][y] = num(mine, x, y) + '0';//由于得到的是一个int类型的整数,+'0'再变成字符
				displayboard(show, ROW, COL);
			}

		}
		else
			printf("你输入的坐标已经超过范围\n");
	
	}
	if (win == ROW * COL - COUNT)
	{
		printf("恭喜你,排雷成功\n");
		displayboard(mine, ROW, COL);
	}

}

总代码

game.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define COUNT 10
void initboard(char board[ROWS][COLS], int rows, int cols, char x);
void displayboard(char board[ROWS][COLS], int row, int col);
void setmine(char board[ROWS][COLS],int row,int col);
int num(char mine[ROWS][COLS], int x, int y);
void findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

game.c

#include"game.h"
void initboard(char board[ROWS][COLS], int rows, int cols, char x)
{
	int i, j;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = x;
		}
	}
}
void displayboard(char board[ROWS][COLS], int row, int col)
{
	int i, j;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i+1);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
void setmine(char board[ROWS][COLS],int row,int col)
{
	int count = COUNT;
	while(count)
	{
		int i = rand() % row+1;
		int j = rand() % col+1;
		if (board[i][j] == '0')//防止雷设置重复
		{
			board[i][j] = '1';
			count--;
		}
	}
}
int num(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 findmine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col)
{
	int x = 0, y = 0;
	int win = 0;
	while (win < ROW * COL - COUNT)
	{
		printf("请输入坐标: ");
		scanf("%d %d", &x, &y);
		if ((x > 0 && x <= row) && (y > 0 && y <= col))//判断在棋盘内
		{
			if (show[x][y] != '*')//由于数组下标从0开始,我们的坐标-1才能得到正确的值
				printf("这个坐标你已经排查过了~\n");
			else if (mine[x][y] == '1')
			{
				printf("你被炸死了\n");
				displayboard(mine, ROW, COL);
				break;
			}
			else
			{
				show[x][y] = num(mine, x, y) + '0';
				displayboard(show, ROW, COL);
			}

		}
		else
			printf("你输入的坐标已经超过范围\n");
	
	}
	if (win == ROW * COL - COUNT)
	{
		printf("恭喜你,排雷成功\n");
		displayboard(mine, ROW, COL);
	}

}

test.c

#include"game.h"
void menu()
{
	printf("*********************\n");
	printf("********1.play*******\n");
	printf("********0.exit*******\n");
	printf("*********************\n");
}
void game()
{
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	initboard(mine, ROWS, COLS, '0');
	initboard(show, ROWS, COLS, '*');
	//displayboard(mine, ROW, COL);
	displayboard(show, ROW, COL);
	setmine(mine,ROW,COL);
	//displayboard(mine, ROW, COL);
	findmine(mine, show, ROW, COL);



}

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

	} while (n);
	return 0;
}

我踩过的坑🕳️:

  • 把数组写的太过笼统

eg: mine为arr1,show为arr2

最后把他们搞混😖,并且很难找到问题所在

  • 直接复制函数声明去用

比较懒,就复制了函数声明,把里面的删了再用,结果用函数的时候,忘记删了 返回类型 ,最终导致错误❌

eg: void initboard(mine,ROWS,COLS,'0');

  • 打印棋盘时,下标从0开始​​​​​​​

​​​​​​​05ad2a49625847f8aa4bb7e1794d1e6c.jpg

 导致最终结果与找的坐标不一样

虽然做了补救d3b9fd89801844e4a892daafb0f4427d.jpg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值