c语言基础概念9

数组和函数实践:扫雷游戏

扫雷游戏的分析和设计

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

信息。

那如果这个位置布置雷,我们就存放1,没有布置雷就存放0.

在布置雷后,寻找周围的雷的数量,为了数组不越界访问:可以设置一个11*11的棋盘。

我们专⻔给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不⼲扰了,把雷布置到mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期排查参考。

同时为了保持神秘,show数组开始时初始化为字符 '*',为了保持两个数组的类型⼀致,可以使⽤同⼀套函数处理,mine数组最开始也初始化为字符'0',布置雷改成'1'。【一个棋盘方便我们测试与操作,另一个棋盘给用户显示。】

1.先设置游戏开始的界面。

源文件test.c中进行扫雷游戏的控制流程操作。【有进入游戏,棋盘的显示,布置雷,排查雷的相关操作,关于他们的具体操作应写在game.c源文件里。】

cs 复制代码
#include <stdio.h>
void menu()
{
	printf("***********************\n");
	printf("*****   1. play   *****\n");
	printf("*****   0. exit   *****\n");
	printf("***********************\n");
}

//void game()
//{
//	//完成扫雷游戏
//
//
//}

void test()
{
	int input = 0;
	do 
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("扫雷\n");
			break;
		case 0:
			printf("游戏结束,退出游戏\n");
		default:
			printf("选择错误,重新选择\n");
		}

	} while (input);
}

int main()
{
	test();
	return 0;
}

或者直接放在主函数中:

cs 复制代码
#include <stdio.h>
void menu()
{
	printf("***********************\n");
	printf("*****   1. play   *****\n");
	printf("*****   0. exit   *****\n");
	printf("***********************\n");
}



int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");

		scanf("%d", &input);
		switch (input)
		{
		case 1:
			//game();
			printf("开始扫雷\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

2.扫雷游戏的具体逻辑代码

(1)扫雷游戏的棋盘是9*9,为了方便显示棋盘给玩家看,所以需要两个棋盘进行操作。

实现初始化棋盘与打印出棋盘的操作:

进行函数声明:

cs 复制代码
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];//存放排查出的雷的信息// 显示棋盘,给玩家看

	//初始化棋盘
	//1. mine数组最开始是全'0'
	//2. show数组最开始是全'*'
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//打印棋盘
	DisplayBoard(mine, ROW, COL);

	DisplayBoard(show, ROW, COL);
}


int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");

		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			//printf("开始扫雷\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}
cs 复制代码
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);
cs 复制代码
game.c文件中:

#include "game.h"

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)//定义set,是为了不重复写相同的代码,避免代码冗余。
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;//此处的set可以是*,也可以是'0'。
		}
	}
}

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");
	}
}

(2)布置雷

cs 复制代码
再test.c文件中添加:

void game()
{
	//完成扫雷游戏
	char mine[ROWS][COLS];//存放布置好的雷,// 真实棋盘,存放地雷位置
	char show[ROWS][COLS];//存放排查出的雷的信息// 显示棋盘,给玩家看

	//初始化棋盘
	//1. mine数组最开始是全'0'
	//2. show数组最开始是全'*'
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//打印棋盘
	DisplayBoard(mine, ROW, COL);//真实棋盘

	DisplayBoard(show, ROW, COL);

	//1. 布置雷
	SetMine(mine, ROW, COL);
	DisplayBoard(mine, ROW, COL);//真实棋盘

}


再game.h文件中添加:

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

再gamen.c文件中添加:

void SetMine(char board[ROWS][COLS], int row, int col)
{
	//布置10个雷
	//⽣成随机的坐标,布置雷
	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--;
		}
	}
}

此外在主函数中要添加随机数种子:
srand((unsigned int)time(NULL));

随机雷布置完成【开始排雷】

(3)排雷

【排雷后查看周围是否有雷,存在;

巧妙之处:

计算周围雷数时,可以直接用字符相减:

// 周围8个格子的字符值相加

// 如果是'0'(无雷),ASCII码是48

// 如果是'1'(有雷),ASCII码是49

// 周围雷数 = (字符总和 - 8 * '0')

如果一开始排的雷是'1',那么你就被炸死了;如果不是,是'0',那么就会显示周围的雷数,继续游戏。把雷排完后,没死,游戏胜利。

cs 复制代码
在test.c文件中:

void game()
{
	//完成扫雷游戏
	char mine[ROWS][COLS];//存放布置好的雷,// 真实棋盘,存放地雷位置
	char show[ROWS][COLS];//存放排查出的雷的信息// 显示棋盘,给玩家看

	//初始化棋盘
	//1. mine数组最开始是全'0'
	//2. show数组最开始是全'*'
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	// mine棋盘:
	//'0' - 无雷
	//	'1' - 有雷(注意:这里是字符'1',不是数字1)

	//	// show棋盘:
	//	'*' - 未翻开
	//	数字字符'0' - '8' - 周围雷数

	//打印棋盘
	DisplayBoard(mine, ROW, COL);//真实棋盘

	DisplayBoard(show, ROW, COL);

	//1. 布置雷
	SetMine(mine, ROW, COL);
	DisplayBoard(mine, ROW, COL);//真实棋盘

	//2. 排查雷
	FindMine(mine, show, ROW, COL);

}

在game.h文件中:

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

在game.c文件中:


//方法一:
//计算周围雷数时,可以直接用字符相减:
//// 周围8个格子的字符值相加
//// 如果是'0'(无雷),ASCII码是48
//// 如果是'1'(有雷),ASCII码是49
//// 周围雷数 = (字符总和 - 8 * '0')
//static 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';
//}

