【扫雷游戏】C语言 用递归实现扫雷游戏

代码分为三部分,test.c(整体游戏框架),game.c(游戏的实现,定义的大部分函数),game.h(放的用到的头文件)

目录

一、游戏流程

二、定义的函数

三、函数的实现

1.void menu();

2.void game();

2.void Initboard(char board[ROWS][COLS],int row,int col,char set);

4.void Displayboard(char board[ROWS][COLS], int row, int col);

 5.void leiboard(char board[ROWS][COLS], int row, int col);

6. void usermove(char board[ROWS][COLS], char userboard[ROWS][COLS], int row, int col);

 7.int leicount(char board[ROWS][COLS],  int x, int y);

8.void openaround(char board[ROWS][COLS], char userboard[ROWS][COLS], int x, int y);

 9.int iswin(char userboard[ROWS][COLS], int row, int col);

四、完整的代码

1.test.c

2.game.c

3.game.h

五、游戏测试

1.你赢了(步骤有点多,可以自己去玩一下嘻嘻)

 2.你输了(我这个运气,第一个子儿就被炸死了哈哈)

3.输入坐标非法请重新输入

4.你已经排查过此位置,请重新输入


一、游戏流程

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请输入:>");
		scanf_s("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("游戏结束\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

二、定义的函数

void menu();
void game();
void Initboard(char board[ROWS][COLS],int row,int col,char set);
void Displayboard(char board[ROWS][COLS], int row, int col);
void leiboard(char board[ROWS][COLS], int row, int col);
void usermove(char board[ROWS][COLS], char userboard[ROWS][COLS], int row, int col);
//返回类型为整型,返回用户输入坐标周围雷的个数
int leicount(char board[ROWS][COLS],  int x, int y);
void openaround(char board[ROWS][COLS], char userboard[ROWS][COLS], int x, int y);
int iswin(char userboard[ROWS][COLS], int row, int col);

三、函数的实现

1.void menu();

void menu()
{
	printf("*********************************\n");
	printf("*********   1.play     **********\n");
	printf("*********   0.end      **********\n");
	printf("*********************************\n");
}

2.void game();

void game()
{
	char board1[ROWS][COLS];
	char board2[ROWS][COLS];
	printf("开始游戏\n");
	//初始化扫雷棋盘
	Initboard(board1, ROW, COL, '0');
	//给用户展示的排雷页面的初始化
	//放雷,把雷初始化好,雷是放在棋盘1
	leiboard(board1, ROW, COL);
	Initboard(board2, ROW, COL, '*');
	//打印一下给用户展示的棋盘
	Displayboard(board2, ROW, COL);
	//这个打印棋盘
	//1.初始化为0以及放入雷后的棋盘
	//2.用户排雷失败被炸死的时候,让用户被炸的明白用这个给打印出来
	//3.让程序员好用来测试完成扫雷游戏,最后注释掉
	//Displayboard(board1, ROW, COL);
	//用户进行扫雷,输入要排的坐标,都是在棋盘1上进行的,棋盘2,只是为了方便给用户展示,总不能把雷的位置也给用户展示了吧
	usermove(board1, board2, ROW, COL);

}

2.void Initboard(char board[ROWS][COLS],int row,int col,char set);

//初始化棋盘
//多初始化的两行,两列棋盘是为了判断边缘是否有雷时,数组会被越界访问
void Initboard(char board[ROWS][COLS], int row, int col, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row + 2; i++)
	{
		for (j = 0; j < col + 2; j++)
		{
			board[i][j] = set;
		}
	}
}

4.void Displayboard(char board[ROWS][COLS], int row, int col);

//打印1-9的初始化的那部分棋盘
void Displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 1;
	int j = 1;
	for (i = 0; i < row + 1; i++)
	{
		printf("%d ", i);
	}
	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");
	}
}

 5.void leiboard(char board[ROWS][COLS], int row, int col);

//放雷
void leiboard(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int i = 0;
	//放十个雷,用字符1表示
	for (i = 0; i < pri_count; i++)
	{
		//用随机数产生1-9的随机数(再棋盘为9*9的情况下,改变ROW和COL会改变)
		x = rand() % row + 1;//放到内圈 整个大圈时0-10 内圈是需要初始化和放雷的 即1-9
		y = rand() % col + 1;
		//这个if也是为了让雷在内圈,多的两行两列只是为了判断边缘的雷的情况,如果不设置的话会比较麻烦,或者会越界访问数组。
		//还有个条件就是这个地方没有雷才能放
		if (x > 0 && x < row + 1 && y>0 && y < col + 1&& board[x][y] == '0')
		{
			board[x][y] = '1';
		}
		else
			i--;//因为如果上一个if没进去的话,i就多加了一个1,但是没有放上雷,所以就减去一个1
	}
}

