用C语言实现扫雷小游戏(简单)


游戏简介

扫雷游戏是一款经典的益智游戏,其目标是在最短的时间内找出所有非雷方格,同时避免踩到地雷。游戏开始时,会随机在棋盘上布置一定数量的地雷,玩家通过点击方格来揭示其周围的数字,从而推断出该方格下是否有雷。如果方格下有雷,游戏立即结束;如果没有雷,则可以继续揭示周围的其他方格


一、游戏菜单

  1. 代码所在头文件:game.h

头文件的包含

代码如下:

#pragma once

#include<stdio.h>
  1. 代码所在源文件:test.c

逻辑分析:
(1)利用 do…whileswitch 两个语句简单地创建一个简易的游戏菜单,选择 1 进入游戏,选择 0 则退出游戏。
(2)选择其他数字时,选择错误,重新输入。

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

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

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;
}
  1. 效果展示:
    (1)选择 1
    在这里插入图片描述
    (2)选择 0
    在这里插入图片描述
    (3)选择其他数字
    在这里插入图片描述

二、创建两个11*11的二维数组

  1. 代码所在头文件:game.h

逻辑分析:
(1)#define 定义 ROW 和 COL 两个常量,以便 “打印棋盘”、“布置雷” 和 “排查雷” 时取出。
(2)#define 定义 ROWS 和 COLS 两个常量,以便创建两个 11 * 11 的数组时取出。
(3)由于排查雷时系统需要遍历玩家指定坐标下周围的八个格子布置雷的情况,所以为了避免在遍历时出现越界访问的问题,我们在设置棋盘大小的时候,真实的( 11 * 11 )棋盘是要比玩家所能够操作的( 9 * 9 )棋盘要大上一圈的。

代码如下:

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
  1. 代码所在源文件:test.c

逻辑分析:
(1)mine数组是专门存放布置好的雷的信息,正常游戏下玩家无法查看。
(2)show数组是专门存放排查出雷的信息,实时反馈棋盘信息给玩家查看。
(3)当玩家输入要排查的坐标后,系统就会判断该坐标在mine数组下是否有雷,从而反馈信息到show数组,告诉玩家该坐标及其周围格子是否有雷的情况。
(4) 如果把show数组比作一张考场试卷的话,那么mine数组就好比这张试卷的答案,考试的时候肯定是不能把答案给你的,只有考完试后,才会公布答案。

代码如下:

void game()
{
	//mine数组是专门存放布置好的雷的信息
	char mine[ROWS][COLS] = { 0 };
	//show数组是专门存放排查出雷的信息
	char show[ROWS][COLS] = { 0 };
}

三、初始化棋盘

  1. 代码所在头文件:game.h

逻辑分析:
(1)对于mine数组的初始化,如果用字符 ’ 1 ’ 表示该坐标有雷,字符 ’ 0 ’ 表示无雷,就把mine数组的每一个元素初始化为字符 ’ 0 ’ 。因为系统在布置雷这个动作之前,mine数组应该是没有雷的。(伏笔)
(2)对于show数组的初始化,为了保持神秘感,把show数组的每一个元素初始化为 ’ * ’ 或者其他非数字字符也行。没有排查的格子是 ’ * ’ ,排查后的格子显示数字字符。
(3)设计一个函数,能够分别实现mine数组和show数组的初始化,毕竟哪个老板不喜欢拿一份工资干两份活的员工呢?所以在传参时多加一个参数,即我们自己想要数组的每个元素初始化成什么,就传什么。

代码如下:

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
  1. 代码所在源文件:test.c

逻辑分析:综上所述。

代码如下:

void game()
{
	//mine数组是专门存放布置好的雷的信息
	char mine[ROWS][COLS] = { 0 };
	//show数组是专门存放排查出雷的信息
	char show[ROWS][COLS] = { 0 };
	//初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');//'0'
	InitBoard(show, ROWS, COLS, '*');//'*'
}
  1. 代码所在源文件:game.c

逻辑分析:嵌套循环,用两个循环干就完事了。

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}
  1. 效果展示

(1)mine数组的初始化
在这里插入图片描述
(2)show数组的初始化

在这里插入图片描述

