C语言--从零开始的扫雷游戏

news2024/12/23 4:44:30

C语言--从零开始的扫雷游戏

  • 1. 游戏说明
  • 2. 总体代码
  • 3. 详细讲解
    • 3.1 菜单部分
    • 3.2 游戏主体部分
      • 3.2.1 总体分析
      • 3.2.2 棋盘初始化
      • 3.2.3 棋盘展示
      • 3.2.4 设置地雷
      • 3.2.5 扫雷阶段
      • 3.2.6 统计雷个数的代码
      • 3.2.7 使用迭代的方式进行展开:
      • 3.2.8 扫雷部分主体代码
  • 4. 总结

1. 游戏说明

扫雷游戏的功能说明:

• 使⽤控制台实现经典的扫雷游戏

• 游戏可以通过菜单实现继续玩或者退出游戏

• 扫雷的棋盘是9*9的格⼦

• 默认随机布置10个雷

• 可以排查雷

◦ 如果位置不是雷,就显⽰周围有⼏个雷

◦ 如果位置是雷,就炸死游戏结束

◦ 把除10个雷之外的所有雷都找出来,排雷成功,游戏结束
在这里插入图片描述
在这里插入图片描述

2. 总体代码

//头文件部分
#define  _CRT_SECURE_NO_WARNINGS
#pragma once

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


//棋盘长宽
#define ROW 9
#define COL 9

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

#define EASY_COUNT 10 //地雷个数


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

//打印棋盘
void PrintBoard(char arr[ROWS][COLS], int row, int col);

//设置地雷
void MineSet(char mine[ROWS][COLS], int row, int col);

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

               //以上为头文件部分
//测试部分
#define  _CRT_SECURE_NO_WARNINGS

#include "game.h"

void menu()
{
	printf("******************\n");
	printf("****  1.play  ****\n");
	printf("****  0.exit  ****\n");
	printf("******************\n");
}

void game()
{
	char show[ROWS][COLS];//  '*'
	char mine[ROWS][COLS];//  '数字'
	//初始化
	InitBoard(show, ROWS, COLS, '*');
	InitBoard(mine, ROWS, COLS, '0');
	//打印棋盘
	//PrintBoard(mine, ROW, COL);
	PrintBoard (show, ROW, COL);
	//设置地雷
	MineSet (mine, ROW, COL);
	//PrintBoard (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:
			printf("< 请注意,本轮一共有%d颗雷 >\n", EASY_COUNT);
			game();
			break;
		case 0:
			printf("游戏结束\n");
			break;
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (input);
	
	return 0;
}
                       //以上为测试部分
//主体游戏部分
#define  _CRT_SECURE_NO_WARNINGS

#include "game.h"

void InitBoard(char arr[ROWS][COLS], int rows, int cols, char ch)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			arr[i][j] = ch;
		}
	}
}

void PrintBoard(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	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 ", arr[i][j]);
		}
		printf("\n");
	}
	printf("********************\n");
}

//设置地雷
void MineSet(char mine[ROWS][COLS], int row, int col)
{
	int count = 0;
	while (count < EASY_COUNT)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count++;
		}
	}
}


//统计该点附近雷的个数
int MineCount(char mine[ROWS][COLS],int x,int y )
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			count =count + (mine[i][j] - '0');
		}
	}
	return count;
}

//如果附近没有雷,直接展开
void BlankExpansion(char mine[ROWS][COLS],char show[ROWS][COLS],int x,int y)
{
	int i = 0;
	int j = 0;
	int count = MineCount(mine, x, y);
	if (count == 0)
	{
		show[x][y] = ' ';
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				if (show[i][j] == '*')
				{
					BlankExpansion(mine, show, i, j);
				}
			}
		}
	}
	else
	{
		show[x][y] = count + '0';
	}

}

//统计剩余‘*’的个数
int LeftCount(char show[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
				count++;
		}
	}
	return count;
}

