C语言实现扫雷小游戏

目录

前言

1.游戏功能

2.游戏实现

2.1 打印菜单

2.2 建立棋盘

2.2.1 电脑埋雷棋盘

2.2.2 玩家面对棋盘

2.3 棋盘初始化

2.4 打印棋盘

2.5 电脑埋雷

2.6 玩家排雷

2.6.1 判断函数

2.6.2 扩散函数

2.6.3 计算函数

2.6.4 安全函数

2.7 判断输赢

3.代码模块

结语


前言

前些天偶然想起曾经初中在机房上课划水时玩的小游戏扫雷,便在电脑上找,发现竟没有这个小游戏了,于是便试着用C实现下它。

1.游戏功能

玩家通过菜单选择玩游戏或退出,进入游戏后,玩家可以输入坐标来实现扫雷。当玩家不幸扫中了雷,那么游戏结束返回菜单,当玩家成功避开所有雷,游戏胜利返回菜单。

2.游戏实现

2.1 打印菜单

玩家选择0退出游戏,选择1开始游戏

2.2 建立棋盘

扫雷嘛,肯定需要电脑埋雷,但这个埋雷的棋盘不能被玩家看到,不然就失去了意义。因此,我们建立两个棋盘,一个用来埋雷的棋盘,一个是给玩家玩的棋盘,我现在演示的是9*9的棋盘,如果想要修改棋盘只需修改ROW和COL的值即可。但我们建立的棋盘却要在原来的基础上都加2,即11*11,原因在判断函数模块中。

2.2.1 电脑埋雷棋盘

我们将雷定义为字符‘1’,将没有雷的地方定义为字符‘0’,至于为何这样操作,先埋下个伏笔。棋盘名字为mine。

2.2.2 玩家面对棋盘

我们将所有棋盘上的元素遍历为字符‘*’,棋盘名字为show。

2.3 棋盘初始化

 初始化要在思考两个棋盘里该放什么什么元素后才应该做的。因此当我们知道了棋盘中要放什么元素后,初始化函数就很容易实现了。但这又有问题,两个棋盘初始化的元素不同,于是,我们再改初始化函数添加一个参数set,将想要初始化成什么的元素也传过来,于是便得到了它:

(为了方便我将图形调整了90度)


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

2.4 打印棋盘

为了方便玩家寻找坐标,我们还需要打印一行和一列的序列号,当然这个序列号并不在棋盘中元素中,相当于是在棋盘外围显示,但我们打印的是中间9行9列,因此就会产生序列号在棋盘中的效果

(图形已调整90度)

void Printboard(char board[ROWS][COLS], int row, int col)
{
	int j = 0, i = 0;
	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");
	}

	
}

2.5 电脑埋雷

实现电脑埋雷又是需要借助rand函数,但rand函数产生的伪随机数(0-32767),且每次在调用之前都会查询是否钓调用过srand(seed)函数,是否给seed赋予了一个值。而srand函数是作为rand的初始化函数,当seed的值相同时将会产生一样的随机值,因此我们又需要借用time函数

因为时间一直在变化,给srand赋值的种子也就不同,由于srand的参数需要为unsigned int型,因此我们还需对返回time强转。

埋的雷我们还需判断其是否越界或者是否在同一个位置布置雷。

 (图形已调整90度)

void setmine(char mine[ROWS][COLS], int row, int col)//布置雷场
{
	int x = 0, y = 0;
	int count = MINE;
	while (count)
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (x >= 1 && x <= row && y >= 1 && y <= col && mine[x][y] == '0')
		{
			count--;
			mine[x][y] = '1';
		}
	}
}

2.6 玩家排雷

好了,又到了最经典的压轴模块。

因为这个函模块太过庞大,当一个模块中写了太多代码时我们调试bug时很难发现错误的原因,因此我们分装几个函数进行实现。

2.6.1 判断函数

获取玩家输入坐标后的周围8个格子内是否有雷并返还雷的个数。

如果我们设计的棋盘为9*9,那么返回的数将会混乱,不如设计一个11*11的棋盘,但我们只操作其中的9*9区域。将11*11的mine棋盘遍历为字符‘0’时,就不在影响输入的坐标是否在角落了。

