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语言】扫雷小游戏详解 -> 正文阅读

[游戏开发]【C语言】扫雷小游戏详解

【C语言】扫雷小游戏详解

前言:
还记得大明湖畔的夏雨荷,电脑课上的扫雷吗?

———————————————————————————是 他 吗———————————————————————————

没错,就是他。你电脑课上偷偷玩过的扫雷,今天我们用C语言做一个扫雷小游戏。

《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。

接下来让我们进入代码的世界:


目录:

一、想法与构思

1.想法

首先想要做一个游戏就先明白游戏的规则和玩法,扫雷,就是在一个n*n的棋盘上操作,玩家通过点击格子出现的数字找出所有非雷格子,避免踩雷,当所以非雷的格子都被找出,那么玩家获胜,一旦踩到任意一颗雷,则玩家失败。

没错,这就是我们想写出来的游戏,那么接下来就是构思了↓

2.构思

写任意一个代码游戏的时候都要先构思,这样写出来的代码才是比较好的,能让你事半功倍。
????????????????????? ? ? ? ? ———沃·兹基硕德

构思好了当你开始写你的代码的时候到那个地方不对劲的时候你也可以比较快速的找到错误并纠正他

废话不多说,开锤!

首先我们要确定我们代码中要实现的:
(为了更好的说明我们制作一个简单难度的扫雷)

  1. 制作一个9 x 9的棋盘并初始化埋好雷。
  2. 制作一个9 x 9的棋盘显示信息。
  3. 输入坐标然后显示该坐标结果。
  4. 游戏结果判断。

完成构思接下来就进入我们的代码领域了。



二、代码与实现

1.代码分块

同样的,为了让整个代码更加可观,我们首先把代码分为三个板块:
1.test.c源文件
测试游戏和一些细节的补充。

2.game.h头文件
存放游戏函数的声明。

3.game.c源文件
整个游戏的实现。



2.test.c 源文件

首先我们来将代码的整体框架给写出来:

//先写一个菜单函数,让我们选择是否进入游戏
void menu()
{
	printf("***********************************\n");
	printf("***********   1. play     *********\n");
	printf("***********   0. exit     *********\n");
	printf("***********************************\n");
}

void game()//游戏主体的代码
{

}

int main()//主函数
{
	int input = 0;
    do
	{
		menu();//引用上面的菜单函数
		printf("请选择>");
		scanf("%d", &input);//选择是否进入游戏
		switch (input)
		{
		case 1://输入1进入游戏
			game();
			break;
		case 0://输入0退出游戏
			printf("已退出游戏!");
			break;
		default://输入其他会提示输入有误
			printf("选择错误,请重新选择!\n");
			break;
		}
	} while (input);

	return 0;
}

然后我们要补充好game()的部分:
按照构思,我们应该要把棋盘写出来,但是在这里需要想一想,是不是两个棋盘更好一点,所以我们可以分别写两个棋盘,一个棋盘放置的是我们扫雷里面哪个下标下是有雷的,然后另一个棋盘作为给玩家进行游戏时看到的棋盘,是只会显示提示附近有几颗雷或者无雷的棋盘。

然后还有一点,我们将数组作为每一个格子里面存放是否有地雷的容器,那当边框坐标要读取旁边有多少地雷的时候,是不是也读取旁边的8个坐标呢,但是边框旁边没有呀,那我们可以在边框上再围上一层边,然后这一层边上的不放雷就好了,所以是加上2的边框。

———————————>

那么直接写?不不不,还记得我们区分板块吗,在这里我们只需要命名几个函数,然后在game.c上写就可以啦(如果你是自己写的时候建议一边命名函数一边写,然后调试过后再写下一个,这里为了给大家写思路更流程一些才这样写!)

代码如下:

//这里是game.h头文件
#pragma once

#include <stdio.h>

#define ROW 9//定义我们的9 x 9简单难度扫雷棋盘
#define COL 9//当然你要难一点可以改数据哦

#define ROWS ROW+2//让外面增加一圈,当该坐标扫描身边坐标时,使数组不越界
#define COLS COL+2
//这里是test.c源文件 