//扫雷
void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	int count = row * col;
	int x = 0;
	int y = 0;
	while (count > EASY_COUNT)
	{	
		printf("请输入坐标:> ");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("好可惜,你被雷炸死了\n");
				PrintBoard(mine, ROW, COL);
				break;
			}
			else
			{
				BlankExpansion(mine, show, x, y);
				count = LeftCount(show, ROW, COL);
				PrintBoard(show, ROW, COL);
				printf("剩余未知点数: %d\n", count);
			}
		}
		else
		{
			printf("输入坐标错误,请重新输入\n");
		}
	}

	if (count == EASY_COUNT)
	{
		printf("恭喜你,你赢了!\n");
	}
	
}
                      //主体游戏部分

3. 详细讲解

3.1 菜单部分

打开游戏程序,第一步是展示菜单,提醒玩家进行选择,菜单与代码如下:
在这里插入图片描述


void menu()
{
	printf("******************\n");
	printf("****  1.play  ****\n");
	printf("****  0.exit  ****\n");
	printf("******************\n");
}

根据玩家的选择,进入不同的程序当中,当玩家选择 1 的时候,进入游戏程序,当玩家选择 0 的时候,结束游戏。考虑到玩家玩游戏不可能只是玩一次,因而需要在开头加入一个循环部分。代码实现如下:


int main()
{
	srand((unsigned int)time(NULL));//生成随机数
	int input=0;
	do
	{
		menu();
		printf("请输入你的选择:> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("< 请注意,本轮一共有%d颗雷 >\n", EASY_COUNT);
			game();
			break;
		case 0:
			printf("游戏结束\n");
			break;
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (input);
	
	return 0;
}

3.2 游戏主体部分

3.2.1 总体分析

扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些信息。
因为我们需要在 9 * 9 的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个 9 * 9 的数组来存放信息。

在这里插入图片描述
那如果这个位置布置雷,我们就存放1,没有布置雷就存放0.

在这里插入图片描述
假设我们排查 ( 2 , 5 ) 这个坐标时,我们访问周围的⼀圈 8 个⻩⾊位置,统计周围雷的个数是 1.
假设我们排查 ( 8 , 6 ) 这个坐标时,我们访问周围的⼀圈 8 个⻩⾊位置,统计周围雷的个数时,最下⾯的三个坐标就会越界,为了防⽌越界,我们在设计的时候,给数组扩⼤⼀圈,雷还是布置在中间的 9 * 9的坐标上,周围⼀圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成 11 * 11 是⽐较合适。
在这里插入图片描述
在这里插入图片描述

我们在棋盘上布置了雷,棋盘上雷的信息(1)和⾮雷的信息(0),假设我们排查了某⼀个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录存储,并打印出来,作为排雷的重要参考信息的。那这个雷的个数信息存放在哪⾥呢?如果存放在布置雷的数组中,这样雷的信息和雷的个数信息就可能或产⽣混淆和打印上的困难。
这⾥我们肯定有办法解决,⽐如:雷和⾮雷的信息不要使⽤数字,使⽤某些字符就⾏,这样就避免冲突了,但是这样做棋盘上有雷和⾮雷的信息,还有排查出的雷的个数信息,就⽐较混杂,不够⽅便。
这⾥我们采⽤另外⼀种⽅案,我们专⻔给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不⼲扰了,把雷布置到
mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期排查参考。
同时为了保持神秘,show数组开始时初始化为字符’*‘,为了保持两个数组的类型⼀致,可以使⽤同⼀套函数处理,mine数组最开始也初始化为字符’0’,布置雷改成’1’。如下如:

在这里插入图片描述
mine数组布置雷后的状态

在这里插入图片描述
show输出初始化的状态

对应的数组应该是:

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

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

为方便代码管理,将文件内容分为以下三个方面

test.c //⽂件中写游戏的测试逻辑  
game.c //⽂件中写游戏中函数的实现等 
game.h //⽂件中写游戏需要的数据类型和函数声明等 

为了方便后期对难易程度的修正,可以将行和列的信息用宏的方式进行修正,如下所示:

//棋盘长宽
#define ROW 9
#define COL 9

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

//地雷个数
#define EASY_COUNT 10
 
//修改后的数组
char show[ROWS][COLS];//  '*'
char mine[ROWS][COLS];//  '数字'

3.2.2 棋盘初始化

在布雷之前,需要将两个棋盘进行初始化。为了后面再计数的时候不影响判断,此时应该将 ROWS 与 COLS 传入函数当中。为了使初始化函数的能对两个数组都起作用,因而还要将要初始化的字符传进去。代码如下:


void InitBoard(char arr[ROWS][COLS], int rows, int cols, char ch)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			arr[i][j] = ch;
		}
	}
}

