C语言实现控制台扫雷游戏(详细教程+完整源码)

C语言实现控制台扫雷游戏(详细教程+完整源码)

一、扫雷游戏简介

1.功能说明

  • 使用控制台实现经典的扫雷游戏
  • 游戏可以通过菜单实现继续玩或者退出游戏
  • 扫雷的棋盘是9*9的格子
  • 默认随机布置10个雷
  • 可以排查雷

2.游戏规则

  • 如果位置不是雷,就显示周围有几个雷。
  • 如果位置是雷,就炸死,游戏结束。
  • 把除10个雷之外的所有位置都找出来,则为排雷成功,游戏结束。

3.游戏界面

3.1.初始界面:

在这里插入图片描述

3.2.排雷界面:

在这里插入图片描述

3.3.排雷失败界面:

在这里插入图片描述

3.4.排雷成功界面:

在这里插入图片描述

二、扫雷游戏分析和设计

1.文件结构设计

为了实现扫雷游戏,我们可以将游戏拆分为三个文件,分别为:

test.c //文件中写游戏的测试逻辑
game.c //文件中写游戏中函数的实现等
game.h //文件中写游戏需要的数据类型和函数声明等
    
//扫雷游戏:
test.c——专门测试的游戏逻辑

game.c——游戏实现

game.h——游戏的函数的声明

2.数据结构设计

扫雷的过程中,布置的雷排查出的雷的信息都需要存储,所以我们需要一定的数据结构来存储这些信息

因为我们需要在 9×9 的棋盘上布置雷的信息和排查雷,我们首先想到的就是创建一个 9×9 的数组来存放信息。

空棋盘:

在这里插入图片描述

那如果这个位置布置雷,我们就存放1,没有布置雷就存放0。

布置雷的棋盘:

在这里插入图片描述

假设我们排查(8,6)这个坐标时,我们访问周围的一圈8个黄色位置,统计周围雷的个数时,最下面的三个坐标就会越界,为了防止越界,我们在设计的时候,给数组扩大一圈还是布置在中间的 9×9 的坐标上,周围一圈不去布置雷就行,这样就解决了越界的问题。所以我们将存放数据的数组创建成 11×11 是比较合适。

雷区外多加一圈防止越界:

在这里插入图片描述

再继续分析,我们在棋盘上布置了雷,棋盘上雷的信息(1)非雷的信息(0),假设我们排查了某一个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录存储,并打印出来,作为排雷的重要参考信息的。那这个雷的个数信息存放在哪里呢?如果存放在布置雷的数组中,这样雷的信息雷的个数信息就可能或产生混淆和打印上的困难

因为雷的信息用1代表,而如果我们排查位置的坐标周围(周围八个坐标中)有一个雷,也将会打印出1,这样会与代表雷的数字冲突了。

这里解决方法就是,比如:雷和非雷的信息不要使用数字,使用某些字符就行,这样就避免冲突了,但是这样做棋盘上有非雷的信息,还有排查出的雷的个数信息,就比较混杂,不够方便。

这里我们采用另外一种方案,我们专门给一个棋盘(对应一个数组mine)存放布置好的雷的信息,再给另外一个棋盘(对应另外一个数组show)存放排查出的雷的信息

同时为了保持神秘,show 数组开始时初始化为字符 ‘*’,为了保持两个数组的类型一致,可以使用同一套函数处理,mine数组最开始也初始化为字符’0’布置雷改成’1’

如下图:

mine数组布置好雷后的状态:

在这里插入图片描述

show数组初始状态:

在这里插入图片描述

对应的数组应该是:

char mine[11][11] = {0};
//用来存放布置好的雷的信息

char show[11][11] = {0};
//用来存放排查出的雷的个数信息

3.函数的分析和设计

在实现扫雷游戏的过程中,我们需要完成,初始化棋盘打印棋盘布置雷排查雷这四个步骤,我们可以创建四个函数来分别完成这四个步骤。

3.1.初始化棋盘函数

我们在前面数据结构设计中分析了,需要创建两个棋盘(数组mine和数组show)来分别存放布置好雷的信息和排查出雷的信息。

所以在扫雷游戏开始之前,我们需要对这两个棋盘进行初始化,而对棋盘进行初始化,实际上也就是对二维数组进行初始化。为了方便使用同一套函数处理,我们要保证两个数组类型一致,将mine数组(11 * 11)全部初始化为字符’ 0 ‘,show数组(11 * 11)全部初始化为字符’ * ‘,且在初始化棋盘的时候,我们选择通过传参的方式将字符传过去,而不是固定为字符’*‘或’0’,函数中的char set就是我所新建的字符变量,这样就能保证此函数的复用性。

