扫雷游戏(数组和函数的实践)

目录

Part1 扫雷游戏初步分析

Part2 扫雷游戏设计难点及其解决方案

难点一:逻辑混乱

难点二:越界访问

Part3 扫雷游戏代码分析

1. 游戏菜单打印

2. 游戏实现

  (1)函数信息的声明

  (2)棋盘的初始化

  (3)打印棋盘

  (4)雷的布置

  (5)查找功能与游戏成功条件

Part4 扫雷游戏整体代码

(1)test.c

(2)game.h

(3)game.c


Part1 扫雷游戏初步分析

游戏要求:

1. 
使?控制台实现经典的扫雷游戏

2. 
游戏可以通过菜单实现继续玩或者退出游戏

3. 
扫雷的棋盘是9*9的格?

4. 
默认随机布置10个雷

5. 
可以排查雷

        如果位置不是雷,就显?周围有?个雷

        如果位置是雷,就炸死游戏结束

        把除10个雷之外的所有雷都找出来,排雷成功,游戏结束

游戏效果:

1.游戏开始进入选择界面,通过选项选择进入或退出游戏

选择1-->进入游戏                   选择0-->退出游戏                   选择其他数值-->输入错误,重新输入

                                                          

2.进入游戏后,选择要排查的位置,若选择的位置有雷,则游戏失败,显示整体棋盘雷的布置

3.若选择的位置没有雷,则显示以排查位置为中心的3*3格子中雷的总个数,当整个棋盘排查完毕后,游戏成功,显示整体棋盘雷的布置

                                                          


Part2 扫雷游戏设计难点及其解决方案

设置思路:

扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要?定的数据结构来存储这些信息。在9*9的棋盘上布置雷的信息和排查雷,我们?先想到的就是创建?个9*9的数组来存放信息

布置雷的棋盘上,‘*’表示有雷,‘0’表示没有雷

显示排查出的雷的信息的棋盘上,‘0’~‘8’表示排查雷位置3*3格子里雷的个数

难点一:逻辑混乱

        当我们排查出雷的信息后需要将信息展示给用户,如果将信息放置在布置雷的数组中,这样雷的信息和雷的信息的个数就可能产生混淆和打印上的困难。

 

解决方案:
所以不如换个思路,直接将雷的信息和排查出雷的信息放在两个不同的棋盘上,也就是创建两个不同的数组,一个用来放置布置的雷的信息,一个用来放置排查出的雷的信息。程序实现的时候,把排查的位置对应到存放雷的数组中,在存放雷的数组中实现雷的排查,将排查好的雷的信息传到放置排查出雷的信息的数组当中,再将放置排查出雷的信息的数组打印出来。

难点二:越界访问

        若将存放雷的信息的数组设置成9*9的格式,对于中间内部8*8的雷的排查是可以实现的,但是四周17个雷的排查会存在问题,若还是按照以排查位置为中心的3*3格子的排查方式,会出现越界访问的情况。

解决方案:为了防?越界,我们在设计的时候,给数组扩??圈,雷还是布置在中间的9*9的坐标上,周围?圈不去布置雷就?,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11是?较合适。


Part3 扫雷游戏代码分析

1. 游戏菜单打印

功能:

玩家可以通过选择1进入游戏,0退出游戏

输入错误提醒玩家重新选择

代码:

void menu()
{
	printf("*********************
");
	printf("*****  1.start  *****
");
	printf("*****  0.exit   *****
");
	printf("*********************
");
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		scanf_s("%d", &input);
		srand((unsigned int)time(NULL));
		switch (input)  //采用while和switch语句完成
		{
		case 1 :
			game();
			goto finish;
		case 0 :
			printf("退出游戏
");
			break;
		default :
			printf("输入错误,请重新输入:
");
		}

	} while (input);
	finish:
	return 0;
}

2. 游戏实现

由于整体代码行数较多,为了便于书写和逻辑的部署,扫雷游戏采用三个文件

test.c   //?件中写游戏的测试逻辑

game.c   //?件中写游戏中函数的实现等

game.h   //?件中写游戏需要的数据类型和函数声明等

  (1)函数信息的声明
#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  //雷的数量
  (2)棋盘的初始化

功能:

设置两个数组分别用来存储布置的雷和查找出来的雷的信息

其中将存储布置的雷的数组设置为mine数组,存储查找出来的雷的信息的数组设置为show数组,将mine数组全部初始化为‘0’,将show数组全部初始化位‘*’

代码:

//test.c中的game函数
char Mine[ROWS][COLS] = { 0 };
char Show[ROWS][COLS] = { 0 };
//初始化棋盘
InitBoard(Mine, ROWS, COLS, '0');
InitBoard(Show, ROWS, COLS, '*');

//game.c文件
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;
		}
	}
}
  (3)打印棋盘

功能:

将初始化完成的存放排查雷的信息的数组打印到屏幕上

棋盘的每一行和每一列都有序号,以方便后续玩家操作

代码:

//game.c文件
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("开始扫雷游戏——————
");
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", i);
	}                               //打印行标
	printf("
");
	for (i = 1; i <= row; i++)      //采用二次循环的方式,打印出9*9的棋盘
	{
		int j = 0;
		printf("%d ", i);           //打印列标
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("
");
	}
}
  (4)雷的布置

功能:

在布置雷的期棋盘中随机选择给定个数个位置布置雷

将雷设置为‘0’

代码:

//game.c文件
void SetBoard(char board[ROWS][COLS], int row, int col)
{
	int count = COUNT;
	while (count)
	{
		int x = (rand() % row) + 1;      
		int y = (rand() % col) + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

重点:

rand()函数可用来生成随机数,但实际上是一个伪随机数,它的运作原理有一定的内部逻辑,是每一次重新生成的随机数一致,为了避免这个问题,可以采用srand函数,它初始化随机种子,会提供一个种子,这个种子会对应一个随机数,再将这个种子设置成与时间相关

也就是在test.c文件中包含srand((unsigned int )time(NULL));并包含头文件stdlib.h和math.h

  (5)查找功能与游戏成功条件

功能:

选择某个在棋盘范围内的位置,若选择的位置不属于棋盘范围,提醒玩家选择错误,重新输入

选择的位置在棋盘范围内,若此位置为雷,则告知玩家,你被炸死了,游戏失败;若此处不为雷,则告知玩家以此位置为中心的9*9的格子中雷的个数

游戏成功条件:

若所有雷都被排查出来则游戏成功

代码:

//game.c文件
int GetMineCount(char board[ROWS][COLS], int x, int y)
{
	return(board[x][y - 1] + board[x][y + 1] + board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1
	] - 8 * '0');
}
void FindBoard(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 - COUNT)
	{
		printf("请输入要查找的雷:
");
		scanf_s("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("你被炸死了:
");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);
				show[x][y] = '0'+count;
				DisplayBoard(show, row, col);
				win++;
			}
		}
		else
		{
			printf("输入错误,请重新输入:
");
		}
	}
	if (win == row * col - COUNT)
	{
		printf("恭喜你成功通关:
");
		DisplayBoard(mine, row, col);
	}
}

Part4 扫雷游戏整体代码

(1)test.c

#include "game.h"
void game()
{
	char Mine[ROWS][COLS] = { 0 };
	char Show[ROWS][COLS] = { 0 };
	//初始化棋盘
	InitBoard(Mine, ROWS, COLS, '0');
	InitBoard(Show, ROWS, COLS, '*');
	//打印棋盘
	DisplayBoard(Show, ROW, COL);
	//布置棋盘
	SetBoard(Mine, ROW, COL);
	//DisplayBoard(Mine, ROW, COL);
	//查找棋盘
	FindBoard(Mine, Show, ROW, COL);
}
void menu()
{
	printf("*********************
");
	printf("*****  1.start  *****
");
	printf("*****  0.exit   *****
");
	printf("*********************
");
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		scanf_s("%d", &input);
		srand((unsigned int)time(NULL));
		switch (input)
		{
		case 1 :
			game();
			goto finish;
		case 0 :
			printf("退出游戏
");
			break;
		default :
			printf("输入错误,请重新输入:
");
		}

	} while (input);
	finish:
	return 0;
}

(2)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 set);
void DisplayBoard(char borad[ROWS][COLS], int row, int col);
void SetBoard(char board[ROWS][COLS], int row, int col);
void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

(3)game.c

#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)
{
	printf("开始扫雷游戏——————
");
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", i);
	}
	printf("
");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("
");
	}
}
void SetBoard(char board[ROWS][COLS], int row, int col)
{
	int count = 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 board[ROWS][COLS], int x, int y)
{
	return(board[x][y - 1] + board[x][y + 1] + board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1
	] - 8 * '0');
}
void FindBoard(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 - COUNT)
	{
		printf("请输入要查找的雷:
");
		scanf_s("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("你被炸死了:
");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMineCount(mine, x, y);
				show[x][y] = '0'+count;
				DisplayBoard(show, row, col);
				win++;
			}
		}
		else
		{
			printf("输入错误,请重新输入:
");
		}
	}
	if (win == row * col - COUNT)
	{
		printf("恭喜你成功通关:
");
		DisplayBoard(mine, row, col);
	}
}