3.2.3 棋盘展示

初始化之后,需要写一个棋盘显示函数来显示棋盘的初始化结果以及后面不同结果的展示,代码如下:


void PrintBoard(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	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 ", arr[i][j]);
		}
		printf("\n");
	}
	printf("********************\n");
}

此处需要注意,传入数组的时候,要把整体 11 * 11 的数一起传进去,目的使方便后面对整体把控,但是在进行显示的时候,只需要显示 9 * 9 的范围即可,第一行和最后一行只是我们为了方便之后对地雷的统计而加进去的,给玩家展时的界面里面不应该存在第一行与最后一行的内容

3.2.4 设置地雷

在 mine 数组里面随机放置预选设置EASY_COUBT好的地雷数目,即EASY_COUBT代表的数。使用随机数生成函数生成随机坐标,mine 数组里面这个坐标的符号如果是 ’ 0 ’ ,则将 ’ 1 ’ 放入里面,用 count 进行计数,使布置的地雷数小于EASY_COUBT代表的数。代码如下:

void MineSet(char mine[ROWS][COLS], int row, int col)
{
	int count = 0;
	while (count < EASY_COUNT)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count++;
		}
	}
}

在这里应该注意,布置地雷的时候,数组同样是将全部传递过来,但是进行布雷操作的时候,只对 1 ~ 9 行数组进行操作,对剩下的两组不做任何操作。此外,因为 rand() % row 的结果是 0 ~ 8 之间的数,需要在此基础上加一,才能保证其只对 1 ~ 9 进行操作。

3.2.5 扫雷阶段

玩家随意输入一个坐标,先进行判断其是否在棋盘之内,如果不是,则提醒其重新输入,如果是,则要判断该点是否的是雷,如果是,则直接结束游戏,如果不是,则要将其附近的雷的个数统计出来。

3.2.6 统计雷个数的代码

int MineCount(char mine[ROWS][COLS],int x,int y )
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			count =count + (mine[i][j] - '0');
		}
	}
	return count;
}

如果统计的个数为非零,则直接在 show 数组相应的坐标上直接显示附近雷的个数,如下图:
在这里插入图片描述
如果为零,则要对其附近的点进行架空处理,即在 show 数组上把其附近全部为零的坐标全部变为空格,直至附近的数全部显示为非零值,如下图:
在这里插入图片描述

3.2.7 使用迭代的方式进行展开:

当玩家输入的坐标附近的雷统计数为零的时候,对该坐标附近的八个坐标都要进行判断其附近是否为零,如果不为零,直接显示其附近雷的个数,如果为零,又要调用该函数对其附近的八个坐标进行判断,一直往复循环,直至附近的坐标全部为非零数。代码如下:

void BlankExpansion(char mine[ROWS][COLS],char show[ROWS][COLS],int x,int y)
{
	int i = 0;
	int j = 0;
	int count = MineCount(mine, x, y);
	if (count == 0)
	{
		show[x][y] = ' ';
		for (i = x - 1; i <= x + 1; i++)
		{
			for (j = y - 1; j <= y + 1; j++)
			{
				if (show[i][j] == '*')
				{
					BlankExpansion(mine, show, i, j);
				}
			}
		}
	}
	else
	{
		show[x][y] = count + '0';
	}

}

