扫雷游戏的实现——C语言版本(1万字超详细解析)

扫雷游戏做为c语言初学者的编程练习还是很不错的,那么本篇文章详细注释了扫雷代码中的各行

代码的含义以及作用,对初学者来说还是很有帮助的我们如果玩过扫雷游戏就应该对下面的图片会

较为收悉

 

下面我们从这几个方面进行讲解

一、扫雷进阶版游戏- C语言程序实现

二、程序实现的介绍

三、如何用代码实现扫雷程序

        1.头文件的编写

        2.游戏文本的实现

        3.主程序的思路介绍

四、扫雷程序的重点突破

        1.设置棋盘大小(9x9)

        2.初始化棋盘

        4.打印棋盘

          4.1.设置棋盘中的雷(10个)

        5.排雷的实现

          5.1排雷过程中的展开实现

下面向大家展示头文件的内容

#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 EASY_COUNT 10
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char ret);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//设置雷的函数
void SetMine(char board[ROWS][COLS], int row, int col);
//找雷函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//展开棋盘函数
void OpenBoard(char mine[ROWS][COLS],char show[ROWS][COLS],int row, int col);
//统计一个坐标旁边对应8个坐标内雷的个数
int GetMine_COUNT(char board[ROW][COL], int row, int col);

 一、头文件里面主要是对后续game.c和test.c文件内所使用函数的一个声明,在使用

函数之前先告诉我们的编译器我们有这些函数

二、游戏文本的实现说明

    在进行编写我们的代码之前我们应该有一个基本的关于扫雷程序的思路

1、棋盘思路

    首先我们想到在扫雷之前我们应该先将我们的游戏棋盘展现出来当然我们能实现的对

棋盘的展示很容易的可以想到去使用二维数组去展示一个9x9的棋盘

2.布置雷的思路以及定义俩个二维数组

之后在在展现的棋盘上布置雷,当然这个时候我们会产生困惑,如果我们布置雷的图像和我们扫雷过程中统计雷的数字,在同一个棋盘上,那么我们不便于区分棋盘所传达的意思,那么我们需要在创建棋盘的时候创建俩个二维数组一个储存雷的信息一个储存扫雷的信息,

3.对没雷情况的展开

在布置好雷之后能在二维数组去进行访问每一个数组内的信息,确定访问是雷或者是旁

边有雷的情况,如果我们访问的位置没有雷他的旁边也没有雷,那么我们就可以通过写

一个递归的方式去展开他旁边没有雷的情况,提高玩家的游戏体验

我们先将test.c的代码展现给大家

#include "game.h"
void menu()//菜单提示玩家如何选择
{
	printf("********************************\n");
	printf("********   1.play   ************\n");
	printf("********   0.exit   ************\n");
	printf("********************************\n");
}
void game()
{
	char mine[ROWS][COLS] = { 0 };//存放雷的棋盘
	char show[ROWS][COLS] = { 0 };//扫雷过程的棋盘
	
    //对存放雷的棋盘进行初始化的函数,并将我们规定的符号传进去
    InitBoard(mine, ROW, COL,'0');
	
    //DisplayBoard(mine, ROW, COL);//打印棋盘的函数在初始化之后,可以先进行打印看棋盘是否正确
   
    //再次调用该函数对扫雷的棋盘进行初始化,并将我们规定的符号传进去
	InitBoard(show, ROW, COL,'*');

    //设置雷的函数,需要的是存放雷的棋盘
	SetMine(mine, ROW, COL);
	
    //最后打印我们的棋盘让玩家可以进行棋盘位置的使用
	DisplayBoard(show, ROW, COL);
	
    //排雷函数需要传入含雷的棋盘 和 扫雷的棋盘,这样可以对棋盘信息进行传递
	FindMine(mine, 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");
		}
	} while(input);
	return 0;
}

下面我们将game.c的游戏函数展现给大家