通过一个for循环生成行数,再在循环内嵌套一个for循环生成列数,然后将字符依次放入数组的每个元素中即可。

初始化棋盘函数代码如下:

//棋盘初始化函数的实现
void InitBoard(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;
		}
	}
}
3.2.打印棋盘函数

初始化棋盘完成后,我们需要将9 * 9 的show数组打印出来。(mine数组是布置雷的棋盘,不打印出来)

通过一个for循环生成行数,再在循环内嵌套一个for循环生成列数,然后通过printf打印出数组的行和列即可。

为了我们方便且清晰的找到各个坐标,我们要在行和列前都标上序号。所以在打印棋盘之前,通过一个for循环将列的序号打印出来,然后在生成行数的for循环中生成行的序号。

打印棋盘函数代码如下:

//打印棋盘函数的实现
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("-------扫雷游戏-------\n");
	for ( i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for ( i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		for ( j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");

	}
}
3.3.布置雷函数

完成棋盘打印之后,我们就要将雷布置到棋盘中去,注意是布置到mine数组中,而不是show数组。

通过随机数生成9 * 9范围内的x和y坐标(根据棋盘大小来定),将里面的字符’0’替换成字符’1’即可('1’代表雷),这一步骤需要循环10次(根据雷的个数来定),每布置成功一个雷就减少一个,直至布置雷的个数为0,所以选择使用while循环,循环条件就是布置雷的个数。

在使用rand()函数之前,需要先通过srand函数初始化随机数生成器的种子,一个程序中只需初始化一次即可,所以一般选择放在main函数中初始化。

布置雷函数代码如下:

//布置雷
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--;
		}
	}
	
}
3.4.排查雷函数

布置雷完成后,我们就要开始进行雷的排查,也就是游戏的正式开始。

进行雷的排查,我们需要输入坐标,在输入坐标时候,我们需要考虑输入的坐标是否在棋盘范围内,如果不是合法的坐标,我们就通过printf函数告知输入的坐标不合法,需要重新输入;如果是合法的坐标,就找到坐标对应的字符。若坐标对应的是字符’1’('1’代表雷),则代表被炸死;若是字符’0’('0’代表非雷),则说明没有被炸死,该位置不是雷。不是雷,我们就需要统计这个坐标周围有几个雷,然后存放到show数组上,再调用打印棋盘函数打印出来。

而这个过程是一个循环,我们需要用到while循坏来实现,循环条件就是:已排查的坐标数 < 通过游戏需要排查的坐标,如:9*9棋盘中,10个雷需要排查71个坐标。当他们相等时(通过if语句判断),表示通过游戏需要排查的坐标全部被排查出,通过printf输出告知扫雷成功

想要统计输入坐标周围8个坐标的雷的个数,我们创建一个函数用来统计,返回类型为int,返回值存入到count变量中。

但由于棋盘中存放的是字符,我们需要将其转化为数字,通过字符数字➖字符’0’能够得到对应数字数字+字符’0’能够得到对应字符,如:‘3’-‘0’ = 51-48 = 3; ‘0’ + 3= 48+3=51 =‘3’。根据这个规则,我们可以将我们输入的坐标周围的坐标对应的字符(8个字符)相加起来,去8 * ‘0’,即可得到此坐标周围雷的个数,然后将得到的雷的个数,存放到show数组上,再调用打印棋盘函数即可。如图所示:

在这里插入图片描述

在这里插入图片描述

(4,6)坐标周围一圈有一个雷,我们把他们相加起来,也就是:7个字符’0’ + 一个字符’1’ = 385,而8个字符’0’ = 384,相减正好为数字1。

排查雷函数代码如下:

