【纯手工扫雷“不含一点添加剂”】完全c语言版

【纯手工扫雷“不含一点添加剂”】完全c语言版_第1张图片

目录

前言

一、扫雷如何基本实现的?

二、代码实现

1.创建事先打包分类:

2.game.h

3.test.c

 4.game.c

一、初始化棋盘(init_board)

二、 打印棋盘(print)

三、玩家的行动 (play_action)

四、埋雷(set_mine)

 五、排雷(demine)

六、排雷时不是雷,连续翻格子(over_mine)

七、标记雷(mark_mine)

八、判断棋盘是否全部翻开(judge_ogame)

附上源代码链接:扫雷源代码 


前言

扫雷包括雷区、地雷计数器(位于左上角,记录剩余地雷数)和计时器(位于右上角,记录游戏时间),确定大小的矩形雷区中随机布置一定数量的地雷(初级为9*9个方块10个雷,中级为16*16个方块40个雷,高级为16*30个方块99个雷,自定义级别可以自己设定雷区大小和雷数,但是雷区大小不能超过24*30),玩家需要尽快找出雷区中的所有不是地雷的方块,而不许踩到地雷。


今天的内容我们就来写一个初级9*9的扫雷

一、扫雷如何基本实现的?

首先它需要来打印一个9*9的‘游戏棋盘’就像左下角那样,由于我们是c语言板就不搞这莫花哨了,就搞个c语言纯手工版吧

【纯手工扫雷“不含一点添加剂”】完全c语言版_第2张图片 原画版游戏棋盘 【纯手工扫雷“不含一点添加剂”】完全c语言版_第3张图片 手工版棋盘

 然后就是扫雷的关键玩家行动的排雷和标记雷了,当然还有比必不可少的判断函数

二、代码实现

1.创建事先打包分类:

我们将游戏实现的底层代码放在test.c里面,将需要使用的且函数的实现放在game.c中,头文件声明一下。  方便我们管理

【纯手工扫雷“不含一点添加剂”】完全c语言版_第4张图片             

2.game.h

我们先来看看我们会用到那些函数与它的作用

#pragma once
#include
#include

#define MINES 10//雷的数量

#define ROW 9
#define COL 9

//比棋盘大一圈防止数组越界访问
#define ROWS ROW + 2
#define COLS COL + 2

//初始化棋盘
void init_board(char board[ROWS][COLS], int rows,int cols,char set);
//打印棋盘
void print(char board[ROWS][COLS], int row, int col);
//埋雷
void set_mine(char mine_board[ROWS][COLS], int row, int col);
//玩家行动
int player_action(char show_board[ROWS][COLS], char mine_board[ROWS][COLS]);
//排雷
int demine(char show_board[ROWS][COLS], char mine_board[ROWS][COLS], int row, int col);
//标记雷
void mark_mine(char show_board[ROWS][COLS], char mine_board[ROWS][COLS], int row, int col);
//判断棋盘是否全部翻开
int judge_ogame(char show_board[ROWS][COLS], int row, int col);
//排雷翻格子
void over_mine(char show_board[ROWS][COLS], char mine_board[ROWS][COLS], int x, int y);

3.test.c

先看预期效果如图                                                                                    :

【纯手工扫雷“不含一点添加剂”】完全c语言版_第5张图片玩家看到的:游戏进入菜单,并提示用户选择开始游戏,打印棋盘 

    由于我们需要展示棋盘,又要记录雷的位置,这里我们创建show展示mine记录雷两个   棋盘来分工一下。 

int main()
{
    //用于随机埋雷
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("用户请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
            //开始游戏
			start_game();
			break;
		case 0:
			printf("退出游戏!!\n");
			break;
		default:
			printf("输入错误,请重新输入:");
			break;
		}
	} while (input);

}

  当然在玩家看不到的地方:两个棋盘的初始化埋雷等准备工作也是必不可少的 ╰( ̄ω ̄o) 

【纯手工扫雷“不含一点添加剂”】完全c语言版_第6张图片 当求边界格子周围9宫格内雷的数量

由于在后续求边界雷的数量(如右图的绿框) 

