C语言——扫雷游戏的攻克,问题的总结

扫雷代码程序包含的内容主要有以下几大块:

1.自定义函数的定义、数组作为自定义函数形式参数的传参方式

2.break跳出语句的局限性

3.不同函数头文件的include,<>和“”对不同头文件的引用情境,.c文件和.h文件之间信息的相互连结

4.不同变量的定义方式,如何有效实现变量名和其作用之间的对应区分

5.如何让程序更加有条理性

首先第一点,关于自定义函数这一知识点,我也会后续补充到《C语言--函数》文章里,以下是实战中对他的进一步加深理解。

为了提高代码的可读性和简洁性,我首先创建了game.h     game.c     whole.c三个文件,我所需要的函数在game.h头文件中进行声明,game.c源文件中完成了函数的定义,最后我们定义的函数在whole.c源文件中执行了他的功能。

其次,当数组作为形式参数加入到自定义函数中时,我们在使用函数时对应的数组只需要给数组名,而不要包括后面的下标值,完善的下标值在程序运行时会解析成数组中对应下标的元素。

以下是示例

//摘取了扫雷文件中打印棋盘的自定义函数print为例
void print(char arr[Rows][Cols], int row, int col);
//这是在头文件中的函数声明

//这是在源文件中的函数定义
void print(char arr[Rows][Cols], int row, int col)
//我们知道在二维数组定义时必须给定列数,行数非必要(电脑可以根据初始化元素数量进行划分)
//但是在我们的自定义函数中,除一维数组参与函数定义时中括号可以不输入对应元素,其余数组的容积大小需完善,同时我们要注意,这里的char arr[Rows][Cols]被视为数组的名称
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
			printf("%c ", arr[i][j]);
		printf("\n");
	}
//以上代码块为二维数组通用的printf函数
}


int main()
{
 char arr[3][5] ={1,2,3,4,5,2,3,4,5,6,3,4,5,6,7}
 print(arr,3,5)
//在使用时只需要输入数组的名称,若照搬函数定义时的arr[Rows][Cols]则会产生报错
 return 0;
}

如果我们在使用函数时对参数的输入格式错误,则会出现下面的情况

这里的char show[Rows][Cols]是类型声明语法(在函数定义时使用),但函数在调用传参时,不需要再写类型,直接传变量或者常量即可。

根据这个问题我们有以下总结:

1.函数调用时,传参只给变量/常量,不带类型

2.调用前,变量(如arr、set数组)均已正确定义

3.核对函数声明和定义的参数格式,确保一致性

_______________________________________________________________________________

观察上图红圈部分,这是由于对'break’语句的不正确理解导致的情况。

break只能跳出离他最近的一次循环,如过break嵌套太深,会导致无法跳出循环的情况发生,他的这种特性类似于else语句总是和离他最近的if语句相关联。

同样的,如果我们担心break语句执行后仍未跳出循环,或继续执行后续的语句,我们可以加强对后续语句执行方式的限定,例如在print前加if(flag == 1)这样语句就会根据flag的值进行判断是否执行。

偷懒技巧:如果担心break跳出循环不准确,我们可以考虑使用goto语句,结束此循环

当一个库函数未包含头文件时,编译器会出现报警,警告内容如“”未定义,假设外部返回int

关于常用库函数对应的头文件总结(后续会逐渐更新):

1.<stdio.h>——printf   scanf    

2.<stdlib.h>——rand  system srand

3.<string.h>——strlen

:sizeof不是一个函数,他是C语言中的一个操作符,用于获取数据类型或者变量在内存中占用的字节数,使用时不需要包含头文件

4.<time.h>——time

_______________________________________________________________________________

在扫雷这个程序编写中,我们会使用到很多的变量去判断信息,那么我们应该如何定义他们的名称才能更加方便快捷的使用他们呢。以下是我个人总结的几大原则:

1.易读性

我们编写的变量名称一定要对看代码的人通俗易懂,我们可以使用他们的中文拼音也可以使用在英语中的单词,这是两个最简单的编写方式

2.唯一性

变量的名称编写要保持唯一性,尤其在全局变量中,名称如果不唯一,编译器会出现报警的情况,同时也会让我们混淆不同变量之间的功能

3.同一性

我们知道在C语言中数据的类型多种多样,如整型类型、字符类型、浮点类型、布尔类型;那么根据这个特性我们可以对同一类型变量,采取相同的名称定义,这样也能方便变量之间的调用。

_______________________________________________________________________________

注释是C语言代码编写中的强大功能,我们可以对自己编写代码键入合适的注释,以便后续自己活着他人阅读时可以更加方便快捷。

扫雷程序编写的思路方向:
1.游戏开始和退出的选择

2.进入游戏的游戏模块编写(思路很重要)

在此步骤中,我们要预设代码的实现,我们知道扫雷展现时我们是无法观察到雷的排布,但同样的我们又需要让系统知道我们在何处藏了雷,这两种功能我们显然不可能通过一个数组全部完成,因此我们可以定义两个数组完成,且他们最好是同数据类型

我们使用*来布置棋盘1,在棋盘2中使用‘1’来表示雷的位置,‘0’表示安全地带,当我们需要确认某一特定下标值是否为雷时,我们直接对此位置上的字符进行核对即可完成。