//排查雷函数的实现
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%*c%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y<= col)
		{
			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");
		}
	}
	if (win == row*col - Easy_Count)
	{
		printf("恭喜你,扫雷成功\n");
		DisplayBoard(mine, ROW, 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()
{
	char mine[ROWS][COLS];//存放布置好的雷
	char show[ROWS][COLS];//存放排查出的雷的信息

	//初始化棋盘
	//1.mine数组最开始全是'0'
	//2.show数组最开始全是'*'
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//打印棋盘
	//DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, 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.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 10

//初始化棋盘
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 board[ROWS][COLS], int row, int col);

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

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;
	for ( i = 0; i < rows; i++)
	{
		int j = 0;
		for ( j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

//打印棋盘函数的实现
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("-------扫雷游戏-------\n");
	for ( i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for ( i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		for ( j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");

	}
}

//布置雷函数的实现
void SetMine(char board[ROWS][COLS], int row, int col)
{
	//布置10个雷
	//生成随机的坐标,布置雷
	int count = Easy_Count;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;

		if (board[x][y] == '0')
		{
			board[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%*c%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y<= col)
		{
			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");
		}
	}
	if (win == row*col - Easy_Count)
	{
		printf("恭喜你,扫雷成功\n");
		DisplayBoard(mine, ROW, COL);
	}

}
  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的控制台版本扫雷游戏C语言代码示例,仅供参考: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdbool.h> #define ROWS 10 // 行数 #define COLS 10 // 列数 #define MINE_NUM 10 // 雷数 int minefield[ROWS][COLS]; // 雷区 bool visible[ROWS][COLS]; // 可视区 // 初始化雷区 void initMinefield() { int i, j, count; for (i = 0; i < ROWS; i++) { for (j = 0; j < COLS; j++) { minefield[i][j] = 0; visible[i][j] = false; } } // 随机生成雷 count = 0; while (count < MINE_NUM) { i = rand() % ROWS; j = rand() % COLS; if (minefield[i][j] == 0) { minefield[i][j] = -1; count++; } } // 计算每个格子周围的雷数 for (i = 0; i < ROWS; i++) { for (j = 0; j < COLS; j++) { if (minefield[i][j] == -1) { continue; } if (i > 0 && j > 0 && minefield[i-1][j-1] == -1) { minefield[i][j]++; } if (i > 0 && minefield[i-1][j] == -1) { minefield[i][j]++; } if (i > 0 && j < COLS-1 && minefield[i-1][j+1] == -1) { minefield[i][j]++; } if (j > 0 && minefield[i][j-1] == -1) { minefield[i][j]++; } if (j < COLS-1 && minefield[i][j+1] == -1) { minefield[i][j]++; } if (i < ROWS-1 && j > 0 && minefield[i+1][j-1] == -1) { minefield[i][j]++; } if (i < ROWS-1 && minefield[i+1][j] == -1) { minefield[i][j]++; } if (i < ROWS-1 && j < COLS-1 && minefield[i+1][j+1] == -1) { minefield[i][j]++; } } } } // 输出雷区 void printMinefield() { int i, j; printf(" "); for (j = 0; j < COLS; j++) { printf("%d ", j); } printf("\n"); printf(" "); for (j = 0; j < COLS; j++) { printf("- "); } printf("\n"); for (i = 0; i < ROWS; i++) { printf("%d|", i); for (j = 0; j < COLS; j++) { if (visible[i][j]) { if (minefield[i][j] == -1) { printf("* "); } else { printf("%d ", minefield[i][j]); } } else { printf(". "); } } printf("\n"); } } // 检查是否赢了 bool checkWin() { int i, j, count = 0; for (i = 0; i < ROWS; i++) { for (j = 0; j < COLS; j++) { if (minefield[i][j] == -1 && visible[i][j]) { return false; } if (minefield[i][j] != -1 && visible[i][j]) { count++; } } } return count == ROWS * COLS - MINE_NUM; } // 处理输入的命令 void handleCommand(char cmd, int row, int col) { if (row < 0 || row >= ROWS || col < 0 || col >= COLS) { printf("Invalid position.\n"); return; } if (cmd == 'o') { // 打开格子 if (visible[row][col]) { printf("This position has already been opened.\n"); return; } visible[row][col] = true; if (minefield[row][col] == -1) { printf("Game over!\n"); printMinefield(); exit(0); } if (minefield[row][col] == 0) { int i, j; for (i = row-1; i <= row+1; i++) { for (j = col-1; j <= col+1; j++) { if (i >= 0 && i < ROWS && j >= 0 && j < COLS && minefield[i][j] != -1) { visible[i][j] = true; } } } } if (checkWin()) { printf("You win!\n"); printMinefield(); exit(0); } } else if (cmd == 'f') { // 标记格子为雷 visible[row][col] = true; } else { printf("Invalid command.\n"); } } int main() { srand(time(NULL)); initMinefield(); printf("Welcome to Minesweeper!\n"); printf("You can enter commands like 'o 3 4' to open the cell at row 3 and column 4.\n"); printf("You can also mark a cell as containing a mine by using the 'f' command.\n"); printf("Good luck!\n"); while (true) { printMinefield(); char cmd; int row, col; printf("Enter command: "); scanf("%c %d %d", &cmd, &row, &col); while (getchar() != '\n'); // 清空输入缓冲区 handleCommand(cmd, row, col); } return 0; } ``` 该代码实现扫雷游戏的基本功能,包括随机生成雷区、计算每个格子周围的雷数、打开格子、标记格子为雷、检查是否赢了等。在控制台中运行该程序,便可以体验简单的扫雷游戏了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值