如何用C语言实现简单的扫雷游戏?

1.扫雷游戏的简单规则

扫雷游戏的规则就是,在一块的网格中,点开所有没有雷的网格。  选择要扫的坐标,若该网格为雷则失败;若该网格周边八个网格中有雷,则显示雷。在C语言中,通过自己输入坐标来实现扫雷。

2.基本理念

其本质是二维数组,需要两块面板,一块面板show数组面对用户,显示游戏界面,另一块面板mine数组用来存储雷的位置。

为什么需要两块面板呢?

因为我们使用字符‘1’来表示雷,而如果面对用户的面板中显示周围有一个雷,也显示1,那么我们怎么判断这是表示周围有一个雷还是这里就是一个雷呢?因此用两个面板可以很好地解决此问题。

show界面让用户输入坐标来扫雷,mine界面判断用户是否踩到雷或者判断周围有几颗雷。

3.界面的设计

一般来说扫雷的简单模式是9*9的界面,我们需要判断周围8个格子有几颗雷,这样就会带来一个问题,四周的格子无法判断,因为他们周围没有8个格子,所以我们在设计界面时就用11*11的界面,这样就保证了所有格子都可以判断周围的8个格子,而在显示的时候,仅显示9*9的界面即可。

4.具体功能的实现

1.初始化界面

我们需要初始化show和mine界面,我们定义show的初始化界面为字符*,mine界面的初始化为字符0,在此之前我们需要定义一个宏ROW和COL的值为9,这样方便我们以后代码的修改。此外我们还需要定义ROWS和COLS的值为ROW+2以及COL+2,以定义11*11的面板,初始化的代码如下

//初始化棋盘
void InitilizeBoard(char arr[ROWS][COLS], int rows, int cols, char set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			arr[i][j] = set;
		}
	}
}
	//定义两个棋盘
	InitilizeBoard(mine, ROWS, COLS, '0');
	InitilizeBoard(show, ROWS, COLS, '*');

在这里我们定义一个函数来初始化,形参中需要有一个char set变量,来自定义初始化界面的元素,否则就要定义两个函数来初始化界面,这样就会让代码冗余。

2.打印show界面

由于mine数组是用来存放地雷信息,不应该给用户显示,所有只显示show数组来让用户扫雷,代码如下

//显示棋盘show
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	for (int i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
}
	//显示棋盘show
	DisplayBoard(show, ROW, COL);
	//DisplayBoard(mine, ROW, COL);

在这里使用for循环来打印,切注意for循环中i和j的值,确保只打印9*9的区域,并且为了方便用户观察坐标,可以打印行号和列号。

3.随机放置地雷

定义一个函数SetMine来实现随机放置地雷,代码如下

//随机放置地雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count--;
		}
	}
}
	//随机放置地雷
	SetMine(mine, ROW, COL);

在此之前我们需要定义一个EASY_COUNT的宏来确定雷的个数,方便以后修改,这里EASY_COUNT的值为10,此代码中,使用了if语句,判断如果mine界面为字符0则可以放下地雷,如果不是字符0则代表已经布置过地雷了,在while循环中,每放置一个地雷count就减一,直到count为0跳出循环。

随机函数需要srand,则需要包含stdlib.h头文件,并且以时间作为种子,需要包含time.h头文件

4.判断周围雷的个数

//判断周围有多少雷
int GetMineCount(char arr[ROWS][COLS], int x, int y)
{
	return arr[x - 1][y - 1] +
		arr[x - 1][y] +
		arr[x - 1][y + 1] +
		arr[x][y - 1] +
		arr[x][y + 1] +
		arr[x + 1][y - 1] +
		arr[x + 1][y] +
		arr[x + 1][y + 1] - 8 * '0';

假设我们输入的坐标是x和y,那么周围8个格子的坐标也随之可以推得,此代码通过计算周围八个格子的ASCII码值再减去8个字符0的ASCII值来得出周围雷的个数,并返回此值。

5.用户输入扫雷

//让用户输入坐标扫雷
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("请输入坐标(e.g. 1 2):>");
		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 minecount = GetMineCount(mine, x, y);
					show[x][y] = minecount + '0';
					DisplayBoard(show, ROW, COL);
					win++;
				}
			}
			else
			{
				printf("该坐标已经被排查,请重新输入^_^\n");
			}
		}
		else
		{
			printf("你输入的坐标有误,请重新输入^_^\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,扫雷成功^_^\n");
		DisplayBoard(mine, ROW, COL);
	}

}

