碰撞•扫雷游戏

本文详细描述了一个使用C语言编写的扫雷游戏的实现过程,包括主函数的选择逻辑、菜单设计、创建并初始化数组、打印棋盘、布置雷和安全排查等关键步骤。
摘要由CSDN通过智能技术生成

总体思路🎉

建立三个文件。

一个头文件game.h用来写游戏主要函数的声明。

两个源文件,一个test.c用来写主函数和菜单,一个game.c用来写game.h中函数的具体实现。

1.主函数(test.c_main();)🐾

我们需要选择玩 或者 不玩,如果玩就进入 game(); 执行游戏,如果不玩就退出程序。(每个源文件都是的单独编译的)

为了让游戏界面更美观,以及提示用户玩或者不玩分别需要输入什么,我们需要一个菜单。

从主函数开始写,明确思路。我们需要写一个game();函数来玩游戏。另外两个选择要有提示语,特别需要注意的是,应当写一个default来应对用户输入错误的情况。

do…while:无论如何,菜单都要执行一次。
❤️菜单至少执行一次、game()、提示语。

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

2.菜单(test.c_menu();)🐾

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

3.创建两个数组(test.c_game();)🐾

数组的大小

在这里插入图片描述
为了计算边缘位置的周围雷的个数,应该扩大一层。
即为了计算下面小黑方块周围雷的个数。
在这里插入图片描述

所以数组的行、列长度应该分别为“雷区的行+2”、“雷区的列+2”,如要设置22的雷区,应该设置44的数组。
在这里插入图片描述

数组的个数

mine数组:如何放置雷。
show数组:游戏体验感。

设置两个数组的原因:1.游戏体验感 2.避免计算周围雷的个数时出现混淆。
1.游戏体验感
在这里插入图片描述
2.避免计算周围雷的个数时出现混淆
(用0表示无雷,用1表示有雷)
下图中的1有歧义:“所排查位置有雷” 或 “所排查位置的周围有1个雷”。 在这里插入图片描述
不用其它符号表示的原因:1.棋盘信息太多 2.效率低下。
下图中打印周围雷的个数,每次打印前都要判断3次,“是#吗?” “是*吗?” “是数字吗?” 导致效率低下。
在这里插入图片描述

4.初始化棋盘(game.c_InitBoard();)🐾

把mine数组初始化为’0’,即全部无雷。
把show数组初始化为’*',即全部未知(游戏体验感)。
int j = 0 放外面也一样,反正每次进入for都会 j = 0

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

在这里插入图片描述
+++++】
二维数组作为参数
• 形参如果是一维数组,数组大小可以省略不写。
• 形参如果是二维数组,行可以省略,但是列不能省略。
• 数组传参,形参是不会创建新的数组的。
• 形参操作的数组和实参的数组是同一个数组。

命名经验✨

参数名小写,由不止一个单词组成的函数名间隔大写,只有一个单词就用小写。

set:不用写死,例如set=‘0’ 、set=‘*’ 。set接收的是字符哦!
❤️二维数组作为参数、命名、不要写死。
去game.h写函数声明:

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

5.打印棋盘(game.c_DisplayBoard();)🐾

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("--------扫雷游戏-------\n");              
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

游戏开始的提示语:都是打印,所以直接放打印棋盘这个函数里,不要放主函数,太乱。

printf("--------扫雷游戏-------\n");

打印行列数字:方便排查雷时写坐标。
在这里插入图片描述

在这里插入图片描述
记得写\n
❤️提示语、坐标、\n。

6.布置雷(game.c_SetMine();)🐾

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

count

  1. 含义: 要布置的雷的个数
  2. 作用: 配合while()语句方便停下。

while经验✨

已知具体循环次数,直接while(循环次数),然后在适当位置 循环次数减减,当循环次数减到0时,while()内就为假,循环就进不去了。
产生随机数

  1. 以时间作为随机数种子:game.h包含头文件<time.h>→只需要在主函数里设置一次种子→生成随机数(横纵坐标都是随机数哦!)
    在这里插入图片描述

  2. 生成一定范围内的随机数:

 int random_number = min + rand() % (max - min + 1);  

