【C语言项目】扫雷(详解,附图、附代码示范)

文章目录

项目思路

  1. 分文件进行创建
  2. 进入游戏前的目录
  3. 画出游戏界面
  4. 用随机值放雷
  5. 游玩扫雷

接下来,我们分步骤进行详细的解释说明。

一、分文件进行创建

在具体的项目实施中,我们需要分成不同的文件进行创建和书写,以此来保证项目的模块化。

那么在扫雷的实际书写中,

  • 源文件:
    • 测试游戏用的代码文件用test.c或者日期.c来作为文件名
    • 游戏实现的底层代码用game.c文件作为文件名
  • 头文件
    • 游戏实现中使用的各个函数的声明 ,以及包含的其他库函数的头文件 需要写在头文件game.h文件里
      如图所示:

二、进入游戏前的目录

2.1 目录的功能:

  1. 在游戏开始时,给玩家视觉上的帮助和提示
  2. 让玩家可以选择进入游戏或者退出游戏
  3. 将游戏形成一个可以不断重玩的循环
    接下来,我们分步骤进行书写:

2.2 目录界面:

c 复制代码
void menu()
{
	printf("***********************\n");
	printf("*****1、开始游戏*******\n");
	printf("*****0、退出游戏*******\n");
	printf("***********************\n");
}

接着,在main函数里面进行调用:

c 复制代码
int main()
{
	menu();
	return 0;
}

这样目录的表面就写好了,接下来需要写玩家选择进入游戏和退出游戏的功能了

2.3 选择进入或退出游戏

选择功能的逻辑:1进入游戏,0退出游戏。

显然,必备的库函数有scanf,switch、case和default。为了让游戏的体验更加良好,可以再加一个printf增加视觉上的游玩帮助,用户友好。

那么根据刚刚的逻辑,可以写出如下的选择结构,其中game函数虽然还没有写出来,但是整体的框架可以先确定下来,之后再往里慢慢写内容。

2.3.1 代码示范

c 复制代码
int main()
{
	int input = 0;
	menu();
	printf("请选择:--->");
	scanf("%d", &input);
	switch (input)
	{
	case 1:
		game();
		break;
	case 0:
		printf("退出游戏\n");
		break;
	default:
		printf("非法输入!请重试\n");
		break;
	}
	return 0;
}

2.3.2 图片示例:

三、画出游戏界面

3.1 创建两个数组

因为扫雷是一个隐藏式的游戏,放雷的数组玩家是不能看到的,所以需要两个数组,一个用来存放雷,一个用来展示给玩家隐藏后的界面,用来游玩。

创建数组代码示范:

c 复制代码
	char mine[ROWS][COLS];
	char show[ROWS][COLS];

这里的行和列,用自定义变量来代替,这样以后在想更改行列的时候就可以很方便了

自定义变量代码在头文件game.h中,代码示范:

c 复制代码
#define ROW 9
#define COL 9

3.2 初始化数组

现在数组已经创建好了,由于没有进行初始化,数组里放的是一些"烫烫烫"之类的乱码。

所以需要写一个函数,给这两个数组进行初始化,这一步也是为之后的重玩奠定了基础。
代码思路 :遍历数组每一个元素并赋值。

由于是给数组赋值,直接通过地址操作数组的,所以不需要返回值 ,直接void类型就可以。

至于接收值 ,需要接收:数组名 、以及需要赋值的符号 ,这样才能让函数有效的对不同的数组进行初始化。

代码示范:

c 复制代码
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}

在game里面的函数:

c 复制代码
void game() 
{
	char mine[ROWS][COLS];//用mine数组来放雷
	char show[ROWS][COLS];//用show数组来展示隐藏后的雷区
	InitBoard(mine, ROWS, COLS,'0');//把放雷的数组赋值成全0,这样可以方便之后的扫雷显示
	InitBoard(show, ROWS, COLS, '*');//把隐藏后的雷区赋成*(这个换其他的符号也可以)
}

3.3 打印游戏界面

3.3.1 代码思路

由于打印游戏界面是一个需要频繁进行的操作,无论是每次扫雷,还是被炸死的时候显示雷的位置,都需要打印。所以在这里,我们同样需要单独写一个函数来完成这个操作。

  1. 遍历数组并打印
  2. 返回值:由于是直接打印,是一个动作,所以做完后直接销毁就行,void类型
  3. 接收值:需要接收需要打印的数组、行、列
  4. 注意事项:
    1. 标头可以打印一个扫雷游戏分割线
    2. 在合适的地方加上换行,以保证游戏界面是9x9的正方形
    3. 在边缘处加上行和列的标号,以便玩家更方便的看到想扫的雷的坐标
    4. 注意是char数组,用%c 来打印,每个元素后面最好加空格,每一句后最好加换行
    5. 注意打印时数组的下标要从1开始,不要从0开始,否则扫雷的时候会向右下偏一位

3.3.2 代码示范:

c 复制代码
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("------扫雷游戏------\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);	//在第一行打印列数
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);//在第一列打印行数
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//打印每一位元素
		}
		printf("\n");
	}
}