3.2.8 扫雷部分主体代码

void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col)
{
	int count = row * col;
	int x = 0;
	int y = 0;
	while (count > EASY_COUNT)
	{	
		printf("请输入坐标:> ");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("好可惜,你被雷炸死了\n");
				PrintBoard(mine, ROW, COL);
				break;
			}
			else
			{
				BlankExpansion(mine, show, x, y);
				count = LeftCount(show, ROW, COL);
				PrintBoard(show, ROW, COL);
				printf("剩余未知点数: %d\n", count);
			}
		}
		else
		{
			printf("输入坐标错误,请重新输入\n");
		}
	}

	if (count == EASY_COUNT)
	{
		printf("恭喜你,你赢了!\n");
	}
	
}

4. 总结

在进行传参数的时候,一定要紧记,传数组时,要将整个数组全部传过去,但是对数组进行操作的时候,只对 1 ~ 9 行进行操作,因为玩家所能看到的展示界面只有 1 ~ 9 行,唯独初始化的时候是要对整个数组进行操作。

以上就是小编要分享的内容,如果有不对的地方,欢迎大家在留言区讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1511061.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云第一次面试记录

java多态&#xff1f; 多态表示一个对象具有多种的状态&#xff0c;具体表现为父类的引用指向子类的实例 Fu f Zi z(); 多态是同一个行为具有多个不同表现形式或形态的能力。 多态就是同一个接口&#xff0c;使用不同的实例而执行不同操作 特点&#xff1a; 对象类型和引用类型…

C++中实现String类

String类实现 概述示例开发环境代码运行结果 注意 概述 本文主要记录自己实现一个String类中的部分功能。 示例 开发环境 Windows下Visual Studio 2019。 代码 MyString.h #pragma once #include <iostream>class MyString{ public:MyString();MyString(char *p);…

c语言函数:atoi与memset

1.atoi函数的头文件stdlib.h 空格跳过&#xff0c;遇到非数字非空格字符准备结束&#xff0c;如果前面有数字则输出数字&#xff0c;没有则输出0&#xff0c;其中-号不受影响&#xff0c;但不输出 memset函数&#xff08;目标值&#xff0c;操作值&#xff0c;字节数&#xff…

LeetCode102题:二叉树的层序遍历(python3)

代码思路&#xff1a;使用队列先进先出的特性&#xff0c;queue[]不为空进入for循环&#xff0c;tmp存储每层的节点&#xff0c;将结果添加至res[]中。 python中使用collections中的双端队列deque()&#xff0c;其popleft()方法可达到O(1)时间复杂度。 class Solution:def lev…

YOLOv9改进项目|关于本周更新计划的说明24/3/12

目前售价售价59.9&#xff0c;改进点30个 专栏地址&#xff1a; 专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 日期&#xff1a;24/3/12 本周更新计划说明&#xff1a; 1. 更新华为Gold YOLO中的…

为表重命名

oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 为表重命名 DDL 属 于 数 据 对 象 定 义 语 言&#xff0c; 主 要 的 功 能 是 创 建 对 象&#xff0c; 所 以 表 创 建 单 词 是create 但问题是&#xff0c;这些对象被…

如何使用vue实现购物车中数字的增减操作

实现效果如下&#xff08;当点击购买数量的时候&#xff0c;可以增减&#xff09;&#xff1a; 首先&#xff0c;写出界面原型&#xff1a; <div id"container"><table ref"myTable"><thead></thead><tbody><tr><…

SQL数据库-SQL命令-SQL语言

我要成为嵌入式高手之3月12日Linux高编第二十天&#xff01;&#xff01; ———————————————————————————— 数据库软件 关系型数据库&#xff1a; Mysql Oracle SqlServer Sqlite 非关系型数据库&#xff08;相当于在内存中搞了一块空间&#xff…

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 2、线条平滑曲面但有间隔

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata imp…

大模型时代下的 BI——智能问数