void game()
{
    //既然要写两个棋盘那么就写两个不同的数组存放
   
	char Mine[ROWS][COLS] = { 0 };//创建初始后台数组
	char Show[ROWS][ROWS] = { 0 };//创建初始显示数组

    //  然后是写我们初始化棋盘的函数,这里因为都是棋盘所以我们可以用同一个
    //函数然后放入不同数据。
    InitBoard(Mine, ROWS, COLS, '0');//初始化后台
	InitBoard(Show, ROWS, COLS, '*');//初始化扫雷

    //那初始化后台之后呢,我们需要把雷给埋好吧?写一个函数布雷。
    SetMine(Mine, ROW, COL);//布雷
	
	//然后进入游戏首先要让玩家看到棋盘,后续也要进行打印进行游戏
	DisplayBoard(Show, ROW, COL);//打印棋盘

    //看到棋盘之后就开始游戏了,所以这里需要的是我们棋盘游戏的函数。
	FindMine(Mine, Show, ROW, COL);//排雷游戏开始
}

所以tese.c就是把我们的函数构思再完善一遍。然后每一个对应函数在game.h里面写,这样份块的优势就是清晰明了。



3.game.h头文件

头文件这里就没什么好说的了,就是把每一个函数都赋予声明,让它能正常运行,前面我们的test.c中game里面写了四个函数,我们只需要把他们分别声明,并且确定好函数的传参就可以了。

代码如下:


#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的思路。



4.game.c源文件

1.分函数实现

同样的,这里我们分为几个函数的讲解

  1. InitBoard 初始化函数(把棋盘数据初始化)
  2. DisplayBoard 打印函数(进行游戏时打印给玩家看)
  3. SetMine 随机布雷函数(棋盘初始化后需要进行布雷)
  4. GetMineCount 返回雷数函数(负责在游戏进行时,玩家输入坐标返回周围雷的数字提示)
  5. FindMine 扫雷游戏函数(进行游戏时判断游戏是否继续)

接下来–

2.InitBoard 初始化函数

这是一个初始化函数,目的就是把我们之前设置的二维数组里面每一个坐标下的内容初始化,比如我们的后台得先把他的初始化成0,然后待会的布雷函数随机放十个1进去就可以了。

那初始化两个数组要放置的内容不同怎么办,我们可以把他们需要初始化的字符作为传承传过去。

代码如下:

//这里我们的set变量就是接收在test.c中传过来的字符,然后初始化函数里面的内容
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++)
		{
			//我们用两个for循环将每一个数组都触及
			board[i][j] = set;//初始化
		}
	}
}

这样我们就把Mine二维数组中的数字全部换成了’ 0 ‘,而显示数组Show中就全部换成了’ * '。


3.DisplayBoard 打印函数

首先我们要思考我们的打印出来的是什么样子的一个界面给别人玩?

是这样子的:

还是这样子的:

还是这样子的:

很明显,三者之间最后一个比较神似扫雷一点吧?同时也有第二个的前置坐标提示,不用担心游戏还没玩多久眼睛就瞎了的第一种。

我们先写第二种:
代码如下:

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	for (i = 0; i <= 9; i++)//顶部数字坐标提示
	{
		if (i == 0)
		{
			printf("0 ");
		}
		else
		printf("%d ", i);
	}
	printf("\n");
	
	for (i = 1; i <= row; i++)
	//这里传进来的row是9,所以可以打印1-9即是9 x 9棋盘上的内容
	{
		int j = 0;
		printf("%d ", i);//靠左数字坐标提示

		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//打印棋盘的显示
		}
		printf("\n");
	}
}

然后就是弄的好看一点嘛,把周围放上边框,加上小游戏,把他移动到不那么靠左的地方。(优化的地方我会标注上说明)

代码如下:

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("            扫雷小游戏      \n");//小游戏说明
	printf("       --------------------\n");//框的上部
	for (i = 0; i <= 9; i++)
	{

		if (i == 0)
		{
			printf("      |0 ");
			//其实把游戏字体右边移一点就是左边打印一些空格,这里我打了6个
		}
		else
		printf("%d ", i);
	}
	
	printf("|");//在上部数字提示那里打印框的右边最上面的一个
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("      |");//打印框的左部
		printf("%d ", i);//打印框后才打印数字提示

		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("|");//打印框的右部剩下的
		printf("\n");
	}
	printf("       --------------------\n");
	//打印框的下部
}

这样子就可以完好的打印出我们的显示界面了。

4.SetMine 随机布雷函数

