数组和函数小实践·扫雷

好了上两篇是讲的数组和函数的知识点,那么,现在尝试一下结合两部分的知识实现一下最简单版本的扫雷游戏吧!

什么?不知道扫雷?嘿嘿,不给你介绍。


首先,写一个游戏之前,我们应该知道这个游戏的基本逻辑,简易版的扫雷的游戏逻辑是在一个规格是9*9的棋盘里面排除雷,雷的数量可以自己输入,位置是随机的,当点某一个位置时,如果有雷你就噶了,如果没有就会显示这个点位周围有多少个雷,这个周围是指周围的8个格子,当成功点击除了雷的位置,你就win了!


游戏嘛,没有菜单怎么玩?所以需要一个菜单,最基本的框架,最好是能循环判断你是否离开游戏或者是继续游戏的。

#include <stdio.h>
void menu()
{
	printf("******************\n");
	printf("**1 play  0 exit**\n");
	printf("******************\n");
}
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("来吗?扫雷哦\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:

		case 0:

		default:
		}

	} while (input);
	return 0;
}

一个最基本的框架就好了,只是还没有判断不同情况下该怎么执行,为什么采用do-while呢?因为菜单总是最先打印的嘛,所以直接打印出来,而前面讲过,函数不仅是用来提高效率的,还是可以用来提高整体的简洁度,不然都放在主函数里面,看着多多少少有点不舒服。

循环的实现就是在while()里面,因为非0就是真,所以只要不输入0,就会循环输入。


当用户输入了1之后,就开始游戏了,那么我们应该是在case 1 情况下实现游戏代码的,但是游戏代码放在单独的源文件笔者认为好一点,不同的源文件干不同的事嘛。

现在,我们菜单有了,那么我们需要一个键盘吧?这个布局多大的呢?基础难度是9*9的,当然,一旦我们需要,9*9也能变成10*10,所以修改为了简单,我们就用define定义长和宽。

像这样,为什么又多了一个头文件?因为函数那篇讲过,函数的声明最好放在头文件里面,尤其是多文件的情况下,那么ROW COL长宽有了,还有个ROWS怎么回事?你猜猜呢?

因为多创建了一个头文件,那么我们不妨把所有的库函数需要的头文件,以及函数的声明都放在这个头文件里面,这样就避免的两个头文件引用许多相同的头文件。头文件还需要这些。

因为雷的位置是随机的,所以不妨会涉及到随机数的生成,自然需要这两个头文件了。

好了,现在应该讨论棋盘了,我们设置的是9*9棋盘,但是实际上当我们要实现点击一个位置会出现周围有几个雷的时候,边界的点位其实是不好计算的,如果在中间,我们可以通过ASCII值判断,但是边上的不行。

那么,怎么办呢?只需要把棋盘扩大一圈,最外面的一圈都不放炸弹,不就解决了吗?所以这就是ROWS COLS的由来。

棋盘肯定是二维数组,所以先创建一个二维数组,然后呢?数组前面提到数组里面的内容如果不初始化,值都是一些随机值的,那么只需要一个二维数组吗?倘若你被炸死了,想要知道雷的位置,是不是需要加一个棋盘呢?一个棋盘用来玩家玩游戏,一个用来给玩家看怎么被炸死的,而且多了一个雷数组,还有利于后期的调试。倘若给数组一个0,那么就全是0,可都是0谁分得清哪个数组是哪个数组啊,所以,初识的重要性来了。


第一步,初始化

函数的声明调用都完成了,就差怎么定义了,你问,set有什么用,好问题,如果我调用两次函数,而初始化内容不变的话,那么最后两个数组的初始化内容是一样的,所以我们多传入一个参数,用这个参数完成每次数组的初始化。

那么这些参数是什么呢?二维数组要传吧?行,列要传吧?那你说ROWS COLS能省略吗?能啊,但是由数组的知识点来说,省行不胜列,所以还不如不省略,美观一点。

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

这,初始化就完成了。


第二步,打印棋盘。

棋盘都初始化好了,你不想打印出来看看吗?我想。

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

要注意的是,这里传参,传的不是ROWS,因为打印数组,只需要打印9*9的就行了,不需要打印多出来的那一圈。

效果图,0 1 2 3 4 5 6 7 8 9 打印出来好一点,这样用户就容易知道自己输入的点的坐标对应的是哪个位置了。当然,游戏开始的时候只需要打印arr1就行了,雷数组大可不必。


第三步,放雷。

初始化,打印都做好了,就差炸弹了,前文提及,炸弹的数量我们是可以自己设置的,所以炸弹的数量我们仍采用宏的方式。比如就定炸弹有10个好了。

#define MINE 10

那么现在就是怎么放置的问题了,我们需要考虑两个问题,一个是随机排放的问题,一个是如果放的位置上已经有了怎么办

1 随机数的问题交给srand,rand就行,坐标我们采用x,y,因为是9*9的棋盘,直接用rand对长宽取模加 1就行,坐标随机数的问题就解决了。

2 放的位置有了?有了循环次数就加1呗

代码如是

void Putmine(char arr[ROWS][COLS], int row, int col)
{
	for (int i = 0; i < MINE; i++)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr[x][y] == '*')
		{
			arr[x][y] = '1';
		}
		else
		{
			i--;
		}
	}
}

这是效果,藏雷前和藏雷后的棋盘。

OK了,雷藏好了,该是玩家进行排雷了。