「智能问数」是 Sugar BI 基于文心大语言模型推出的对话式数据问答产品&#xff0c;让用户能够通过自然语言的方式进行对答形式的数据查询&#xff0c;系统自动使用可视化图表的方式呈现数据结果&#xff0c;并支持对数据做summary总结。 智能问数功能邀测中&#xff0c;欢迎CS…

海外媒体发稿:新闻媒体发稿7种方法-华媒舍

新闻报道媒体发稿营销推广是当代企业形象宣传的重要手段之一&#xff0c;合理推广可以提升品牌知名度、吸引住潜在用户。在这篇文章中&#xff0c;我们将要详细介绍七种科学合理的形式&#xff0c;帮助你完全把握新闻报道媒体发稿营销推广。 1.明确目标群体明确目标群体是实现推…

算法之滑动窗口

题目1:209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 解法⼀(暴力求解): 思路&#xff1a; 从前往后, 枚举数组中的任意⼀个元素, 把它当成起始位置, 然后从这个起始位置开始, 然 后寻找⼀段最短的区间, 使得这段区间的和「⼤于等于」⽬标值. 将所有元素作为…

郑州大学2024年3月招新赛题解

1.两重二for循环维护次大值 这里我就直接用map维护了&#xff0c;多了个logn复杂度还是可以的&#xff0c;下面是AC代码&#xff1a; #include<bits/stdc.h> using namespace std; int n,a[1010]; map<int,int> mp; int main(){cin>>n;int sum0;map<int,…

操作系统--LRU算法,手撕

今天研究一下LRU算法&#xff0c;上学期学数据结构的时候就应该学一下这个算法&#xff0c;不过后面操作系统也会讲到LRU算法 题目 LRU缓存leetocde146 LRU&#xff08;Least Recently Used&#xff0c;最近最少使用&#xff09;算法是一种常见的缓存替换算法&#xff0c;通…

索引失效的介绍和避免方法

索引是什么 在关系数据库 中&#xff0c;索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种 存储结构 &#xff0c;它是某个表中一列或若干列值的集合和相应的指向表中物理标识这些值的数据页的逻辑指针清单。 索引的作用相当于图书的目录&#xff0c;可以根据…

​《宏伟世纪》在 TheSandbox 中带来虚拟苏丹体验!

《宏伟世纪》&#xff08;Magnificent Century&#xff09;与 The Sandbox 合作&#xff0c;将戏剧带入数字领域&#xff01;这部土耳其历史小说电视连续剧以苏丹苏莱曼大帝和许蕾姆苏丹的生平为原型&#xff0c;曾在 140 多个国家和地区播出&#xff0c;收视率超过 5 亿&#…

交换机STP工作原理

文章目录 一、确定交换机角色二、确定端口角色1.根端口选举2.指定端口选举3.非指定端口选举 三、确定端口状态常用查询命令实验拓扑实例一拓扑实例二拓扑实例三拓扑实例四拓扑图实例五 BPDU报文中携带的Root Identifier、Root Path Cost、Bridge Identifier、Port Identifier字…

13年老鸟整理,性能测试技术知识体系总结,从零开始打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 从个人的实践经验…

新质生产力浪潮下,RPA如何成为助力先锋?

新质生产力浪潮下&#xff0c;RPA如何成为助力先锋&#xff1f; 在数字化、智能化的今天&#xff0c;“新质生产力”一词越来越频繁地出现在我们的视野中。那么&#xff0c;究竟什么是新质生产力&#xff1f;它与我们又有什么关系&#xff1f;更重要的是&#xff0c;在这一浪潮…

2024年,10大产业趋势:创新驱动下的全面转型与发展

本趋势指南深入探讨塑造企业创新未来的力量&#xff0c;以及为什么企业必须改变创新方式。指南概述了创新未来的愿景&#xff0c;其中人类智慧和AI技术在创新中相结合&#xff0c;相互补充和放大&#xff0c;这将是一个全新范围的端到端创新平台&#xff0c;旨在将各个点连接起…