为了避免越界访问,我们就将棋盘的定义大一圈 ,则为11*11

#include"game.h"
void menu()
{
	printf("**********************\n");
	printf("*******1.paly*********\n");
	printf("*******0.exct*********\n");
	printf("**********************\n");
}
//开始游戏
void start_game()
{
	int flag = 1;
	char show[ROWS][COLS] = { 0 };//用于展示扫雷棋盘
	char mine[ROWS][COLS] = { 0 };//用于记录扫雷信息
	//初始化棋盘
	init_board(show,ROWS,COLS,'*');
	init_board(mine,ROWS,COLS,'0');
	//打印棋盘
	print(show, ROW, COL);
	//埋雷
	set_mine(mine, ROW, COL);
	//print(mine, ROW, COL);(调试用)
	//玩家行动
	while (flag)
	{
		// flag=1 游戏继续
		// flag=0 游戏结束
		if (player_action(show, mine, ROW, COL) == 0)
			break;
		//打印棋盘
		print(show, ROW, COL);
		//判断棋盘是否翻完
		flag = judge_ogame(show, ROW, COL);
	}
}

 4.game.c

一、初始化棋盘(init_board)

由于我们这里需要初始化两个棋盘,所以为了通用性,我们需要将把棋盘内需要初始化的类型传进去就有了 char set

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

二、 打印棋盘(print)

访问方式类似与 ‘初始化棋盘’,再加一点点修饰

【纯手工扫雷“不含一点添加剂”】完全c语言版_第7张图片 红框为修饰部分

void print(char board[ROWS][COLS], int row, int col)
{
	int i, j;
	printf("  _________扫雷游戏_________\n");
	printf("  1  2  3  4  5  6  7  8  9\n");
	printf("  —————————————\n");
	for (i = 1; i < row + 1; i++)
	{
		printf("%d|", i);
		for (j = 1; j < col + 1; j++)
		{
			printf("%c  ", board[i][j]);
		}
		printf("\n");
	}
}

三、玩家的行动 (play_action)

1.这里我们分一下流,分为排雷标记雷

2.将 flag = 0 时,认定为游戏结束,flag = 1时,认定为游戏继续

(对应了test.c中start_game函数)

int player_action(char show_board[ROWS][COLS], char mine_board[ROWS][COLS])
{
	int op = 0;
	int flag = 1;
	printf("请输入你想\n1.排雷\n2.标记雷\n");
	scanf("%d", &op);
	switch (op)
	{
		//排雷
	case 1:
		flag = demine(show_board, mine_board, ROW, COL);
		break;
		//标记雷
	case 2:
		mark_mine(show_board, mine_board, ROW, COL);
		break;
	default:
		printf("输入错误,请重新输入!");
	}
	return flag;
}

四、埋雷(set_mine)

利用rand、srand与time函数生成的时间戳,生成随机数(srand在int main()主函数里一开始就设置好了)【纯手工扫雷“不含一点添加剂”】完全c语言版_第8张图片

void set_mine(char mine_board[ROWS][COLS], int row, int col)
{
	int x, y;
	for (int i = 0;i < MINES; )
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (mine_board[x][y] == '0')
		{
			//printf("%d %d\n", x, y);生成雷的坐标(调试检查用)
			mine_board[x][y] = '1';
			i++;
		}
	}
}

 五、排雷(demine)

排雷时如果排的是‘雷’话,那就game over

int demine(char show_board[ROWS][COLS], char mine_board[ROWS][COLS], int row, int col)
{
	int x, y;
	
	int flag = 1;
	
	while (1) {
		printf("请输入你想排雷的坐标:");
		scanf("%d %d", &x, &y);
		if (show_board[x][y] == '*') 
		{
			if (mine_board[x][y] == '1')
			{
				printf("你被炸死了!!游戏结束\n");
				return 0;
			}
			else if (mine_board[x][y] == '0')
			{
				over_mine(show_board, mine_board, x, y);
				return 1;
			}
		}
		else
			printf("坐标非法或已被占用,");	
	}
}

六、排雷时不是雷,连续翻格子(over_mine)

