C语言——简单扫雷小游戏

一、分模块

因为两个都是小游戏,大的实现方向也一致,所以就偷个懒,倒一下昨天的图。
整体构思

二、具体实现

1、声明模块

这部分放在了game.h 文件里,主要是对用到的头文件、函数和 #define 常量进行声明,声明完之后其他文件只要包含这个头文件就可以使用这里出现的所有东西。下面把这部分代码作一个展示:

#define _CRT_SECURE_NO_WARNINGS

//扫雷的规模
#define ROW 5
#define COL 5

//为了计算边的时候防止溢出,在本来规模的基础上加一层边界
#define ROWS (ROW+2)
#define COLS (COL+2)

//设置雷的个数
#define NUM 5

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

 //初始化表格
void InitTable(char arr[ROWS][COLS], char c); 
//将表格打印到屏幕上
void PrintTable(char arr[ROWS][COLS]);    
//放NUM个雷
void SetMine(char arr[ROWS][COLS]);
//扫雷
void FindMine(char arr1[ROWS][COLS], char arr2[ROWS][COLS]);
//计算周围一圈雷的个数
int CalMineNum(char arr[ROWS][COLS], int x, int y);

2、测试模块

这部分放在了test.c 文件里,主要实现的是玩家打开游戏之后,出现在屏幕上的逻辑构想。

首先,菜单的打印不需要实现什么逻辑,所以我就把它和主函数放在了一个源文件里面。然后对游戏的选择我使用了while语句嵌套switch语句的常见套路。代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"

void menu() {
	printf("________________________________________\n");
	printf("*********  欢迎进入扫雷游戏! **********\n");
	printf("************ 玩游戏请输 1 **************\n");
	printf("************  退出请输 0  **************\n");
	printf("————————————————————\n");
	return;
}
int main() {
	srand((unsigned int)time(NULL));
	int input = 0;
	do {
		menu();
		printf("请输入:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("感谢体验,已退出!\n");
			break;
		default:
			printf("只能输入1/0,请重新输入!\n");
			printf("\n");
			break;
		}
	} while (input);
	return 0;
}

3、实现模块

这部分放到了 game.c 文件里,主要实现的是整个游戏需要的所有具体功能的逻辑实现。下面具体说一下实现:

(1)游戏主要功能

这部分就像是一个树的枝干,所有具体的每个流程都是从这里分散出去的,主要逻辑也是在这里构思。同样先放流程图:
扫雷
首先肯定是定义格盘数组,在这里我定义了两个。玩家排雷,点开非雷的格盘需要计算周围一圈的雷的个数,这就是一个盘,我把它命名为play,就是实际会打印在屏幕上的那个供玩家看的格盘。还有一个盘我命名为mine,这个盘里放的是在这局游戏里我设置的雷的个数。

下面就是整个游戏的流程:初始化雷盘 -> 将雷盘打印到屏幕 -> 随机设置一定个数的雷 -> 玩家排雷。具体实现如下代码:

void game() {
	//全部初始化
	char mine[ROWS][COLS] = { 0 };    //放雷的盘
	char play[ROWS][COLS] = { 0 };    //玩游戏即找雷的盘
	InitTable(mine, '0');
	InitTable(play, 'x');
	PrintTable(play);
	//设置雷
	SetMine(mine);
	PrintTable(mine);    //测试时使用,正式使用可以注销掉
	//找雷
	FindMine(mine, play);
	return;
}

(2)初始化格盘

对于两个盘的内容:

  • mine盘:有雷:‘1’;无雷:‘0’(为了后面计算一圈雷的个数方便)
  • play盘:未查:‘x’;查过:显示周围雷的个数

由于两个盘需要初始化的内容不同(mine盘——‘1’;play盘——‘x’),所以在初始化的格盘里还需要加一个形参初始化内容。其他就没啥好说,就是遍历赋值。具体代码实现如下:

void InitTable(char arr[ROWS][COLS], char c) {
	int i = 0;
	int j = 0;
	for (i = 0; i < ROWS; i++) {
		for (j = 0; j < COLS; j++) {
			arr[i][j] = c;
		}
	}
	return;
}

(3)打印格盘

由于雷盘不需要像三子棋那样要具体的格子也可以玩,所以省了很多事就直接遍历然后按行列打印即可。

