IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 新手30天,C语言实现扫雷(上) -> 正文阅读

[C++知识库]新手30天,C语言实现扫雷(上)

因为大学生活的到来,对程序设计这门课程非常感兴趣,便开始超过学校进度开始自学,自学一个月过去了,不断摸索,写了无数bug,分享一个bug游戏,希望大家给给意见,让我也尝一下进步的感觉

软件用的是VS2019

首先建立新的(空项目)工程:

?随后开始思考(构建框架)

扫雷是啥?

?扫雷的功能?

一、有边边框框,方便用户操作

二、呈方形的排布(二维数组)

三、具有游戏开始界面、具有游戏模块

游戏模块包括:

(一)画出雷区

?

(二)随机生成雷区

? ? ? ? 随机在雷区产生指定数目的地雷

(三)玩家扫雷

(四)系统判定

? ? ? ? ①是否合法的操作(有没有在棋盘内操作)

? ? ? ? ②是否已经扫过了

? ? ? ? ③是否炸了

? ? ? ? ④是否赢了

(五)更新游戏最新画面(更新雷区)

我们现在想象,其中的(三)(四)(五)具有重复性,(一)(二)属于游戏生成阶段的产物

现在,我们思路已经清晰了,我们开始写这一代码:

第一步:创立三个文档

?

以上述的步骤,创建出以下三个文件,注意其中一个是game.h是头文件,源文件处的后缀可以是cpp,也可以是c,由于我正在学c语言,此处使用.c作为后缀。

?

(一)预处理第一个文档:?在test.c的源文件中,键入代码:

//广东工业大学
#define _CRT_SECURE_NO_WARNINGS

int main()
{
	test();
}

?因为是任务驱动编程,因此test()函数,作为整个程序的起点

#define _CRT_SECURE_NO_WARNINGS

此语句为C语言环境中使用scanf函数时,屏蔽系统得不安全警告

(二)预处理第二个文档:在game.h文件中键入

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

第一行为创建的时候自带的,先不管

为什么要在头文件中输入三个库函数呢?

这里我们可以想象:

你每天起床都会喝果汁

果汁有:草莓汁,蓝莓汁,香蕉汁等一堆汁

假如你每天起床,都要一杯杯的打果汁,又耗时又费力

就像你在每个不同的文档里面写代码,每个文档都要引用一大堆头文件。

那么,我们可以想到一个好方法:

把一堆水果,都放在同一个果汁机里面,打成一杯,然后一口喝下!

这里我们把可能用到的头文件,都放在了game.h头文件中

接着,我们在源文件(game.c和test.c)中,引用

(三)第三个文件,我们弄好头文件后,就先不管

好,现在开始,我们开始实现最根本的流程

因为任务是,在test()函数中,实现最基本的运行逻辑

我们打开test.c文件编写如下代码

?如图:代码在图下

//广东工业大学
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"

void test()
{
	int input = 0;
	do
	{
		menu();
		printf("请输入选项\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
		{
			printf("进行游戏\n");
			game();
			break;
		}
		case 0:
		{
			printf("退出游戏\n");
			break;
		}
		default:
		{
			printf("输入献血不合法请重新输入\n");
		}
		}
	} while (input);
}
int main()
{
	test();
}

我们开始解读一下这个代码行

首先,我们想要读取一个用户的输入,所以我们设置一个变量,然后请求用户输入

再者,我们希望,无论如何,请求输入的次数永远是大于零次的(谁也不想还没开始,就被关掉了游戏),因此我们使用do-while函数进行最基础的框架搭建

最后,game()函数作为游戏的最终入口!

menu()函数,是实现用户交互的函数,我们首先要有一个菜单

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

实现:

好,我们跑起来现在最基本流程了

一、进入

二、询问

三、输入

四、判断

五、开始(或退出)




以下是game函数的写法