第四步,排雷。

排雷,首先考虑的是坐标的合法性,然后要考虑的是输入坐标后要能成功的输出周围有几个雷。

因为我们设置的雷是字符1,那么只要周围的所有字符与字符0减,就可以返回周围有几个雷。

Ps:刚有点问题,所以在数组的初始化上进行了一些改动。

改动是因为如果把雷数组初始化为0字符的时候,计算雷的个数会出现问题,计算出来就不是雷的个数了,别问,问就是笔者花了挺久才发现。

1 坐标的合法性可以使用if语句进行判断

2 因为排雷,排一次,计算是在雷数组里面计算,而打印出来的是展现数组,所以这次传参需要传两个数组

3 判断胜利条件,只需要多创建一个变量,当这个变量 = 长 * 宽 - 雷的个数的时候就可以结束游戏了,因为你赢了!

//计算周围几个雷
int Comput(char arr[ROWS][COLS],int x,int y)
{
	return arr[x - 1][y] + arr[x - 1][y - 1] +
		arr[x][y - 1] + arr[x + 1][y - 1] +
		arr[x + 1][y] + arr[x + 1][y + 1] +
		arr[x][y + 1] + arr[x - 1][y + 1] - 8 * '0';
}
//排雷
void Excludemine(char arr1[ROWS][COLS],char arr2[ROWS][COLS], int row, int col)
{
	int count = 0;
	int x = 0, y = 0;
	while (1)
	{
		printf("来,输入坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if ('1' == arr2[x][y])
			{
				printf("遭!\n");
				Printboard(arr2, ROW, COL);
				break;
			}
			else
			{
				count++;
				int num = Comput(arr2, x, y);
				arr1[x][y] = num + '0';
				Printboard(arr1, ROW, COL);
			}
		}
		else
		{
			printf("耶装疯哦,重新输入\n");
		}
		if (count == (ROW * COL - MINE))
		{
			printf("耶凶哦!!!\n");
			Printboard(arr2, ROW, COL);
			break;
		}
	}
}

好了,最简单的扫雷就这样匆忙结束了,,,来看看所有的代码吧。

主函数在的源文件的代码

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
	printf("******************\n");
	printf("**1 play  0 exit**\n");
	printf("******************\n");
}
void game()
{
	char arr1[ROWS][COLS] = { 0 };
	char arr2[ROWS][COLS] = { 0 };
	Initboard(arr1, ROWS, COLS, '*');//初始化展现的数组
	Initboard(arr2, ROWS, COLS, '0');//初始化藏雷的数组
	Printboard(arr1, ROW, COL);//打印展现的数组
	//Printboard(arr2, ROW, COL);//打印藏雷的数组
	Putmine(arr2, ROW, COL);//藏雷咯
	//Printboard(arr2, ROW, COL);//打印藏雷的数组
	Excludemine(arr1,arr2, ROW, COL);//排雷
}
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();//打印菜单
		printf("来吗?扫雷哦\n");
		scanf("%d", &input);//用户输入
		switch (input)//判断输入
		{
		case 1://用户想玩
			game();
			break;
		case 0://用户不玩
			printf("不玩?哦豁\n");
			break;
		default://用户装疯
			printf("耶,输错了\n");
			break;
		}
	} while (input);
	return 0;
}

游戏代码块

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘
void Initboard(char arr[ROWS][COLS], int rows, int cols, char set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			arr[i][j] = set;
		}
	}
}
//打印棋盘
void Printboard(char arr[ROWS][COLS], int row, int col)
{
	for (int m = 0; m <= row; m++)
	{
		printf("%d ", m);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
}
//藏雷
void Putmine(char arr[ROWS][COLS], int row, int col)
{
	for (int i = 0; i < MINE; i++)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
		}
		else
		{
			i--;
		}
	}
}
//计算周围几个雷
int Comput(char arr[ROWS][COLS],int x,int y)
{
	return arr[x - 1][y] + arr[x - 1][y - 1] +
		arr[x][y - 1] + arr[x + 1][y - 1] +
		arr[x + 1][y] + arr[x + 1][y + 1] +
		arr[x][y + 1] + arr[x - 1][y + 1] - 8 * '0';
}
//排雷
void Excludemine(char arr1[ROWS][COLS],char arr2[ROWS][COLS], int row, int col)
{
	int count = 0;
	int x = 0, y = 0;
	while (1)
	{
		printf("来,输入坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if ('1' == arr2[x][y])
			{
				printf("遭!\n");
				Printboard(arr2, ROW, COL);
				break;
			}
			else
			{
				count++;
				int num = Comput(arr2, x, y);
				arr1[x][y] = num + '0';
				Printboard(arr1, ROW, COL);
			}
		}
		else
		{
			printf("耶装疯哦,重新输入\n");
		}
		if (count == (ROW * COL - MINE))
		{
			printf("耶凶哦!!!\n");
			Printboard(arr2, ROW, COL);
			break;
		}
	}
}

头文件

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

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

//函数的声明
void Initboard(char arr[ROWS][COLS],int rows,int cols,char set);//初始化
void Printboard(char arr[ROWS][COLS], int row, int col);//打印
void Putmine(char arr[ROWS][COLS], int row, int col);//藏雷
void Excludemine(char arr1[ROWS][COLS], char arr2[ROWS][COLS], int row, int col);//排雷

好了看官们慢慢看吧,,博主累了哈哈哈。

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值