图片示例:

四、用随机值放雷

4.1 代码思路:

  1. 首先放雷是需要一个随机性的,那么就可以使用srand和rand函数(伪随机数),加上时间戳,构成一个真随机数,再利用这个真随机数取一个行列的模,就可以实现随机的放雷了。
  2. 随机取十个坐标,把mine里面的数字放成1,10个雷全部放完之后就有71个0和10个1。
  3. 在实现代码的时候需要注意,随机取的坐标不能重复,需要是0才能赋1。
  4. 一共只需要十个雷,while循环,再用一个int count来计算真实赋进去的雷的个数,就可以实现随机放雷了。
  5. 由于还是远程操作数组,所以返回值是void,接收值仍然是数组和行列。
  6. 在雷数方面,可以通过自定义变量来方便以后的操作。

4.2 代码示范:

c 复制代码
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count) {
		i = rand() % row+1;
		j = rand() % col+1;
		if (board[i][j] == '0')
		{
			board[i][j] = '1';
			count--;
		}
	}
}

头文件的代码:

c 复制代码
#define EASY_COUNT 10

五、游玩扫雷

5.1 代码思路:

  1. printf进行视觉提示,scanf让玩家输入坐标
  2. 将坐标所处的数组元素进行判断
    1. 如果是雷,游戏结束
    2. 如果不是雷,统计该地点周围8个点位的雷数,并显示在棋盘上
    3. 如果位置超出棋盘范围,则重新输入
    4. 此过程不断循环,直到除了雷以外的所有点位被排干净,判定玩家赢

5.2 注意事项:

  1. 如果该点位处于边角位,如果统计周边8个点位,数组会越界,所以需要把数组多拓两行,避免越界
  2. 数字和字符的相互转换:加上'0'。比如如果想要把数字1转换为字符1,就需要加一个字符0,原理是0的ASCII码值是48,1的ASCII码值是49......加上字符0相当于加上48,正好是字符1的ASCII码值

5.3 代码实现:

c 复制代码
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS] ,int row, int col)
{
	int win = 0;
	while (win < col * row - EASY_COUNT)
	{
		printf("请输入要排查的坐标>:");
		scanf("%d %d", &i, &j);
		if (i > 0 && i <= row && j > 0 && j <= col)
		{
			if (mine[i][j] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetCountMine(mine, i, j);
				show[i][j] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
			printf("位置非法,请重新输入");
	}

	if (win == col * row - EASY_COUNT)
	{
		printf("恭喜你,游戏胜利!\n");
	}
}

六、整体代码示范

game.h

c 复制代码
#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 row,int col,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 board[ROWS][COLS], int row, int col);

test.c

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];
	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.c

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
	int i, j = 0;
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	printf("------扫雷游戏------\n");
	for (i = 0; i <= row; i++)
	{
		printf("%d ", i);	
	}
	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 board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count) {
		i = rand() % row+1;
		j = rand() % col+1;
		if (board[i][j] == '0')
		{
			board[i][j] = '1';
			count--;
		}
	}
}

int GetCountMine(char mine[ROWS][COLS] ,int x,int y)
{
	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 FindMine(char mine[ROWS][COLS],char show[ROWS][COLS] ,int row, int col)
{
	int win = 0;
	while (win < col * row - EASY_COUNT)
	{
		printf("请输入要排查的坐标>:");
		scanf("%d %d", &i, &j);
		if (i > 0 && i <= row && j > 0 && j <= col)
		{
			if (mine[i][j] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetCountMine(mine, i, j);
				show[i][j] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
			printf("位置非法,请重新输入");
	}

	if (win == col * row - EASY_COUNT)
	{
		printf("恭喜你,游戏胜利!\n");
	}
}

写在最后

如果本文对您有帮助,可不可以给我一个小小的点赞呀❤~您的支持是我最大的动力。

博主小白一枚,才疏学浅,难免有所纰漏,欢迎大家讨论和提出问题,博主一定第一时间改正。

谢谢观看嘿嘿(๑•̀ㅂ•́)و✧~!

相关推荐
Lucky小小吴1 分钟前
java代码审计入门篇——Hello-Java-Sec(完结)
java·开发语言
晨晖22 分钟前
循环队列:出队
算法
LYFlied5 分钟前
【每日算法】LeetCode 70. 爬楼梯:从递归到动态规划的思维演进
算法·leetcode·面试·职场和发展·动态规划
csbysj20208 分钟前
XML 技术
开发语言
最晚的py9 分钟前
聚类的评估方法
人工智能·算法·机器学习
清晓粼溪9 分钟前
Java登录认证解决方案
java·开发语言
小徐Chao努力11 分钟前
Go语言核心知识点底层原理教程【变量、类型与常量】
开发语言·后端·golang
锥锋骚年11 分钟前
go语言异常处理方案
开发语言·后端·golang
沐知全栈开发12 分钟前
JSP 自动刷新技术详解
开发语言
业精于勤的牙13 分钟前
浅谈:算法中的斐波那契数(五)
算法·leetcode·职场和发展