随机布雷函数就比较简单了,我们已经初始化将后台表中所有数组内都变成了字符0了,那么我们只需要在数组范围内随机生成几个坐标然后把字符1替换进去就可以了。

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	//注意,这里我们直接对应是我们头文件中简单难度的10个雷
	
	while(count)//布一个雷就少一个,当count==0就不进入循环了
	{
		
		int x = rand() % row + 1;//随机生成x,y坐标布置
		int y = rand() % col + 1;
		
		if (board[x][y] != '1')
		//为了防止重复布雷进行一个if语句的筛查
		{
			board[x][y] = '1';//替换
			count--;
		}    
	}
}

以上就是我们的布雷函数了,如果想要扩大界面布置更多的雷可以将数据改一下就可以啦。


5.GetMineCount 返回雷数函数

这个是镶嵌在第五个就是FindMine 扫雷游戏函数中的,但为了下面的函数大家更好理解,我们就先将这个函数写了。

返回雷的个数就是我需要统计我输入坐标周围的8个坐标中是否有雷,那么这里就显现出我们当初设置空为字符0,雷为字符1的优势了,当我们要知道附近雷的个数,可以直接用周围的ASCII码值来进行计算。

比如:当右边是地雷字符1的时候,我们只需要计算他减去字符0就可以得到存在1个地雷了,当不是地雷时相减就是存在0个地雷。所以

代码如下:

int GetMineCount(char Mine[ROWS][COLS],int x,int y)
//假设我输入的坐标是x,y
{
	int ret = 0;//计算总和
	int i = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
		//表示周围8个就是两个for循环从减一到加一
			ret = ret + Mine[i][j] - '0';
		}
	}
	return ret;//这样我们就可以将多少个雷的提示返回

}


6.FindMine 扫雷游戏函数

首先我们要明确这个函数要干什么:

1. 玩家输入坐标进行游戏
2. 判断玩家输入坐标是否在范围内
3. 判断玩家坐标是否为地雷
4. 判断玩家是否扫出所有地雷游戏胜利

那我们挨个实现:

代码如下:

void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;//创建x,y的变量表示坐标
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{
		printf("请输入要排查的坐标:>");//1.玩家输入坐标
		scanf("%d %d", &x, &y);
		
		if (x >= 1 && x <= row && y >= 1 && y <=col)
		//2.判断玩家输入坐标是否在范围内
		{
			if (Mine[x][y] == '1')//3.是字符1就是地雷
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(Mine, ROW, COL);
				break;
			}
			else//不是地雷就要显示周围地雷数
			{
				int count = GetMineCount(Mine, x, y);
                //这时候就调用GetMineCount函数返回值了

				Show[x][y] = count + '0';
				DisplayBoard(Show, ROW, COL);//打印给玩家看结果
				win++;
				//不是雷就加1,当win是棋盘减雷数就获胜
			}
		}
		else
		{
			printf("坐标非法,请重新输入:\n");//输入坐标不在范围内
		}
	}
	if (win == row * col - EASY_COUNT)
	//获胜条件
	{
		printf("恭喜你赢得胜利!\n");
		DisplayBoard(Mine, ROW, COL);
	}
}

然后这就是我们的扫雷函数啦。


三、完整代码

1.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] = { 0 };//创建初始后台数组
	char Show[ROWS][ROWS] = { 0 };//创建初始扫雷数组
	InitBoard(Mine, ROWS, COLS, '0');//初始化后台
	InitBoard(Show, ROWS, COLS, '*');//初始化扫雷
	SetMine(Mine, ROW, COL);//布雷
	DisplayBoard(Mine, ROW, COL);//打印棋盘
	DisplayBoard(Show, 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("已退出游戏!");
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
			
	} while (input);
	return 0;
}

2.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);


3.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");
	printf("       --------------------\n");
	for (i = 0; i <= 9; i++)//顶部数字顺序
	{

		if (i == 0)
		{
			printf("      |0 ");
		}
		else
		printf("%d ", i);
	}
	
	printf("|");
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("      |");
		printf("%d ", i);//打印每一行前面的顺序数

		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);//打印棋盘内
		}
		printf("|");
		printf("\n");
	}
	printf("       --------------------\n");
}

//布雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	
	int count = EASY_COUNT;

	while(count)
	{
		
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] != '1')
		{
			board[x][y] = '1';
			count--;
		}    
	}
}


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

}


//排雷游戏开始
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 %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);
	}
}

这就是扫雷小游戏的C语言写法啦,小白制作不易,有错的地方还请xdm指正,互相关注,共同进步。

还有一件事:

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-01 14:48:33  更:2021-08-01 14:48:46 
 
开发: 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年4日历 -2024/4/28 12:34:26-

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