这里的形参有两个二维数组,用来联合判断是否用户踩到雷以及判断周围雷的个数

首先需要判断用户输入的坐标是否合法,判断方法为x>=1 && x<=row && y>=1 && y<=col

如果不满足这个条件则提示坐标非法,如果满足,进入子循环,需要判断这个坐标是不是被排查过,即如果该坐标的值为字符*代表没有被排查,繁殖则为排查过,若没有被排查,进入子循环,判断该坐标是否为雷(即在mine数组中是否为字符1),若是,则提示被炸死,打印mine数组显示雷的位置并跳出循环,若不是,则计算周围雷的个数,用该处的地雷数加上字符0的ASCII值即可判断。

在这里有一个问题,什么时候才算赢?

一般来说简单模式是10个雷,而只需要选出其他所有不是雷的坐标便可胜利,因此这里引入一个变量win,给win赋值row*col-EASY_COUNT,每成功扫一个雷win就加一,直到满足条件后跳出该循环,表示游戏胜利。

6.开始界面

void menu()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		printf("*************************************\n");
		printf("*************************************\n");
		printf("**************1.play*****************\n");
		printf("**************0.exit*****************\n");
		printf("*************************************\n");
		printf("*************************************\n");
		printf("请选择1或0确定是否开始游戏:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏^_^\n");
			break;
		default:
			printf("输入有误,请重新输入^_^\n");
			break;
		}
	} while (input);
}

int main()
{
	menu();
	return 0;
}

这里定义了一个函数菜单,且使用循环函数,用来判断是否开始游戏,并且游戏结束后可以再次选择,用do while函数,while用input来判断,若1则循环,若0则跳出循环,选择是否开始用了switch语句,若输入1则开始执行game()函数,即开始游戏,若输入0则退出游戏,若输入其他值则提示输入有误,重新输入。

5.全部代码

在这里我们使用多个文件写代码,game.h里包含了头文件的引用,函数的申明,game.c里面实现游戏的核心操作,main.c用来引用函数来实现游戏,全部代码如下

game.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

#define ROW 9
#define COL 9

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

#define EASY_COUNT 10

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

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

//显示棋盘show
void DisplayBoard(char arr[ROWS][COLS], int row, int col);

//随机放置地雷
void SetMine(char arr[ROWS][COLS], int row, int col);

//让用户输入坐标扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);

main.c

#define _CRT_SECURE_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

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

//显示棋盘show
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	for (int i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
}

//随机放置地雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count--;
		}
	}
}

//判断周围有多少雷
int GetMineCount(char arr[ROWS][COLS], int x, int y)
{
	return arr[x - 1][y - 1] +
		arr[x - 1][y] +
		arr[x - 1][y + 1] +
		arr[x][y - 1] +
		arr[x][y + 1] +
		arr[x + 1][y - 1] +
		arr[x + 1][y] +
		arr[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("请输入坐标(e.g. 1 2):>");
		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 minecount = GetMineCount(mine, x, y);
					show[x][y] = minecount + '0';
					DisplayBoard(show, ROW, COL);
					win++;
				}
			}
			else
			{
				printf("该坐标已经被排查,请重新输入^_^\n");
			}
		}
		else
		{
			printf("你输入的坐标有误,请重新输入^_^\n");
		}
	}
	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,扫雷成功^_^\n");
		DisplayBoard(mine, ROW, COL);
	}

}

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

//定义两个棋盘,一个作为用户显示,另一个作为隐藏地雷的表,显示地雷位置
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };

//游戏主程序,这里这是申明函数,函数的实现在main.c
void game()
{
	//定义两个棋盘
	InitilizeBoard(mine, ROWS, COLS, '0');
	InitilizeBoard(show, ROWS, COLS, '*');

	//随机放置地雷
	SetMine(mine, ROW, COL);

	//显示棋盘show
	DisplayBoard(show, ROW, COL);
	//DisplayBoard(mine, ROW, COL);

	//让用户输入坐标扫雷
	FindMine(mine, show, ROW, COL);

}

void menu()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		printf("*************************************\n");
		printf("*************************************\n");
		printf("**************1.play*****************\n");
		printf("**************0.exit*****************\n");
		printf("*************************************\n");
		printf("*************************************\n");
		printf("请选择1或0确定是否开始游戏:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏^_^\n");
			break;
		default:
			printf("输入有误,请重新输入^_^\n");
			break;
		}
	} while (input);
}

int main()
{
	menu();
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿梦Anmory

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

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

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

打赏作者

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

抵扣说明:

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

余额充值