四、打印棋盘

  1. 代码所在头文件:game.h

逻辑分析:
(1)注意:我们这里传的实参不再是 ROWS 和 COLS了,即传的值不再是 11 和 11 。
(2)因为数组大小确实是 11 * 11,但是无论是系统布置雷还是玩家排查雷,实际操作的棋盘大小都只有 9 * 9,所以这里传的实际参数是 ROW 和 COL,而不是 ROWS 和 COLS了。

代码如下:

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
  1. 代码所在源文件:test.c

逻辑分析:综上所述。

代码如下:

void game()
{
	//mine数组是专门存放布置好的雷的信息
	char mine[ROWS][COLS] = { 0 };
	//show数组是专门存放排查出雷的信息
	char show[ROWS][COLS] = { 0 };
	//初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');//'0'
	InitBoard(show, ROWS, COLS, '*');//'*'
	//打印棋盘
	DisplayBoard(show, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
}
  1. 代码所在源文件:game.c

逻辑分析:
(1)为了方便玩家能够准确输入想要排查雷的坐标,这里我们先是在打印棋盘前,打印了棋盘每一列的序号,然后再在每一行棋盘打印前先打印每一行的序号。
(2)注意:列的序号是从0开始的,而行的序号是从1开始的,这是因为行的序号也要占一列,否则每一列的序号对应的就是下一列的序号了(最后一列除外),所以列的序号从0开始。
(3)特别注意:这里行与列的序号并没有占用 mine 或者 show 数组的空间,而是单独打印的。在统计坐标周围有几个雷的时候可能会考虑到这一点,误把序号加进去计算。

代码如下:

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("******* 扫雷 ******* \n");
	for (j = 0; j <= col; j++)
	{
		printf("%d ", j);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
  1. 效果展示
    在这里插入图片描述

五、布置雷

1.代码所在头文件:game.h

逻辑分析:
(1)系统布置雷所生成的坐标肯定是随机的,所以为了得到一个随机数,我们需要调用randsrandtime 这几个函数来生成随机坐标去布置雷。
(2)这里 #define 定义的 EASY_COUNT 是所布置雷的数量。

代码如下:

#define EASY_COUNT 10
#include<stdlib.h>
#include<time.h>
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
  1. 代码所在源文件:test.c

逻辑分析:
(1)在打印棋盘的时候就提及过,系统布置雷实际操作的是 9 * 9 的棋盘,所以这里传的实参也是 ROW 和 COL 。
(2)同时注意布置雷传的是mine数组的数组名。

代码如下:

void game()
{
	//mine数组是专门存放布置好的雷的信息
	char mine[ROWS][COLS] = { 0 };
	//show数组是专门存放排查出雷的信息
	char show[ROWS][COLS] = { 0 };
	//初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');//'0'
	InitBoard(show, ROWS, COLS, '*');//'*'
	//打印棋盘
	DisplayBoard(show, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	
	//布置雷
	SetMine(mine, ROW, COL);
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));//注意主函数多了这一行代码
	......//中间代码省略
	return 0;
}
  1. 代码所在源文件:game.c

逻辑分析:
(1)设置系统所布置雷的个数作为循环的限制条件。
(2)由于棋盘的横纵坐标范围均为 1 ~ 9 ,所以要使生成的随机数分别经 %row 和 %col 后再 +1的处理之后,才能达到准确在棋盘上布置雷的目的。
(3)不能直接对处理后的坐标布置雷,因为不确定同一个坐标下是否存在重复布置雷的情况,使得最后雷的数量无法达到预期,所以要加一个限制条件,即该坐标没有雷才能布置雷。

代码如下:

//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}
  1. 效果展示

在这里插入图片描述

六、排查雷

  1. 代码所在头文件:game.h

注意:这里既传了mine数组的数组名,又传了show数组的数组名。

代码如下:

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
  1. 代码所在源文件:test.c

逻辑分析:
(1)由于系统要根据玩家排查雷的坐标去判断在mine数组下该坐标及其周围是否有雷,然后再把信息记录到show数组反馈给玩家,所以需要传两个数组的数组名。
(2)注意:这里传的实参也是 ROW 和 COL ,只需对 9 * 9 棋盘操作即可。

代码如下:

void game()
{
	//mine数组是专门存放布置好的雷的信息
	char mine[ROWS][COLS] = { 0 };
	//show数组是专门存放排查出雷的信息
	char show[ROWS][COLS] = { 0 };
	//初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');//'0'
	InitBoard(show, ROWS, COLS, '*');//'*'
	//打印棋盘
	DisplayBoard(show, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	
	//布置雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);

	//排查雷
	FindMine(mine, show, ROW, COL);
}
  1. 代码所在源文件:game.c

逻辑分析:
(1)用变量 win 表示已排查格子的个数,当 win = 棋盘大小 9 * 9 -布置雷的数量 EASY_COUNT时,则表示棋盘上所有非雷区域已被排查,排雷成功。
(2)首先判断玩家要排查雷的坐标是否非法,若横坐标或纵坐标不在 1 ~ 9 这个范围内,则该坐标非法,重新输入。
(3)坐标合法后,然后再判断该坐标在show数组下是否为 ’ * ‘,即该坐标是否已经被排查过,若为 ’ * ’ ,则重新输入。
(4)最后判断该坐标是否为雷,即该坐标在mine数组下是否为 ’ 1 ’ ,若为雷,则游戏结束,并打印棋盘,告诉玩家布置雷的情况;若不是雷,就要统计这个坐标周围有几个雷。
(5)由于在mine数组中,’ 1 ’ 表示有雷,’ 0 ‘表示非雷,所以要统计该坐标周围有几个雷,只需要把周围的数字相加即可,这里便呼应了初始化棋盘时的伏笔了。但要知道,mine数组下的数字是字符数字,而接收雷个数的 count 接收的整型数字。
(6)因为 ’ 0 ’ - ’ 0 ’ = 0 、’ 1 ’ - ’ 0 ’ = 1 ,所以只需要把该坐标周围八个坐标的值求和后 - 8 * ’ 0 ’ 的整型数字返回给 count 即可。

代码如下:

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

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{
		printf("请输入要排查的坐标>:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死\n");
					DisplayBoard(mine, ROW, COL);
					break;
				}
				else
				{
					//如果该坐标不是雷,就要统计这个坐标周围有几个雷
					int count = GetMineCount(mine, x, y);
					show[x][y] = count + '0';
					DisplayBoard(show, ROW, COL);
					win++;
				}
			}
			else
			{
				printf("该坐标已经被排查\n");
			}
		}
		else
		{
			printf("排查的坐标非法,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}
  1. 效果展示

(1)排查的坐标非法,请重新输入

在这里插入图片描述
(2)该坐标已经被排查,重新输入
在这里插入图片描述

(3)很遗憾,你被炸死
在这里插入图片描述

(4)恭喜你,排雷成功

在这里插入图片描述


总代码

  • game.h
#pragma once

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

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 80

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);

//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
  • test.c
#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

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

void game()
{
	//mine数组是专门存放布置好的雷的信息
	char mine[ROWS][COLS] = { 0 };
	//show数组是专门存放排查出雷的信息
	char show[ROWS][COLS] = { 0 };
	//初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');//'0'
	InitBoard(show, ROWS, COLS, '*');//'*'
	//打印棋盘
	DisplayBoard(show, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	
	//布置雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);

	//排查雷
	FindMine(mine, show, ROW, COL);
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	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;
}
  • game.c
#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("******* 扫雷 ******* \n");
	for (j = 0; j <= col; j++)
	{
		printf("%d ", j);
	}
	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 SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

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

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{
		printf("请输入要排查的坐标>:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死\n");
					DisplayBoard(mine, ROW, COL);
					break;
				}
				else
				{
					//如果该坐标不是雷,就要统计这个坐标周围有几个雷
					int count = GetMineCount(mine, x, y);
					show[x][y] = count + '0';
					DisplayBoard(show, ROW, COL);
					win++;
				}
			}
			else
			{
				printf("该坐标已经被排查\n");
			}
		}
		else
		{
			printf("排查的坐标非法,请重新输入\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}
}

最后,感谢大家的观看,希望这篇文章能够对你们有所帮助!

  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值