【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");
	}
}

写在最后

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

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

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

相关推荐
EricWang13587 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
测试界的酸菜鱼9 分钟前
Python 大数据展示屏实例
大数据·开发语言·python
我是谁??9 分钟前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
小码农<^_^>11 分钟前
优选算法精品课--滑动窗口算法(一)
算法
羊小猪~~13 分钟前
神经网络基础--什么是正向传播??什么是方向传播??
人工智能·pytorch·python·深度学习·神经网络·算法·机器学习
晨曦_子画18 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
Black_Friend26 分钟前
关于在VS中使用Qt不同版本报错的问题
开发语言·qt
软工菜鸡38 分钟前
预训练语言模型BERT——PaddleNLP中的预训练模型
大数据·人工智能·深度学习·算法·语言模型·自然语言处理·bert
南宫生41 分钟前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
希言JY1 小时前
C字符串 | 字符串处理函数 | 使用 | 原理 | 实现
c语言·开发语言