//方法二:
//为了得到周围雷的个数:
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int count = 0;
	//遍历九个坐标,中间的是'0',不会有任何影响,不需要规避。
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
			count += (mine[i][j] - '0');
		}
	}
	return count;
}

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)//程序结束标志,
		//想要测试,直接把雷安排成80,那么只需要排一次就知道程序是否可行。
	{
		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);
	}
}

扫雷游戏的代码实现

test.c

cs 复制代码
#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];//存放排查出的雷的信息// 显示棋盘,给玩家看

	//初始化棋盘
	//1. mine数组最开始是全'0'
	//2. show数组最开始是全'*'
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	// mine棋盘:
	//'0' - 无雷
	//	'1' - 有雷(注意:这里是字符'1',不是数字1)

	//	// show棋盘:
	//	'*' - 未翻开
	//	数字字符'0' - '8' - 周围雷数

	//打印棋盘
	//DisplayBoard(mine, ROW, COL);//真实棋盘

	DisplayBoard(show, ROW, COL);

	//1. 布置雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);//真实棋盘

	//2. 排查雷
	FindMine(mine, show, ROW, COL);

}


int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));

	do
	{
		menu();
		printf("请选择:>");

		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			//printf("开始扫雷\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

game.c

cs 复制代码
#include "game.h"

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)//定义set,是为了不重复写相同的代码,避免代码冗余。
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;//此处的set可以是*,也可以是'0'。
		}
	}
}

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)
{
	//布置10个雷
	//⽣成随机的坐标,布置雷
	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--;
		}
	}
}

//方法一:
//计算周围雷数时,可以直接用字符相减:
//// 周围8个格子的字符值相加
//// 如果是'0'(无雷),ASCII码是48
//// 如果是'1'(有雷),ASCII码是49
//// 周围雷数 = (字符总和 - 8 * '0')
//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';
//}

//方法二:
//为了得到周围雷的个数:
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int count = 0;
	//遍历九个坐标,中间的是'0',不会有任何影响,不需要规避。
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
			count += (mine[i][j] - '0');
		}
	}
	return count;
}

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)//程序结束标志,
		//想要测试,直接把雷安排成80,那么只需要排一次就知道程序是否可行。
	{
		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);
	}
}

game.h

cs 复制代码
#pragma once

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

#define EASY_COUNT 10

#define ROW 9
#define COL 9
//好处:
//1. 计算边界格子周围雷数时,不需要特殊判断边界
//2. 避免数组越界访问
//3. 简化代码逻辑
#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文件里添加:

cs 复制代码
#include "game.h"

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)//定义set,是为了不重复写相同的代码,避免代码冗余。
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;//此处的set可以是*,也可以是'0'。
		}
	}
}

//展示棋盘
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)
{
	//布置10个雷
	//⽣成随机的坐标,布置雷
	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--;
		}
	}
}

//方法一:
//计算周围雷数时,可以直接用字符相减:
//// 周围8个格子的字符值相加
//// 如果是'0'(无雷),ASCII码是48
//// 如果是'1'(有雷),ASCII码是49
//// 周围雷数 = (字符总和 - 8 * '0')
//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';
//}

//方法二:
//为了得到周围雷的个数:
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int count = 0;
	//遍历九个坐标,中间的是'0',不会有任何影响,不需要规避。
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
			count += (mine[i][j] - '0');
		}
	}
	return count;
}

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)//程序结束标志,
		//想要测试,直接把雷安排成80,那么只需要排一次就知道程序是否可行。
	{
		printf("请输⼊要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			
            //如果是未排查坐标
			if (show[x][y] == '*') {

				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");
			}
			
		}

		else
		{
			printf("坐标⾮法,重新输⼊\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

【*是未翻开,未排查的雷。】

解释:为什么要有两个棋盘?

两个棋盘是一样的类型(都是 char 数组)对 mine棋盘(隐藏棋盘)进行操作和计算把结果"映射"到 show棋盘(显示棋盘)上显示给玩家。【两个棋盘是一样的类型,为了方便操作与区分,在看不到的棋盘进行操作后,把它显示到玩家能看得到的棋盘上。就像考试时,老师有答案+试题,而学生只有试题。】

相关推荐
yxm26336690812 小时前
【洛谷压缩技术续集题解】
java·开发语言·算法
键盘帽子2 小时前
多线程情况下长连接中的session并发问题
java·开发语言·spring boot·spring·spring cloud
毅炼2 小时前
Java 基础常见问题总结(1)
开发语言·python
fengxin_rou2 小时前
【黑马点评实战篇|第一篇:基于Redis实现登录】
java·开发语言·数据库·redis·缓存
数智工坊2 小时前
【数据结构-栈】3.1栈的顺序存储-链式存储
java·开发语言·数据结构
R-G-B2 小时前
python 验证每次操作图片处理的顺序是否一致,按序号打上标签,图片重命名
开发语言·python·图片重命名·按序号打上标签·验证图片处理的顺序
小二·2 小时前
Go 语言系统编程与云原生开发实战(第10篇)性能调优实战:Profiling × 内存优化 × 高并发压测(万级 QPS 实录)
开发语言·云原生·golang
多多*2 小时前
2月3日面试题整理 字节跳动后端开发相关
android·java·开发语言·网络·jvm·adb·c#
v_for_van2 小时前
力扣刷题记录4(无算法背景,纯C语言)
c语言·算法·leetcode