只有一个需要注意的点就是为了防止计算边缘盘格溢出加的辅助格在这里也被作为形参传了进来,而真正打印是不需要这些格子的,所以循环都是直接从1开始,到本身行列值的地方(ROW,COL)结束即可。

void PrintTable(char arr[ROWS][COLS]) {
	int i = 0;
	int j = 0;
	for (i = 1; i <= ROW; i++) {
		for (j = 1; j <= COL; j++) {
			printf(" %c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("\n");
	return;
}

(4)放雷部分

这里和三子棋游戏一样,因为放雷的位置是随机的,采用了rand函数,在主函数里,以时间戳为基准指标,保证值是随机产生,再取余行列值保证值都是合法。

当然这里要给行列值+1,原因同“(3)打印格盘”,为了防止计算边缘盘格溢出加的辅助格在这里也被作为形参传了进来,而真正放置雷的时候是不需要这些格子的,所以设置值的时候也是从1开始。

然后放的内容——有雷放 ‘1’ 就好;雷的数量是在头文件里声明过的NUM。

void SetMine(char arr[ROWS][COLS]) {
	int num = NUM;
	while (num > 0) {
		int x = rand() % ROW + 1;
		int y = rand() % COL + 1;
		if (arr[x][y] == '0') {
			arr[x][y] = '1';
			num--;
		}
	}
	return;
}

(5)排雷部分

首先需要传进来两个盘,我设 arr1 是mine盘,arr2 是play盘。

排雷过程中,当玩家排到不是雷的部分,需要计算出这个play盘格周围的雷的个数,同样考虑到可读性,我把这个单独拎出来写了一个函数实现这一功能。

当遇到字符 ‘1’ ,给周围一圈雷的个数count加一。设立整型返回值,返回count。因为只在game.c这个源文件里使用,所以我也用关键字 static 限制了它的使用范围。计算周围一圈雷的个数的代码实现如下:

static int CalMineNum(char arr[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++) {
			if (arr[i][j] == '1') {
				count++;
			}
		}
	}
	return count;
}

接着,排雷结果有两种,雷排完,玩家胜利:雷爆出,玩家失败。这里就需要判断雷是否排完,那么就需要计算剩余的个数看是否和一开始声明里规定的NUM一样。我用的方法是设一个次数 time ,初始化为mine盘格总数,当玩家每成功排完一个雷格,就给它减一,最后当剩余次数与雷个数一致时,说明雷被排完,玩家胜利。

大概流程就是输入一个行列值,判断合法之后看这个play盘格对应的mine盘里有没有雷,有雷直接结束;如果没雷再看一下这个盘格排查过了没,这样防止最后雷没排完,但是 time 值已经和NUM一样,游戏结束,这样的话规则就会被破坏;没雷且没排过就time减一,把周围一圈雷的个数通过CalMineNum函数返回值赋给排查过的play盘格。

这里还想说一个点,因为两个盘格里放的都是字符,雷的个数CalMineNum函数返回值是整型数字,所以最后计算周围一圈雷的个数要用 0 的ASCII码值加上雷的个数,表示雷的个数的ASCII值,再赋给play盘格。

实现代码如下:

void FindMine(char arr1[ROWS][COLS],char arr2[ROWS][COLS]) {  
	int x = 0;
	int y = 0;
	int time = ROW * COL;
	while (time > NUM) {
		printf("请输入要查的坐标:");
		scanf("%d,%d", &x, &y);
		if (x >= 1 && x <= ROW && y >= 1 && y <= ROW) {    //输入合法
			if (arr1[x][y] == '1') {
				PrintTable(arr1);
				printf("不小心扫到雷咯!\n");
				break;
			}
			else if(arr2[x][y]!='x'){
				printf("这个坐标已经被扫过,请重新输入:");  
				continue;
			}
			else {
				int sum = CalMineNum(arr1, x, y);
				arr2[x][y] = '0' + sum;    
				PrintTable(arr2);
				time--;
			}
		}
		else {
			printf("请注意,输入不合法!\n");
		}
		if (time == NUM) {
			printf("棒!游戏结束,雷全部排完!\n");
			PrintTable(arr1);
			break;
		}
	}
	return;
}

三、收获

emmm…总的写了两个多文件形式的小项目,最大的感受就是多文件确实方便,而且代码的可读性也强了不少,如果能确定只是某个函数有问题,在这部分里找问题内解决即可,不需要牵一发而动全身,这是最大的好处。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值