6. void usermove(char board[ROWS][COLS], char userboard[ROWS][COLS], int row, int col);

void usermove(char board[ROWS][COLS], char userboard[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	do
	{
		printf("请输入你要排除的坐标:>");
		scanf_s("%d %d", &x, &y);
		//判断输入的坐标是否正确
		if (x > 0 && x < row + 1 && y>0 && y < col + 1)
		{
			//判断是否为雷
			if (board[x][y] == '1')
			{
				printf("很遗憾,你被炸死了,哈哈哈,太遗憾了\n");
				printf("下面是本局雷的分布\n");
				Displayboard(board, ROW, COL);//被炸死后把这个棋盘打印出来,让用户知道自己踩了哪里的雷,以及雷都分布在哪里
				break;
			}
			else if(board[x][y] == '0' && userboard[x][y] == '*')
			{
				//设计一个函数,计算这个坐标周围八个位置的雷的数量
				userboard[x][y] = leicount(board, x, y);
				openaround(board, userboard, x, y);
				Displayboard(userboard, ROW, COL);
				//判断棋盘上剩余的雷的个数,等于初级设计的地雷的个数,就赢了
				if (iswin(userboard, row, col) == pri_count)
				{
					printf("恭喜你,赢了!!!!\n");
					Displayboard(board, ROW, COL);
					break;
				}
				//if (win == row * col - pri_count)
				//{
				//	printf("恭喜你,赢了!!!!\n");
				//	break;
				//}
			}
			else {
				printf("你已经排查过这个位置,请重新输入!\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	} while(1);
}

 7.int leicount(char board[ROWS][COLS],  int x, int y);

(//返回类型为整型,返回用户输入坐标周围雷的个数)

int leicount(char board[ROWS][COLS], int x, int y)
{
	return board[x - 1][y] + board[x - 1][y + 1] +
	board[x][y + 1] + board[x + 1][y + 1] +
	board[x + 1][y] + board[x + 1][y - 1] +
	board[x][y - 1] + board[x - 1][y - 1] - 8 * '0';
}
	//字符1和字符0 的ASCLL码的十进制相差1,所以相减为1
	// 有雷的话减去字符0,就表示一颗雷,所以八个坐标全判断一下
	//例:(用户坐标头上的这个位置)board[x-1][y]-'0'
	//如果每一个都减'0'就比较繁琐,所以直接八个坐标的位置直接相加 减去八倍的字符0	
    //所以以用户输入的坐标为中心

8.void openaround(char board[ROWS][COLS], char userboard[ROWS][COLS], int x, int y);

void openaround(char board[ROWS][COLS], char userboard[ROWS][COLS], int x, int y)
{
	if (x > 0 && x < ROW + 1 && y>0 && y < COL + 1)
	{
		//这里两个限制条件,第一个限制条件就是为了展开没有雷的位置的周围的雷的情况
		//第二个限制条件就是为了防止弄成死递归,可以想一下,如果不限制,就会进入死递归,最终导致栈溢出
		if (leicount(board, x, y) == 0 && userboard[x][y] != ' ')
		{
			userboard[x][y] = ' ';
				openaround(board, userboard, x - 1, y);
				openaround(board, userboard, x - 1, y + 1);
				openaround(board, userboard, x, y + 1);
				openaround(board, userboard, x + 1, y + 1);
				openaround(board, userboard, x + 1, y);
				openaround(board, userboard, x + 1, y - 1);
				openaround(board, userboard, x, y - 1);
				openaround(board, userboard, x - 1, y - 1);
		}
		//这个限制条件也是为了防止把已经赋值为空格的坐标又给赋值为0了
		else if(userboard[x][y] != ' ')
		{
			userboard[x][y] = leicount(board, x, y) + '0';
			//这里解释一下 为什么加一个字符0
			//因为首先返回来的是一个整型,而这个数组是一个字符型
			//一个整型被进行强制类型转换为字符型的话,也会造成二进制位的丢失,打印出来也不确定打印棋盘时,打印出来的也不确定
			//所以这里加上字符0,字符零在其十进制上加返回来的十进制位数,对应的也是字符的也是整型的那个数
		}
	}
}

 9.int iswin(char userboard[ROWS][COLS], int row, int col);

int iswin(char userboard[ROWS][COLS],int row,int col)
{
	int win = 0;
	int i = 0;
	int j = 0;
	for (i = 1; i < row + 1; i++)
	{
		for (j = 1; j < col + 1; j++)
		{
			if (userboard[i][j] == '*')
			{
				win++;
				//printf("%d\n", win);这个是我当时测试我的win是不是正确的时候弄得,我的思考过程,就注释掉,不删了吧
			}
		}
	}
	return win;
}

四、完整的代码

1.test.c

#define _CRT_SECURE_NO_WARNINGS 1//这个是因为我用的vs编译器认为scanf函数不安全,会报错

#include "game2.h"

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请输入:>");
		scanf_s("%d", &input);//因为加了上面那个不报错了,但是有警告,所以我还使用了scanf_s
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("游戏结束\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

2.game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game2.h"

void menu()
{
	printf("*********************************\n");
	printf("*********   1.play     **********\n");
	printf("*********   0.end      **********\n");
	printf("*********************************\n");
}

void game()
{
	char board1[ROWS][COLS];
	char board2[ROWS][COLS];
	printf("开始游戏\n");
	//初始化扫雷棋盘
	Initboard(board1, ROW, COL, '0');
	//给用户展示的排雷页面的初始化
	//放雷,把雷初始化好,雷是放在棋盘1
	leiboard(board1, ROW, COL);
	Initboard(board2, ROW, COL, '*');
	//打印一下给用户展示的棋盘
	Displayboard(board2, ROW, COL);
	//这个打印棋盘
	//1.初始化为0以及放入雷后的棋盘
	//2.用户排雷失败被炸死的时候,让用户被炸的明白用这个给打印出来
	//3.让程序员好用来测试完成扫雷游戏,最后注释掉
	//Displayboard(board1, ROW, COL);
	//用户进行扫雷,输入要排的坐标,都是在棋盘1上进行的,棋盘2,只是为了方便给用户展示,总不能把雷的位置也给用户展示了吧
	usermove(board1, board2, ROW, COL);

}

//初始化棋盘
//多初始化的两行,两列棋盘是为了判断边缘是否有雷时,数组会被越界访问
void Initboard(char board[ROWS][COLS], int row, int col, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row + 2; i++)
	{
		for (j = 0; j < col + 2; j++)
		{
			board[i][j] = set;
		}
	}
}

//放雷
void leiboard(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int i = 0;
	//放十个雷,用字符1表示
	for (i = 0; i < pri_count; i++)
	{
		//用随机数产生1-9的随机数(再棋盘为9*9的情况下,改变ROW和COL会改变)
		x = rand() % row + 1;//放到内圈 整个大圈时0-10 内圈是需要初始化和放雷的 即1-9
		y = rand() % col + 1;
		//这个if也是为了让雷在内圈,多的两行两列只是为了判断边缘的雷的情况,如果不设置的话会比较麻烦,或者会越界访问数组。
		//还有个条件就是这个地方没有雷才能放
		if (x > 0 && x < row + 1 && y>0 && y < col + 1&& board[x][y] == '0')
		{
			board[x][y] = '1';
		}
		else
			i--;//因为如果上一个if没进去的话,i就多加了一个1,但是没有放上雷,所以就减去一个1
	}
}

//打印1-9的初始化的那部分棋盘
void Displayboard(char board[ROWS][COLS], int row, int col)
{
	int i = 1;
	int j = 1;
	for (i = 0; i < row + 1; i++)
	{
		printf("%d ", i);
	}
	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");
	}
}

int leicount(char board[ROWS][COLS], int x, int y)
{
	return board[x - 1][y] + board[x - 1][y + 1] +
	board[x][y + 1] + board[x + 1][y + 1] +
	board[x + 1][y] + board[x + 1][y - 1] +
	board[x][y - 1] + board[x - 1][y - 1] - 8 * '0';
}

void openaround(char board[ROWS][COLS], char userboard[ROWS][COLS], int x, int y)
{
	if (x > 0 && x < ROW + 1 && y>0 && y < COL + 1)
	{
		//这里两个限制条件,第一个限制条件就是为了展开没有雷的位置的周围的雷的情况
		//第二个限制条件就是为了防止弄成死递归,可以想一下,如果不限制,就会进入死递归,最终导致栈溢出
		if (leicount(board, x, y) == 0 && userboard[x][y] != ' ')
		{
			userboard[x][y] = ' ';
				openaround(board, userboard, x - 1, y);
				openaround(board, userboard, x - 1, y + 1);
				openaround(board, userboard, x, y + 1);
				openaround(board, userboard, x + 1, y + 1);
				openaround(board, userboard, x + 1, y);
				openaround(board, userboard, x + 1, y - 1);
				openaround(board, userboard, x, y - 1);
				openaround(board, userboard, x - 1, y - 1);
		}
		//这个限制条件也是为了防止把已经赋值为空格的坐标又给赋值为0了
		else if(userboard[x][y] != ' ')
		{
			userboard[x][y] = leicount(board, x, y) + '0';
			//这里解释一下 为什么加一个字符0
			//因为首先返回来的是一个整型,而这个数组是一个字符型
			//一个整型被进行强制类型转换为字符型的话,也会造成二进制位的丢失,打印出来也不确定打印棋盘时,打印出来的也不确定
			//所以这里加上字符0,字符零在其十进制上加返回来的十进制位数,对应的也是字符的也是整型的那个数
		}
	}
}

int iswin(char userboard[ROWS][COLS],int row,int col)
{
	int win = 0;
	int i = 0;
	int j = 0;
	for (i = 1; i < row + 1; i++)
	{
		for (j = 1; j < col + 1; j++)
		{
			if (userboard[i][j] == '*')
			{
				win++;
				//printf("%d\n", win);这个是我当时测试我的win是不是正确的时候弄得,我的思考过程,就注释掉,不删了吧
			}
		}
	}
	return win;
}

void usermove(char board[ROWS][COLS], char userboard[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	do
	{
		printf("请输入你要排除的坐标:>");
		scanf_s("%d %d", &x, &y);
		//判断输入的坐标是否正确
		if (x > 0 && x < row + 1 && y>0 && y < col + 1)
		{
			//判断是否为雷
			if (board[x][y] == '1')
			{
				printf("很遗憾,你被炸死了,哈哈哈,太遗憾了\n");
				printf("下面是本局雷的分布\n");
				Displayboard(board, ROW, COL);//被炸死后把这个棋盘打印出来,让用户知道自己踩了哪里的雷,以及雷都分布在哪里
				break;
			}
			else if(board[x][y] == '0' && userboard[x][y] == '*')
			{
				//设计一个函数,计算这个坐标周围八个位置的雷的数量
				userboard[x][y] = leicount(board, x, y);
				openaround(board, userboard, x, y);
				Displayboard(userboard, ROW, COL);
				//判断棋盘上剩余的雷的个数,等于初级设计的地雷的个数,就赢了
				if (iswin(userboard, row, col) == pri_count)
				{
					printf("恭喜你,赢了!!!!\n");
					Displayboard(board, ROW, COL);
					break;
				}
				//if (win == row * col - pri_count)
				//{
				//	printf("恭喜你,赢了!!!!\n");
				//	break;
				//}
			}
			else {
				printf("你已经排查过这个位置,请重新输入!\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
		}
	} while(1);
}

3.game.h

#pragma once

#define ROW 9
#define COL 9

#define ROWS ROW + 2
#define COLS COL + 2

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

//初级版本的雷的数量
#define pri_count 10

void menu();
void game();
void Initboard(char board[ROWS][COLS],int row,int col,char set);
void Displayboard(char board[ROWS][COLS], int row, int col);
void leiboard(char board[ROWS][COLS], int row, int col);
void usermove(char board[ROWS][COLS], char userboard[ROWS][COLS], int row, int col);
//返回类型为整型,返回用户输入坐标周围雷的个数
int leicount(char board[ROWS][COLS],  int x, int y);
void openaround(char board[ROWS][COLS], char userboard[ROWS][COLS], int x, int y);
int iswin(char userboard[ROWS][COLS], int row, int col);

五、游戏测试

1.你赢了(步骤有点多,可以自己去玩一下嘻嘻)

 

 

 

 

 

 

 

 

 

 

 

 

 2.你输了(我这个运气,第一个子儿就被炸死了哈哈)

3.输入坐标非法请重新输入

4.你已经排查过此位置,请重新输入

 最后希望可以帮助到大家,去尝试一下~

有bug请指正我!

猜你喜欢

转载自blog.csdn.net/m0_57549888/article/details/124328485