#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char ret)
{
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= col; j++)
		{
          //因为不能同时确保ret是俩个不同的字符所以我们可能在调用不同数组时传送过来不同的字符
			board[i][j] = ret;
		}
	}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);//打印 1 - 10个数来告诉玩家每个'*'的坐标
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{

		int j = 0;
		printf("%d ", i);//打印行方向的1-9个数
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//将二维数组进行打印
		}
		printf("\n");
	}
}
//设置雷的函数
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
//这个EASY_COUNT的是我们想要设置的雷的个数 在game.h中有包含  
//如果想要改变雷的个数可以直接去game.h去进行修改
	while (count)
	{
		//这里是进行随机数调用
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';//在这里我们规定字符 '1' 为雷
			count--;//每成功布置一个雷就进行count - 1
		}
	}
}
//找雷函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("要排除的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该坐标已经被排查过,不能重复排查\n");
			}
			else
			{
				if (mine[x][y] == '1')
				{
					printf("你被炸死了\n");
					DisplayBoard(mine, ROW, COL);//如果被炸死打印棋盘让玩家看见棋盘位置
					break;
				}
				else
				{
                    //如果这个坐标没有雷则进行周围坐标的排查并进行展开
					OpenBoard(mine, show, x, y);
					DisplayBoard(show, ROW, COL);//打印展开后的新的棋盘
				}
			}
		}
		else
		{
			printf("坐标不合法请重新输入\n");
		}
		int i = 0;
		int flag = 0;//我们的胜利情况是对棋盘上字符 '*' 的判断
		for (i = 1; i <= row; i++)
		{
			int j = 0;
			for (j = 1; j <= col; j++)
			{
				if (show[i][j] == '*')
				{
					flag++;  //棋盘上如果有 '*' 这对flag++
				}
			}
		}
		if (flag == EASY_COUNT)//当flag 等于我们雷的个数 那么游戏胜利
		{
			printf("恭喜你扫雷成功\n");
			DisplayBoard(mine, ROW, COL);
			break;
		}
	}
}
//统计一个坐标旁边对应8个坐标内雷的个数
int GetMine_COUNT(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	
	int count = 0;
	for (i = row - 1; i <= row + 1; i++)
	{
		int j = 0;
		for (j = col - 1; j <= col + 1; j++)
		{
			if (board[i][j] == '1')//判断旁边是否是雷
			{
				count++;//有雷就进行count++
			}
		}
	}
	return count;
}
//展开棋盘函数
void OpenBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	//对接收的坐标进行判断看是否超出我们棋盘的范围
	if (row > 0 && row <= ROW && col > 0 && col <= COL)
	{
		//判断在扫描的坐标位置雷的个数
		int count = GetMine_COUNT(mine, row, col);
		if (count != 0)
		{
			show[row][col] = count + '0';//如果旁边有雷将雷的信息返回给排查的这个坐标,  
		}								 //count+ '0' 将其转化为相应的 字符数字
                                 //(我们定义的雷为字符'1' 为了保持统一所以我们进行一定区分)
		else if (show[row][col] != ' ')
		{
            //如果这个坐标旁边8个坐标都没有雷这进行展开并给该点赋值为 空格 ' ';
			show[row][col] = ' ';
			int i = 0;
			for (i = row - 1; i <= row + 1; i++)
			{
				int j = 0;
				for (j = col - 1; j <= col + 1; j++)
				{
                   //最后我们进行反复调用这个展开函数去判断周围没有雷的所以点
					OpenBoard(mine, show, i, j);
				}
			}
		}
		else
		{
			return ;
		}
	}
}

下面我们对上面的 代码进行解析

void InitBoard(char board[ROWS][COLS], int row, int col, char ret)
{
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= col; j++)
		{
            //因为不能同时确保ret是俩个不同的字符所以我们可能在调用不同数组时传送过来不同的字符
			board[i][j] = ret;
		}
	}
}

这个主要是需要在额外传入一个字符 ‘ *’  或者字符 ‘1’ 来对棋盘初始化ret进行赋值这样可以节约计算机的空间 用一个函数去初始化俩个棋盘

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);//打印 1 - 10个数来告诉玩家每个'*'的坐标
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{

		int j = 0;
		printf("%d ", i);//打印行方向的1-9个数
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//将二维数组进行打印
		}
		printf("\n");
	}
}

 打印棋盘在打印棋盘之前先打印一行0-9的数字 在打印列之前在打印 1 - 9 个数字来告诉玩家每个 ' * ' 的坐标是什么,提高玩家的游戏体验

//设置雷的函数
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;//这个EASY_COUNT的是我们想要设置的雷的个数 在game.h中有包含  如果想要改变雷的个数可以直接去game.h去进行修改
	while (count)
	{
		//这里是进行随机数调用
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';//在这里我们规定字符 '1' 为雷
			count--;//每成功布置一个雷就进行count - 1
		}
	}
}

 设置雷的函数因为是需要随机设置那么我们就需要去引用一个为时间戳的函数去生成随机值不知道想小伙伴可以点开链接去看看时间戳

 这个时间戳是一直在发生变化的 为了引用他 我们需要在test.c里面写一个他的声明

