C语言经典小游戏之扫雷(超详解释+源码)

“少年气,是历尽千帆举重若轻的沉淀,也是乐观淡然笑对生活的豁达!” 今天我们学习一下扫雷游戏怎么用C语言来实现!

1.游戏介绍

一个扫雷盘面由许多方格(cell)组成,方格中随机分布着一定数量的雷(mine),一个格子中至多只有1雷。胜利条件是打开所有安全格(非雷格,safe cell),失败条件是打开了一个雷格(踩雷)。下面图片中是一个9*9的示例:
在这里插入图片描述

2.游戏准备

和前面的三子棋一样,这里,我们也需要三个源文件来共同实现这个程序。
在这里插入图片描述

(1)头文件game.h,头文件里是用来存放函数的声明,#define常量的定义,库函数的引用的。
(2)源文件test.c,这个文件里面放的是游戏的测试逻辑。
(3)源文件game.c,这个文件里面放的是游戏的实现逻辑(函数实现)。

3.游戏实现

3.1生成菜单

这里呢,我们和三子棋一样,还是通过switch语句给用户选择,当用户输入不同的数字,我们的程序就会给出不同的功能。

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
#include <stdio.h>
void menu()
{
    
    
	printf("******************************\n");
	printf("*********   1.play   *********\n");
	printf("*********   0.exit   *********\n");
	printf("******************************\n");
}
void game()
{
    
    

}
int main()
{
    
    
	int input = 0;
	do
	{
    
    
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch(input)
		{
    
    
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

大致的框架就出来了,现在我们就需要看扫雷游戏的具体实现了。
在这里插入图片描述

3.2游戏的具体实现

这里我们简化游戏,设置一个9*9的棋盘,安置10个雷
排查过程如下:
1.如果这个位置是雷,那么游戏结束。
2.如果把不是雷的位置都找出来了,那么游戏结束。
3.如果这个位置不是雷,就计算这个位置的周围的8个格子有几个雷,并显示出雷的个数。

3.2.1初始化棋盘

我们这里的棋盘是9*9的,可以和三子棋一样,先将每个棋盘都初始化为0,如下图所示,然后有雷的地方填上1,如下图所示:
在这里插入图片描述
但是,我们从游戏规则中知道,当我们点到一个不是雷的格子的时候,要返回它周围八个格子中雷的个数。如下图中,如果我们点到了绿色1的那个格子,那么该位置将返回1这个值,此时,这里的1就会和表示雷的1就混起来了。
在这里插入图片描述
同时,当我们点到一个处于四边的格子的时候,还会出现越界的问题:
在这里插入图片描述
这个时候,我们就可以考虑在排查雷的时候,将棋盘扩展成11*11的棋盘。
在这里插入图片描述

同时,我们可以将排查雷的9*9的格子里都初始化为*,避免出现两个1意义不同混淆的情况。这样的话,我们就得到两个11*11的棋盘。
在这里插入图片描述

//game.h
#pragma once
#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);

//game.h*
//初始化棋盘的定义
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void Init_Board(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;
		}
	}
}

//test.c
void game()
{
    
    
	char assign[ROWS][COLS] = {
    
     0 };
	char show[ROWS][COLS] = {
    
     0 };
	Init_Board(assign,ROWS,COLS,'0');
	Init_Board(show, ROWS, COLS, '*');
}

初始化完成之后,我们想要验证一下对不对呢?这个时候,我们就需要将棋盘打印出来。

3.2打印棋盘

虽然我们这里初始化的是11*11的棋盘,但是用户需要的是中间区域的9*9,因此,我们只需要打印中间的9*9就可以了。

//game.h
//打印棋盘函数的声明
void Display_Board(char board[ROWS][COLS], int row, int col);

//game.c
//打印棋盘函数的定义
//我们只需要打印出中间的9*9的格子
void Display_Board(char board[ROWS][COLS], int row, int col)
{
    
    
	int i = 0;
	for (i = 1; i <= row; i++)
	{
    
    
		int j = 0;
		for (j = 1; j <= col; j++)
		{
    
    
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

//test.c
void game()
{
    
    
	Display_Board(assign, ROW, COL);
}

在这里插入图片描述
这里我们需要把行号和列号也打印出来,能够看的更清晰。代码进行这样的修改就好啦!
在这里插入图片描述
再优化一下,就如下图所示:
在这里插入图片描述

3.3布置雷

我们希望的是在这个9*9的棋盘里随机生成10个雷,这里我们就想到了能够产生随机数的函数rand()和srand(),使用这两个函数,需要添加头文件#include <time.h>和#include <stdlib.h>,同时,在主函数内还需要添加语句srand((unsigned int)time(NULL));来产生随机数的种子。

//game.h
#define Easy_Thunder 10
#include <stdlib.h>
#include <time.h>
//布置雷函数的声明
void Set_thunder(char board[ROWS][COLS], int row, int col);

//game.c
//布置雷函数的定义
void Set_thunder(char board[ROWS][COLS], int row, int col)
{
    
    
	int count = Easy_Thunder;
	while (count)
	{
    
    
		int x = rand() % row + 1;//生成横坐标
		int y = rand() % col + 1;//生成纵坐标
		if (board[x][y] == '0')//防止在同一个地方重复布雷
		{
    
    
			board[x][y] = '1';
			count--;
		}
	}
}

//test.c
void game()
{
    
    
	char assign[ROWS][COLS] = {
    
     0 };
	char show[ROWS][COLS] = {
    
     0 };
	Init_Board(assign,ROWS,COLS,'0');
	Init_Board(show, ROWS, COLS, '*');
	Display_Board(assign, ROW, COL);
	Set_thunder(assign, ROW, COL);
}
int main()
{
    
    
	srand((unsigned int)time(NULL));
	return 0;
}

那么,代码写好了,我们来验证一下,这样是不是能够布置出十个雷呢?
在这里插入图片描述

3.4排查雷

我们开始随机点一个格子,当这个格子不是雷的时候,可以计算它周围八个格子字符的ASCII码值,减去8个'0'的ASCII码值,就可以知道,这个格子周围有多少个雷了,然后我们加上'0'的ASCII码值就能在该处的格子上返回相应的字符。当我们把所有不是雷的格子找出来的时候,给用户提示排雷成功。当我们踩到雷的时候,也给出相应的提示给用户,游戏结束。这里和三子棋一样,我们可以添加清屏的操作system("cls");,让我们的游戏面板不是那么的冗长。

在这里插入图片描述

//game.h
//排查雷函数的声明
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//game.c
int Getassign(char assign[ROWS][COLS], int row, int col)//查找不是雷的格子周围有几个雷
{
    
    
	return (assign[row - 1][col - 1]//字符'0'的ASCII值为48,字符'1'的ASCII值为49
		+ assign[row - 1][col]
		+ assign[row - 1][col + 1]
		+ assign[row][col - 1]
		+ assign[row][col + 1]
		+ assign[row + 1][col - 1]
		+ assign[row + 1][col]
		+ assign[row + 1][col + 1] - 8 * '0');
}
//排查雷函数的定义
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    
    
	int x = 0;
	int y = 0;
	int win = 0;//计算没有踩到雷的次数
	while (win<col*row-Easy_Thunder)
	{
    
    
		printf("请输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
    
    
			if (assign[x][y] == '1')
			{
    
    
				printf("很遗憾,你被炸死了!\n");
				Display_Board(assign, ROW, COL);
				break;
			}
			else
			{
    
    
				//如果这个位置不是雷就统计周围八个格子雷的个数
				int c = Getassign(assign, x, y);
				show[x][y] = c + '0';
				Display_Board(show, ROW, COL);
				system("cls");
				Display_Board(show, ROW, COL);
				win++;
			}
		}
		else
		{
    
    
			printf("坐标非法,请重新输入!\n");
		}
	}
	if (win == row * col - Easy_Thunder)
	{
    
    
		printf("恭喜你排雷成功!\n");
		Display_Board(assign, ROW, COL);
	}	
}

//test.c
void game()
{
    
    
	Find_thunder(assign, show, ROW, COL);//排雷函数的引用
}

这里,我们随机输入,不用思考,试一下踩到雷的结果:
在这里插入图片描述
那么,如果我们仔细思考,看看当我们把所有不是雷的格子找出来的时候,能不能成功:
在这里插入图片描述

4.扫雷游戏的详细代码

//game.h
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define Easy_Thunder 10
#include <stdlib.h>
#include <time.h>
//初始化棋盘函数的声明
void Init_Board(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘函数的声明
void Display_Board(char board[ROWS][COLS], int row, int col);
//布置雷函数的声明
void Set_thunder(char board[ROWS][COLS], int row, int col);
//排查雷函数的声明
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘的定义
void Init_Board(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;
		}
	}
}
//打印棋盘函数的定义
//我们只需要打印出中间的9*9的格子
void Display_Board(char board[ROWS][COLS], int row, int col)
{
    
    
	int i = 0;
	int j = 0;
	printf("--------扫雷--------\n");
	for (i = 0; i <= col; i++)
	{
    
    
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
    
    
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
    
    
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
//布置雷函数的定义
void Set_thunder(char board[ROWS][COLS], int row, int col)
{
    
    
	int count = Easy_Thunder;//布置10个雷
	while (count)
	{
    
    
		int x = rand() % row + 1;//生成横坐标
		int y = rand() % col + 1;//生成纵坐标
		if (board[x][y] == '0')//防止在同一个地方重复布雷
		{
    
    
			board[x][y] = '1';
			count--;
		}
	}
}
int Getassign(char assign[ROWS][COLS], int row, int col)//查找不是雷的格子周围有几个雷
{
    
    
	return (assign[row - 1][col - 1]//字符'0'的ASCII值为48,字符'1'的ASCII值为49
		+ assign[row - 1][col]
		+ assign[row - 1][col + 1]
		+ assign[row][col - 1]
		+ assign[row][col + 1]
		+ assign[row + 1][col - 1]
		+ assign[row + 1][col]
		+ assign[row + 1][col + 1] - 8 * '0');
}
//排查雷函数的定义
void Find_thunder(char assign[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    
    
	int x = 0;
	int y = 0;
	int win = 0;//计算没有踩到雷的次数
	while (win<col*row-Easy_Thunder)
	{
    
    
		printf("请输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
    
    
			if (assign[x][y] == '1')
			{
    
    
				printf("很遗憾,你被炸死了!\n");
				Display_Board(assign, ROW, COL);
				break;
			}
			else
			{
    
    
				//如果这个位置不是雷就统计周围八个格子雷的个数
				int c = Getassign(assign, x, y);
				show[x][y] = c + '0';
				Display_Board(show, ROW, COL);
				system("cls");//清屏操作
				Display_Board(show, ROW, COL);
				win++;
			}
		}
		else
		{
    
    
			printf("坐标非法,请重新输入!\n");
		}
	}
	if (win == row * col - Easy_Thunder)
	{
    
    
		printf("恭喜你排雷成功!\n");
		Display_Board(assign, ROW, COL);
	}	
}

//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
#include <stdio.h>
void menu()
{
    
    
	printf("******************************\n");
	printf("*********   1.play   *********\n");
	printf("*********   0.exit   *********\n");
	printf("******************************\n");
}
void game()
{
    
    
	char assign[ROWS][COLS] = {
    
     0 };
	char show[ROWS][COLS] = {
    
     0 };
	Init_Board(assign,ROWS,COLS,'0');
	Init_Board(show, ROWS, COLS, '*');
	Display_Board(assign, ROW, COL);
	Set_thunder(assign, ROW, COL);
	Find_thunder(assign, show, ROW, COL);
}
int main()
{
    
    
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
    
    
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch(input)
		{
    
    
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

以上就是关于扫雷游戏的全部代码啦!当然这个程序还是存在可优化的空间(比如我们的游戏只能一个一个点,但是不能像网页版的那样能展示一片,这还有待思考),欢迎大家在评论区交流,优化代码。

猜你喜欢

转载自blog.csdn.net/qq_73121173/article/details/132120555