但我们肯定不会把外面这圈打印出来,只要我们心中知道就OK了。

我们查阅ASCII表发现字符‘0’的值为48,字符‘1’的值为49,而在计算机中,字符相加减其实就是ASCII值的相加减。因此字符‘1’减字符‘0’返回的为数字1。于是,我们便设计出:

int Get_mine_count(char mine[ROWS][COLS],int x,int y)//获取周围8个格子是否有雷
{
	return mine[x - 1][y - 1] +
		mine[x - 1][y] +
		mine[x - 1][y + 1] +
		mine[x][y - 1] +
		mine[x][y + 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] - 8 * '0';
}

2.6.2 扩散函数

当判断函数返回的值为0时,即周围没有雷时,将继续判断该8个格子的周围8个格子,并将原来的8个格子遍历为空格,直到判断的数字不为0时才停止,并将该数字传到该坐标。

当然我们的游戏就没有这么精致了,因为还没有颜色和图形模块。

void Spread(char show[ROWS][COLS],char mine[ROWS][COLS], int x, int y)//传递
{
	show[x][y] = ' ';
	int i = 0, j = 0, ret = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (i > 0 && i <= ROW && j > 0 && j <= COL && mine[i][j] != '1' && show[i][j] == '*')
			{
				ret = Get_mine_count(mine, i, j);
				if (ret != 0)
				{
				    show[i][j] = ret + '0';
				}
				if (!ret)
				{
					Spread(show, mine, i, j);
				}
				else if (show[i][j] == '*')
				{
					show[i][j] = ' ';
				}
			}
		}
	}
}

2.6.3 计算函数

计算show棋盘上不是字符‘*’的个数,并返还个数

int Isblank(char show[ROWS][COLS], int row, int col)//计算已判断完的个数
{
	int i = 0, j = 0, n = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] != '*')n++;
		}
	}
	return n;
}

2.6.4 安全函数

如果玩家第一个排的就是雷,函数将会把该坐标的雷重置为字符‘0’并重新设计一个雷。

测试:将MINE值改为80

if (n==0 && mine[x][y] == '1')
			{
				mine[x][y] = '0';
				int a, b = 0;
				while (1)
				{
					a = rand() % row + 1;
					b = rand() % col + 1;
					if (a >= 1 && a <= row && b >= 1 && b <= col && mine[a][b] == '0'&&a != x && b != y)
					{
						mine[a][b] = '1';
						break;
					}
				}
			}

2.7 判断输赢

如果不是第一次就踩雷,之后的踩雷将会使游戏结束,当SIZEX-n的值为0时,扫雷成功

void showmine(char mine[ROWS][COLS], char show[ROWS][COLS] ,int row, int col)//玩家所看到的棋盘
{
	int x = 0, y = 0;
	int n = 0;//记录已排除的个数
	while (SIZEX-n)
	{
		printf("请输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col )
		{
			//欧皇测试仪
			if (n==0 && mine[x][y] == '1')
			{
				mine[x][y] = '0';
				int a, b = 0;
				while (1)
				{
					a = rand() % row + 1;
					b = rand() % col + 1;
					if (a >= 1 && a <= row && b >= 1 && b <= col && mine[a][b] == '0'&&a != x && b != y)
					{
						mine[a][b] = '1';
						break;
					}
				}
			}
			//坐标合法
			//踩雷
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你扫到了雷\n");
				Printboard(mine, ROW, COL);
				break;
			}
			else//未踩雷
			{
				int count = 0;
				count= Get_mine_count(mine, x, y)+'0';
				if (count == '0')
				{
					Spread(show, mine, x, y);//递归传递空格
				}
				show[x][y] = count;
				n=Isblank(show, ROW, COL);
				system("cls");
				Printboard(show, row, col);
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入!!!\n");
		}
	}
	if (SIZEX - n == 0)
	{
		printf("恭喜你,扫雷成功\n");
		Printboard(mine, ROW, COL);
		system("pause");
	}
}

3.代码模块

game.h
#pragma once
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINE 10//80   80用来测试欧皇的
#define SIZEX ROW*COL-MINE

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

void Initboard(char board[ROWS][COLS], int row, int col, char set);
void Printboard(char board[ROWS][COLS], int rpws, int cols);
void setmine(char mine[ROWS][COLS], int row, int col);
void showmine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);
int Get_mine_count(char mine[ROWS][COLS], int x, int y);
void Spread(char show[ROWS][COLS],char mine[ROWS][COLS], int x, int y);
game.c
#define _CRT_SECURE_NO_WARNINGS
#include  "game.h"

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