rand() % (max - min) 中%运算符是取模运算,它返回两个数相除的余数。
余数的范围是 0 到 (除数 - 1)。
为了包含 max,需要将除数的范围扩展到 (max - min + 1)。
在这里插入图片描述
防止重复布置雷:只有为0,即该位置无雷时才布置雷。只有成功布置了雷,才count减减。
在这里插入图片描述
去game.h写函数声明:

 void SetMine(char board[ROWS][COLS], int row, int col);

❤️结束循环的办法(count、while)、随机数、避免重复。

7.排查雷(FindMine();)🐾

思路:写FindMine→中途发现经常要排查周围雷的个数,于是拆分出一个GetMineCount函数→想好GetMineCount函数要实现什么,继续写FindMine(就像先写主函数思路,再写具体函数一样)→写GetMineCount函数。

int GetMineCount(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;
	int y = 0;
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

GetMineCount();

横坐标不变,纵坐标变;纵坐标不变,横坐标变。
要计算周围雷的个数,只需要把周围坐标上对应的数字(字符形式)加起来再减去8*48就可以了。因为:

  1. mine是字符型数组。
  2. 字符’0’的ASCII码值是48。
  3. 数字的ASCII码值=48+数字本身。如 ‘1’=48+1=49;‘2’=48+2=50。
  4. 字符形式的直接等于它的ASCII码值。

在这里插入图片描述
要方便玩家找坐标。在这里插入图片描述

FindMine();

进入循环的条件是“还没有排完雷”:

  while (win < row * col - EASY_COUNT)
  1. row*col是可排查位置的总数。
  2. EASY_COUNT是雷的个数。
  3. row *col - EASY_COUNT是安全位置的个数。
  4. 每安全一次就win+1,如果中途踩到雷,就break退出while循环(if 不是循环,所以break退出的是while。);如果一直安全,那win就会++到等于row *col - EASY_COUNT,排雷成功。

第一个if:坐标合法(范围是从1到“横、纵坐标的最大值”)才能进入。

 if (x >= 1 && x <= row && y >= 1 && y <= col)

第二个if:等于字符’1’就说明踩到雷了。break退出while循环。

 if (mine[x][y] == '1')

当前位置的周围雷的个数

 show[x][y] = count + '0';
  1. show是字符型数组。
  2. count是整型。
  3. 字符型数字=整型数字 +‘0’。

DisplayBoard:最后把雷的布置打印出来,让玩家“死”的明白。

win++:每安全一次,就win+1。
在这里插入图片描述

第三个if:如果win能够++到与安全位置的个数相等,就说明已经排查了所有安全位置,那剩下的就是雷了,排查成功。
在这里插入图片描述

完整代码🎉

test.c:记得包含game.h

#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(show, ROW, COL);
	SetMine(mine, ROW, COL);
	FindMine(mine, show, ROW, COL);
}

game.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define EASY_COUNT 10
#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 set);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void SetMine(char board[ROWS][COLS], int row, int col);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

game.c:记得包含game.h

#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("--------扫雷游戏-------\n");              
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		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 = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

int GetMineCount(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;
	int y = 0;
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

不要马虎哦🙌

  1. 数组作为参数,直接传递数组名。
  2. 初始化后要打印show数组,让玩家开始选要排查的坐标。
  3. 别忘记打印提示横纵坐标的数字,别把这个和平时数学里习惯的列出的坐标等同哦,因为玩家体验感优先。
  4. 有很多地方需要换行哦,别忘了加上\n。
  5. 雷区的横纵坐标都是从1开始的哦!
  6. SetMine()函数里随机数坐标的生成要放进循环里哦,不然只能生成一个。
  7. 别把“安全位置的个数”和“雷的个数”搞混。
  8. 判断相等是“==”哦。
  9. 打印数组的时候,是从1开始,到row和col哦。(i = 1;i <= row;i++)
    10.GetMineCount()函数里的坐标不要加错哦,如果前面加少了,后面又减了8*‘0’,那返回的值就是负数了哦 。返回的值还要加’0’变成字符型,很乱诶,这会导致得不到我们想要的东西哦。

💎💎💎💎💎💎 恭喜! 恭喜! 排雷成功! 💎💎💎💎💎💎

  • 17
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值