#include<time.h>
#include<stdlib.h>
srand((unsigned int)time(NULL));

 其中#include<time.h>和#include<stdlib.h>是引用这个函数所需的头文件srand((unsigned int)time(NULL));是对rand的使用的声明

//找雷函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("要排除的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该坐标已经被排查过,不能重复排查\n");
			}
			else
			{
				if (mine[x][y] == '1')
				{
					printf("你被炸死了\n");
					DisplayBoard(mine, ROW, COL);//如果被炸死打印棋盘让玩家看见棋盘位置
					break;
				}
				else
				{
                    //如果这个坐标没有雷则进行周围坐标的排查并进行展开
					OpenBoard(mine, show, x, y);
					DisplayBoard(show, ROW, COL);//打印展开后的新的棋盘
				}
			}
		}
		else
		{
			printf("坐标不合法请重新输入\n");
		}
		int i = 0;
		int flag = 0;//我们的胜利情况是对棋盘上字符 '*' 的判断
		for (i = 1; i <= row; i++)
		{
			int j = 0;
			for (j = 1; j <= col; j++)
			{
				if (show[i][j] == '*')
				{
					flag++;  //棋盘上如果有 '*' 这对flag++
				}
			}
		}
		if (flag == EASY_COUNT)//当flag 等于我们雷的个数 那么游戏胜利
		{
			printf("恭喜你扫雷成功\n");
			DisplayBoard(mine, ROW, COL);
			break;
		}
	}
}

 找雷函数需要注意的是,在传递数组的时候需要将设置了雷的数字mine[ROWS]

[COLS]和排查雷信息的数字show[ROWS][COLS]传递过来,这便于我们去统计和传递

出棋盘上真实的信息,当然细心的小伙伴应该已经发现了我们需要的棋盘是9x9的棋

盘,那么为什么棋盘的设置是12X12的呢。下面我来展示一种雷的存放位置

如果我们遇到的是这种放雷的方式,我们设置的是9x9的棋盘,那么在统计雷的个数的

时候,是不是就不能每次都排查这个坐标周围8个坐标的信息了,因为如果在角落我们

去排查他周围的8个坐标就会产生一个越界问题,如果去设置一个函数去判断我们排查

的点是否越界,那么我们排查的每个点都需要去判断,那这大大降低了我们程序运算的

速度,为了避免这种情况,我们在设置棋盘的时候设置棋盘为12x12的棋盘,但是在排

查雷和设置雷的时候我们只使用9x9的这一部分,这样就很方便的解决了越界问题

 

//统计一个坐标旁边对应8个坐标内雷的个数
int GetMine_COUNT(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	
	int count = 0;
	for (i = row - 1; i <= row + 1; i++)
	{
		int j = 0;
		for (j = col - 1; j <= col + 1; j++)
		{
			if (board[i][j] == '1')//判断旁边是否是雷
			{
				count++;//有雷就进行count++
			}
		}
	}
	return count;
}

 排查雷的时候我们将坐标的(x,y);分别传递给 row 和 col 然后排查旁边雷的个数

//展开棋盘函数
void OpenBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	//对接收的坐标进行判断看是否超出我们棋盘的范围
	if (row > 0 && row <= ROW && col > 0 && col <= COL)
	{
		//判断在扫描的坐标位置雷的个数
		int count = GetMine_COUNT(mine, row, col);
		if (count != 0)
		{
			show[row][col] = count + '0';//如果旁边有雷将雷的信息返回给排查的这个坐标,  
		}								 //count+ '0' 将其转化为相应的 字符数字
                                 //(我们定义的雷为字符'1' 为了保持统一所以我们进行一定区分)
		else if (show[row][col] != ' ')
		{
            //如果这个坐标旁边8个坐标都没有雷这进行展开并给该点赋值为 空格 ' ';
			show[row][col] = ' ';
			int i = 0;
			for (i = row - 1; i <= row + 1; i++)
			{
				int j = 0;
				for (j = col - 1; j <= col + 1; j++)
				{
                   //最后我们进行反复调用这个展开函数去判断周围没有雷的所以点
					OpenBoard(mine, show, i, j);
				}
			}
		}
		else
		{
			return ;
		}
	}
}

 这是一个展开函数,当然为了防止重复访问我们将每个周围没了雷的坐标更新为空格 

'   ' 这样我们就能在展开判断之前先判断向周围展开之前这个坐标是否是空格 '   ' 如果

是空格那就不在以这个点进行展开判断这个就不会出现栈溢出的情况

最后将代码放完整放出