void Printboard(char board[ROWS][COLS], int row, int col)
{
	int j = 0, i = 0;
	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 x = 0, y = 0;
	int count = MINE;
	while (count)
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (x >= 1 && x <= row && y >= 1 && y <= col && mine[x][y] == '0')
		{
			count--;
			mine[x][y] = '1';
		}
	}
}

int Get_mine_count(char mine[ROWS][COLS],int x,int y)//获取周围8个格子是否有雷
{
	return mine[x - 1][y - 1] +
		mine[x - 1][y] +
		mine[x - 1][y + 1] +
		mine[x][y - 1] +
		mine[x][y + 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] - 8 * '0';
}

void Spread(char show[ROWS][COLS],char mine[ROWS][COLS], int x, int y)//传递
{
	show[x][y] = ' ';
	int i = 0, j = 0, ret = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (i > 0 && i <= ROW && j > 0 && j <= COL && mine[i][j] != '1' && show[i][j] == '*')
			{
				ret = Get_mine_count(mine, i, j);
				if (ret != 0)
				{
				    show[i][j] = ret + '0';
				}
				if (!ret)
				{
					Spread(show, mine, i, j);
				}
				else if (show[i][j] == '*')
				{
					show[i][j] = ' ';
				}
			}
		}
	}
}

int Isblank(char show[ROWS][COLS], int row, int col)//计算已判断完的个数
{
	int i = 0, j = 0, n = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] != '*')n++;
		}
	}
	return n;
}

void showmine(char mine[ROWS][COLS], char show[ROWS][COLS] ,int row, int col)//玩家所看到的棋盘
{
	int x = 0, y = 0;
	int n = 0;//记录已排除的个数
	while (SIZEX-n)
	{
		printf("请输入坐标:>");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col )
		{
			//欧皇测试仪
			if (n==0 && mine[x][y] == '1')
			{
				mine[x][y] = '0';
				int a, b = 0;
				while (1)
				{
					a = rand() % row + 1;
					b = rand() % col + 1;
					if (a >= 1 && a <= row && b >= 1 && b <= col && mine[a][b] == '0'&&a != x && b != y)
					{
						mine[a][b] = '1';
						break;
					}
				}
			}
			//坐标合法
			//踩雷
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你扫到了雷\n");
				Printboard(mine, ROW, COL);
				break;
			}
			else//未踩雷
			{
				int count = 0;
				count= Get_mine_count(mine, x, y)+'0';
				if (count == '0')
				{
					Spread(show, mine, x, y);//递归传递空格
				}
				show[x][y] = count;
				n=Isblank(show, ROW, COL);
				system("cls");
				Printboard(show, row, col);
			}
		}
		else
		{
			printf("输入坐标非法,请重新输入!!!\n");
		}
	}
	if (SIZEX - n == 0)
	{
		printf("恭喜你,扫雷成功\n");
		Printboard(mine, ROW, COL);
		system("pause");
	}
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

void game()
{
	char mine[ROWS][COLS] = { 0 };//建立一个11*11的棋盘
	char show[ROWS][COLS] = { 0 };//
	Initboard(mine, ROWS, COLS, '0');
	Initboard(show, ROWS, COLS, '*');
	//建立有雷的棋盘
	setmine(mine, ROW, COL);
	//打印棋盘
	Printboard(mine, ROW, COL);//玩家不能看到仅用于测试,也可以用来作弊。。。。
	Printboard(show, ROW, COL);//玩家能看到的棋盘
	//排雷
	showmine(mine, show, ROW, COL);
}
void menu()
{
	printf("****** 菜单 *****\n");
	printf("***** 1.play ****\n");
	printf("***** 0.exit ****\n");
}
int main()
{
	srand((unsigned)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);
}

结语

如果该代码有不懂的可以问我,评论区回复。

如有错误也可以指出来我会及时修改的。

谢谢点赞。

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海绵宝宝养的的小窝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值