void game()
{
	//识别扫雷的个数
	srand((unsigned)time(NULL));
	//布置雷的信息
	char mine[ROWS][COLS] = { 0 };//雷
	//根据玩家落子,扫描雷的信息,进行反馈
	char show[ROWS][COLS] = { 0 };
	//初始化雷区和识别区域
	InitBoard(mine, ROWS, COLS,'0');
	InitBoard(show, ROWS, COLS,'*');
	
	//放雷
	SetMine(mine,ROWS,COLS);

	//打印雷区进行观察
	Displayboard(mine, ROWS, COLS);
	//mine[1][1] = '1';手动放了一个

	Displayboard(show, ROWS, COLS);

	//展示棋盘,开始游戏
	//扫雷
	int zt = 0;//状态
	int win = 0;
	do//不断的扫雷,直到炸死,或者结束
	{
		zt = 1;
		zt = OutMine(mine, show, ROWS, COLS,&win);
		if (zt == 0)
			break;
		Displayboard(show, ROWS, COLS);
	} while (1);
}

?首先,我们先不管第一行的srand()后面再细讲

我们可以看见有两个数组

?这两个数组,分别存储:

(一)mine存储的是随机产生的雷

(二)show是存储用户视角的数据

不懂?没关系

mine数组,对应的是雷的排布:(相当于地下)

而玩家所看见的是show数组(相当于地上)

?

而创建数组的时候,为了方便后期维护,我们不写具体的数组框定数组的范围

在这里我们使用定义常量

诶?为什么定义的时候是ROW和COL

而在数组的那边是ROWS和COLS?

因为假设雷区是3*3

如果程序要判断坐标(2,2)附近雷的个数,自然去搜索

但是!

如果程序要判断坐标(3,2)附近雷的个数,要去搜索

显然,棋盘是不够大的

但是!

如果我们人为,在棋盘的周围,多加一个圈

那么

计算机搜索的时候,便不需要刻意去搞特殊!

这样,在棋盘的周围多加了一圈,蓝色就能肆无忌惮的扫描,不用在意壁垒。

因此,在宏定义下,我们在上下左右各加一行,则是

因此ROWS/COLS是扩大后的棋盘横纵


?观察game函数

我们第一件事,就是建立一个地下,和地面

第二步:是将地下的区域,全部都置零(先把杂质全部挖掉,为后面埋雷做准备)

?

参数的意义是:

mine/show:传入数组

ROWS/COLS:是代表行数和列数

而'0'和'*'则是代表将mine数组或者show数组填满所用的元素

效果(故意打印出来后)大概为:

?(注:上边棋盘的1,是随机产生的地雷,产生地雷的方法后面再说)


在game.c内实现各个函数的内容:

void InitBoard(char board[ROWS][COLS], int rows, int cols,char set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int u = 0; u < cols; u++)
		{
			board[i][u] = set;
		}
	}
}

这是将两个数组mine和show初始化的函数


光有内容,可不行,现在还没有能打印出我们游戏界面的功能

而,game()中的Displayboard()

便是展现游戏界面的功能

void Displayboard(char board[ROWS][COLS], int rows, int cols)
{
	int count1 = 0;
	for (int i = 1; i < rows-1; i++)//行--为了方便扫雷,左右上下都加了一栏
	{
		if (i==1)
		{
			//printf("   |");
			for (int h = 0; h < cols - 1; h++)
			{
				printf(" %d ", h);
				if (h!=cols-2)
				{
					printf("|");
				}
			}
			printf("\n");
			for (int j = 0; j < cols-1; j++)
			{
				printf("---");
				if (j!=cols-2)
				{
					printf("|");
				}
			}
			printf("\n");
		}
		for (int u = 1; u < cols-1; u++)//列的数据线
		{
			if (u==1)
			{
				count1++;//计数,打印序号
				printf(" %d ",count1);
				if (u!=cols-2)
				{
					printf("|");
				}
			}
			printf(" %c ", board[i][u]);
			if (u != cols-2)
			{
				printf("|");
			}
		}
		printf("\n");
		for (int k = 1; k < cols-1; k++)//列的分割线
		{
			if (i == rows - 2)//判断若是最后一行则免去分割线
			{
				break;
			}
			if (k == 1)
			{
				printf("---");
				if (k!=cols-2)
				{
					printf("|");
				}
			}
			printf("---");
			if (k != cols-2)
			{
				printf("|");
			}
		}
		printf("\n");
	}
}

这个函数实现了:

(一)在棋盘的周围布上数字,让用户更好的定位坐标

(二)在格子之间加上分割线,使格子更好看

(三)根据show和mine的棋盘内容,相应打印出数组内的情况

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-08 11:36:46  更:2021-10-08 11:37:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 1:52:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码