1.我们要筛选一下不合法已经翻过的坐标(因为“连续”翻会用到递归,可能会越界访问和重复去翻翻过的格子导致死循环)

 2.求该坐标(x,y)周围有几个雷

【纯手工扫雷“不含一点添加剂”】完全c语言版_第9张图片如图我们求(x,y)的坐标,该坐标就为周围8个格子雷数之和。                                                                      但是这里有个问题:我们之前定义棋盘时用的是char而不是int及,字符 '0' ≠ 整形 0,如果直接把8个坐标对应的数加起来它(字符对应的ascii码加起来)肯定不对。                                                         

举2个列子:char  0 - char 0  = int 0         char  1 - char 0  = int 1                           

根据上面的例子我们可以得到:char x - char y =int(x,y的ASCII码之差)

解决方法:相加时,将每个格子与char 0('0')相减,最后将结果再加上char 0('0')

特别的:为啥不先加起来在直接减去多余的'0'呢?因为ACSII码,有上限为“127”,如果直接加会溢出的!!!☠☠☠

 2.连续的实现:

【纯手工扫雷“不含一点添加剂”】完全c语言版_第10张图片 黄色周围没有雷

当(x,y)坐标周围一个雷都没有时,及该点为0,在展示面板(show)为空格 ,记录雷的面板(mine)上就为'0',

此时就该连续翻格子了

【纯手工扫雷“不含一点添加剂”】完全c语言版_第11张图片 炸金花般的连锁反应

如左图:我们从(x,y)向周围蔓延开,并求周围格子它们周围的雷数量。在此我们使用一个循环加递归的方式去实现该功能(在 六大点的1小点中我们设好了第一个终止条件:翻过的就不翻),第二个终止条件当然就是周围雷的数量不为' 0 '辣

【纯手工扫雷“不含一点添加剂”】完全c语言版_第12张图片 效果预览

代码如下:

void over_mine(char show_board[ROWS][COLS], char mine_board[ROWS][COLS], int x, int y)
{
	//判断坐标是否翻过与是否合法
	if (show_board[x][y] != ' ' && (x >= 1 && y >= 1 && x <= ROW && y <= COL)) {
		char num = '0';
		int i, j;
		for (i = -1;i <= 1;i++) {
			for (j = -1;j <= 1; j++) {
				num = mine_board[x + i][y + j] + num - '0';
			}
		}

		if (num == '0' )
		{
			show_board[x][y] = ' ';
			for (i = -1;i <= 1;i++) {
				for (j = -1;j <= 1; j++) {
					over_mine(show_board, mine_board, x + i, y + j);
				}
			}
		}
		else if (num != '0') {
			show_board[x][y] = num;
		}
	}	
}

七、标记雷(mark_mine)

标记的话,改一个字符就行

void mark_mine(char show_board[ROWS][COLS], char mine_board[ROWS][COLS], int row, int col)
{
	int x, y;
	while (1) {
		printf("请输入你想标记雷的坐标:");
		scanf("%d %d", &x, &y);
		if (show_board[x][y] == '*') {
			show_board[x][y] = 'X';
			break;
		}
		else
			printf("坐标非法或已被占用,");
	}
}

八、判断棋盘是否全部翻开(judge_ogame)

 实现原理:所以不是雷的格子全翻开,且是雷的格子要标记

int judge_ogame(char show_board[ROWS][COLS],char mine_board[ROWS][COLS], int row, int col) {
	int i, j;
	int mine = 0;//记录被标记的雷
	for (i = 1; i < row; i++)
	{
		for (j = 1; j < col; j++)
		{
			if (show_board[i][j] == '*') {
				return 1;
			}
			if (mine_board[i][j] == '1') {
				if (show_board[i][j] == 'X') {
					mine++;
				}
			}
		}
	}
	//将雷全部真确标记
	if (mine == MINES) {
		printf("         你赢了!!!             \n");
		return 0;
	}
	//未将雷全部真确标记
	else
		return 1;
}

附上源代码链接:扫雷源代码https://gitee.com/daylight-star/test.c/tree/master/%E6%89%AB%E9%9B%B7/%E6%89%AB%E9%9B%B7

你可能感兴趣的:(c语言,其他,开发语言)