#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 EASY_COUNT 10
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char ret);
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//设置雷的函数
void SetMine(char board[ROWS][COLS], int row, int col);
//找雷函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//展开棋盘函数
void OpenBoard(char mine[ROWS][COLS],char show[ROWS][COLS],int row, int col);
//统计一个坐标旁边对应8个坐标内雷的个数
int GetMine_COUNT(char board[ROW][COL], int row, int col);

 

#include"game.h"
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char ret)
{
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= col; j++)
		{
          //因为不能同时确保ret是俩个不同的字符所以我们可能在调用不同数组时传送过来不同的字符
			board[i][j] = ret;
		}
	}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);//打印 1 - 10个数来告诉玩家每个'*'的坐标
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{

		int j = 0;
		printf("%d ", i);//打印行方向的1-9个数
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//将二维数组进行打印
		}
		printf("\n");
	}
}
//设置雷的函数
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
//这个EASY_COUNT的是我们想要设置的雷的个数 在game.h中有包含  
//如果想要改变雷的个数可以直接去game.h去进行修改
	while (count)
	{
		//这里是进行随机数调用
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';//在这里我们规定字符 '1' 为雷
			count--;//每成功布置一个雷就进行count - 1
		}
	}
}
//找雷函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("要排除的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] != '*')
			{
				printf("该坐标已经被排查过,不能重复排查\n");
			}
			else
			{
				if (mine[x][y] == '1')
				{
					printf("你被炸死了\n");
					DisplayBoard(mine, ROW, COL);//如果被炸死打印棋盘让玩家看见棋盘位置
					break;
				}
				else
				{
                    //如果这个坐标没有雷则进行周围坐标的排查并进行展开
					OpenBoard(mine, show, x, y);
					DisplayBoard(show, ROW, COL);//打印展开后的新的棋盘
				}
			}
		}
		else
		{
			printf("坐标不合法请重新输入\n");
		}
		int i = 0;
		int flag = 0;//我们的胜利情况是对棋盘上字符 '*' 的判断
		for (i = 1; i <= row; i++)
		{
			int j = 0;
			for (j = 1; j <= col; j++)
			{
				if (show[i][j] == '*')
				{
					flag++;  //棋盘上如果有 '*' 这对flag++
				}
			}
		}
		if (flag == EASY_COUNT)//当flag 等于我们雷的个数 那么游戏胜利
		{
			printf("恭喜你扫雷成功\n");
			DisplayBoard(mine, ROW, COL);
			break;
		}
	}
}
//统计一个坐标旁边对应8个坐标内雷的个数
int GetMine_COUNT(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	
	int count = 0;
	for (i = row - 1; i <= row + 1; i++)
	{
		int j = 0;
		for (j = col - 1; j <= col + 1; j++)
		{
			if (board[i][j] == '1')//判断旁边是否是雷
			{
				count++;//有雷就进行count++
			}
		}
	}
	return count;
}
//展开棋盘函数
void OpenBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	//对接收的坐标进行判断看是否超出我们棋盘的范围
	if (row > 0 && row <= ROW && col > 0 && col <= COL)
	{
		//判断在扫描的坐标位置雷的个数
		int count = GetMine_COUNT(mine, row, col);
		if (count != 0)
		{
			show[row][col] = count + '0';//如果旁边有雷将雷的信息返回给排查的这个坐标,  
		}								 //count+ '0' 将其转化为相应的 字符数字
                                 //(我们定义的雷为字符'1' 为了保持统一所以我们进行一定区分)
		else if (show[row][col] != ' ')
		{
            //如果这个坐标旁边8个坐标都没有雷这进行展开并给该点赋值为 空格 ' ';
			show[row][col] = ' ';
			int i = 0;
			for (i = row - 1; i <= row + 1; i++)
			{
				int j = 0;
				for (j = col - 1; j <= col + 1; j++)
				{
                   //最后我们进行反复调用这个展开函数去判断周围没有雷的所以点
					OpenBoard(mine, show, i, j);
				}
			}
		}
		else
		{
			return ;
		}
	}
}

 

#include "game.h"
void menu()
{
	printf("********************************\n");
	printf("********   1.play   ************\n");
	printf("********   0.exit   ************\n");
	printf("********************************\n");
}
void game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	InitBoard(mine, ROW, COL,'0');
	//DisplayBoard(mine, ROW, COL);

	InitBoard(show, ROW, COL,'*');

	SetMine(mine, ROW, COL);
	
	DisplayBoard(show, ROW, COL);
	
	FindMine(mine, 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");
		}
	} while(input);
	return 0;
}

到此进全部结束,后续我还跟更新有关C语言的知识

最后谢谢各位小伙伴的观看,如果对你有帮助请点个

 

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值