我们知道安全区会在他的地带上标写周围9格(含自身)的地雷数量,,那么我们知道他们是限定行和列范围的一个长方形,所以我们可以依靠这个特性对后续判断进行简化。同样的边缘位置的判断会出现越界问题,故而我们考虑给棋盘每边+1的空间,即9*9的游戏空间我们创建11*11的数组

3.设置游戏的循环方式和退出方式

———————————————————————————————————————————

下面是我编写的扫雷程序三个文件的源代码,可供大家参考,也辛苦大家指出我的编码问题

#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

void init_hide(char arr[Rows][Cols],int row,int col,char a);
void setbomb(char arr[Rows][Cols],  int b, int row, int col);
void init_show(char arr[Rows][Cols], int row, int col, char a);
void print(char arr[Rows][Cols], int row, int col);
void compare_both(char arr[Rows][Cols], char arr2[Rows][Cols], int x, int y,int flag);
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void init_hide(char arr[Rows][Cols], int row, int col, char a)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			arr[i][j] = a;
		/*	printf("%c", arr[i][j]);*/
		}
   }
}
void setbomb(char arr[Rows][Cols],int b ,int row,int col)
{
	int x, y;
	while (b > 0)
	{
		x = rand() % row + 1;
	    y = rand() % col + 1;
		if (arr[x][y] != '1')
		{
			arr[x][y] = '1';
			b --;
		}
	}

}
void init_show(char arr[Rows][Cols], int row, int col, char a)
{
	int i = 0;
	int j = 0;
	for ( i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (i == 0 || i == row - 1)
				arr[i][j] = j + '0';
			else if (j == 0 || j == col -1)
				arr[i][j] = i + '0';
			else
				arr[i][j] = a;
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
}
void print(char arr[Rows][Cols], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
			printf("%c ", arr[i][j]);
		printf("\n");
	}
}
void compare_both(char arr[Rows][Cols],char arr2[Rows][Cols], int x, int y,int flag)
{
	int sum = 0;
	if ((x > 0 && x < 9) && (y > 0 && y < 9))
	{
		if (arr[x][y] == '1')
		{
			flag = 0;
			printf("很不幸,你被雷炸死了\n");
		}
		else
		{
			for (int i = -1; i < 2; i++)
			{
				for (int j = -1; j < 2; j++)
				{
					if (arr[x + i][y + j] == '1')
						sum++;
				}
			}
			arr2[x][y] = '0' + sum;
		}
	}
	else
		printf("请输入正确的行列数\n");
}
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

//choice()
//{
//	int count;
//	printf("请自定义你的埋雷个数\n");
//	/*printf("简单   普通   困难\n");*/
//	printf("请输入 ");
//	scanf("%d", &count);//定义埋雷数量
//
//}

game()//游戏具体操作方式
{
	char show[Rows][Cols];
	char hide[Rows][Cols];
	init_show(show,Rows,Cols, '*');//游戏上界面展示
	init_hide(hide,Rows,Cols, '0');//游戏下界面雷区
	//布置雷	
	int count;
	printf("请自定义你的埋雷个数\n");
	/*printf("简单   普通   困难\n");*/
	printf("请输入 ");
	scanf("%d", &count);//定义埋雷数量
	setbomb(hide, count, Row, Col);//埋雷
	print(hide, Rows, Cols);
	//检验埋雷的位置
	int flag = 1;//用来判断是否结束循环
	int try = Row * Col - count;//限制测试的次数
	while (flag == 1 && try > 0 )
	{
		int x, y;
		printf("请输入你想查询的埋雷坐标");
		scanf("%d%d", &x, &y);
		try--;
		/*compare_both(hide, show, x, y,flag);*/
		int sum = 0;
		if ((x > 0 && x < 9) && (y > 0 && y < 9))
		{
			if (hide[x][y] == '1')
			{
				flag = 0;
				printf("很不幸,你被雷炸死了\n");
				break;
			}
			else
			{
				for (int i = -1; i < 2; i++)
				{
					for (int j = -1; j < 2; j++)
					{

						if (hide[x + i][y + j] == '1')
							sum++;
					}
				}
				show[x][y] = '0' + sum;
			}
		}
		else
			printf("请输入正确的行列数\n");
	/*	if (flag == 0)
		{
			break;
		}
		else if (flag == 1 && try == 0)
		{
			printf("恭喜你完成了游戏\n");
		}
		else*/
			print(show, Rows, Cols);
	}
	
}


//进入游戏界面
int main()
{
	int answer;//判断玩家输入值,以实现游戏的开始、结束、重新输入
	while (1)
	{
		srand((unsigned int)time(NULL));
		printf("*******************\n");
		printf("******1. play******\n");
		printf("******0. exit******\n");
		printf("*******************\n");
		printf("请输入 ");
		scanf("%d", &answer);//输入answer的值
		if (answer == 1)
		{
			printf("游戏开始\n");
			game();
		  /*	break;*/
		}
		else if (answer == 0)
		{
			printf("游戏结束\n");
			break;
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	}

	return 0;
}

打怪升级中..................................................................................................................................

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值