扫雷(简化版)介绍

目录

一、扫雷游戏分析

1.1棋盘大小设置

1.2棋盘初始化

1.2.1棋盘打印

1.3雷的布置

1.4雷的排查

1.4.1字符—整型之间的切换

1.4.2排雷次数判定

1.4.3排雷函数实现

二、操作界面的实现

2.1界面设置

2.2选择操作

2.3添加清屏功能

PS:使用 头文件中的 time() 函数即可得到当前的时间,利用时间作为参数,每次播种的时间不同,那么生成的种子就不同,最终的随机数也就不同。


相信阅读本篇文章的你或多或少的接触过一款名为“扫雷”的游戏,在一定区域的矩阵内布置雷,通过标点,排查雷的个数。那么如何用c语言制作属于自己的一款“扫雷(简化版)”游戏呢?

一、扫雷游戏分析

1.1棋盘大小设置

定义一个9*9的棋盘,将棋盘的行,列分别定义为全局变量——方便后面的引用和棋盘大小的更改。

在这里还定义了ROWS和COLS,至于为什么,在后文会有解释。

#define ROW 9
#define COL 9

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

1.2棋盘初始化

开始定义两个大小由ROWS,COLS决定的二维字符型数组,一个用于存储雷命名为mine(真正的棋盘),用'1'表示雷,'0'则表示安全区(无雷);而另外一个数组用于掩盖整个棋盘命名为show(展示的棋盘),让玩家进行猜雷,用'*'来表现它的神秘感。

通过initboard函数,利用两个for循环嵌套来用传入的字符填满整个数组,代码如下:

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

1.2.1棋盘打印

定义一个displayboard函数来实现棋盘打印功能,代码如下:

void displayboard(char board[ROWS][COLS], int row, int col) {
	int i=0;
	printf("----------扫雷游戏------------\n");
	for (i = 1; i <=row; i++) {
		int j = 0;
		for (j = 1; j <= col; j++) {
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("----------扫雷游戏------------\n");
}

棋盘展示:

观察棋盘发现只有字符,当要排雷时,坐标还得一行一列一个个数,所以对上面代码进行修正,对棋盘的上方和左侧分别添加行列数,方便找到排查点坐标,进行排雷,修正后代码如下:

void displayboard(char board[ROWS][COLS], int row, int col) {
	int i=0;
	printf("----------扫雷游戏------------\n");
	for (i = 0; i <=col; i++) {
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <=row; i++) {
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++) {
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("----------扫雷游戏------------\n");
}

修正后棋盘:

1.3雷的布置

雷的布置应该是每次不一样的,就是说是随机的,那么如何在棋盘中随机布雷呢?

在上一步打印棋盘时,为了方便排查雷,给棋盘外围添加了行,列数;那么可以通过坐标来定位某个位置来进行布雷,而c语言中为我们提供了一个rand的函数用来产生随机数,只需引入〈stdlib.h〉的头文件,下面先进行试验看看rand产生的是否是随机数,代码如下:

#include <stdio.h>
#include <stdlib.h>
int main()
{
 printf("%d\n", rand());
 printf("%d\n", rand());
 printf("%d\n", rand());
 printf("%d\n", rand());
 printf("%d\n", rand());
 return 0;
}

第一次输出结果:

第二次输出结果:

观察结果发现rand函数单独一次输出结果不同,但两次分别输出的结果相同,知晓rand生产的并不是真正意义上的“随机数”,而是“伪随机数”,是根据一个数值按照某个公式推算出来的,这个数值我们称之为“种子”。种子在每次启动计算机时是随机的,但是一旦计算机启动以后它就不再变化了,一旦启动计算机以后,种子就是定值了,所以根据公式推算出来的结果(也就是生成的随机数)就是固定的。那如何才能产生真正的“随机数”呢?同样在〈stdlib.h〉头文件中有个srand函数,使用时还需要引如〈time.h〉,用法如下:

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

srand((unsigned int)time(NULL));//刷新“种子”

int x = rand() % row + 1;//范围0-row
int y = rand() % col + 1;//范围0-col

在文章的末尾会讲解srand产生的是真正的“随机数”的原因。

知道“随机数”的产生,那如何落实到具体位置呢?在这里定义雷的横坐标为x,纵坐标为y,通过rand(rand生成范围是0—(num-1))所以要在棋盘范围布雷,则需要+1;将生成随机数赋予x,y,这样就产生了雷坐标,在将雷坐标给mine棋盘,讲该位置的'0'—>'1',一个雷就此诞生啦!

如果需要布置多个雷,定义一个set_mine函数,同时定义个全局变量set_count来设置雷数目,可利用while/for循环,每循环一次,count--,当count==0,条件为假跳出循环同时意味着布雷完毕。代码实现如下:

#define set_count 10//设置雷数
void set_mine(char board[ROWS][COLS], int row, int col) {
	int count = set_count;
	while (count) {
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0') {
			board[x][y] = '1';
			count--;
		}
	}
}

1.4雷的排查

再次玩扫雷,发现当点击非雷区时会出现大片空白,以及某些方块是会有数字,这些数字代表着在这方块周围一圈内雷的个数,这为我们“扫雷”的实现提供关键的一块“拼图”。所以当我们排查某个坐标时,如果是安全区,则统计在这排查坐标周围一圈的雷数,并把改数在show棋盘展示。当排查坐标[x][y]为安全区时,对排查坐标周围一圈雷数统计,即[x - 1][y - 1] ,[x][y-1] ,[x+1][y + 1] ,[x+1][y] ,[x+1][y + 1] ,[x][y+1] ,[x-1][y+1] ,[x-1][y] 这八个区域统计雷数。那么当棋盘为9*9时,统计边缘位置周围的雷数会出现错误,输出?或' ',这是因为棋盘周边没开辟空间,统计雷数时无法识别,会报乱码。所以在开头定义棋盘大小时讲横纵都拓宽了两行或两列,这是为了方便统计雷数 。

那么棋盘是个二维字符型数字,该如何统计周围的雷数(整型)呢?

1.4.1字符—整型之间的切换

字符型变量在计算机中存储用的是Ascll码(二进制编码),而'0'的Ascll码是48;当其他字符型数字减去'0',相当于对应的Ascll码-48,等于该数。举例:'1'(Ascll码为48)-'0'=1。

既然字符可以转换数字,那么数字也应该可以转换字符,只需要反向操作,+'0'即可。

而完成统计雷数可以写成一个函数命名为getmine,代码如下:

int  GetMinecount(char mine[ROWS][COLS], int x, int y) {
	return (mine[x - 1][y - 1] + mine[x][y-1] + mine[x+1][y + 1] + mine[x+1][y] + mine[x+1][y + 1] + mine[x][y+1] + mine[x-1][y+1] + mine[x-1][y] - 8 * '0');
}

1.4.2排雷次数判定

排雷需要一定的次数限定,毕竟棋盘大小已经规定,不可能无限制的排下去。那么排雷次数<棋盘大小-雷数;同时当排雷次数==棋盘大小-雷数时,是胜利条件哎(扫雷成功)。

1.4.3排雷函数实现

通过嵌套调用getmine函数来获取周围雷数,打印到show棋盘展示,让玩家刚好的排雷,结合上面分析,就能够构建一个排雷函数Findmine,代码如下:

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 - set_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);
				system("cls");
				displayboard(show, ROW, COL);
				win++;
			}
		}
		else {
			printf("非法输入,重新输入\n");
		}
	}
	if (win == row * col - set_count) {
		printf("恭喜你,扫雷成功!!!!\n");
		displayboard(mine, ROW, COL);
	}
}

二、操作界面的实现

2.1界面设置

设置一个menu函数,简述输入不同的数字进行的下一步操作,代码如下:

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

}

2.2选择操作

利用switch语句,通过输入不同的数字,进行不同的操作,同时将扫雷操作各个函数放置到game函数中,代码如下:

int input=0;
scanf("%d", &input);
		switch (input) {
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入:\n");
			system("cls");
			break;
		}
	} while (input);

2.3添加清屏功能

发现当进入游戏模式后,在运行窗口每次排雷都会留下记录,看起来不美观,在这里用到〈window.h〉的头文件,其中的system(“cls”)能够实现清屏功能,添加带适当位置能够让游戏体验加倍(ಡωಡ) 。

下面是整个程序全部代码:

game.h:

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

#define ROW 9
#define COL 9

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

#define set_count 10//设置雷数

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char m);
//打印棋盘
void displayboard(char board[ROWS][COLS], int row, int col);
//布置雷
void set_mine(char board[ROWS][COLS], int row, int col);
//排差雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col);

game.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void InitBoard(char board[ROWS][COLS], int rows, int cols,char m) {
	int i = 0;
	for (i = 0; i <rows; i++) {
		int j = 0;
		for (j = 0; j <cols; j++) {
			board[i][j] = m;
		}
	}
}
void displayboard(char board[ROWS][COLS], int row, int col) {
	int i=0;
	printf("----------扫雷游戏------------\n");
	for (i = 0; i <=col; i++) {
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <=row; i++) {
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++) {
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("----------扫雷游戏------------\n");
}
void set_mine(char board[ROWS][COLS], int row, int col) {
	int count = set_count;
	while (count) {
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0') {
			board[x][y] = '1';
			count--;
		}
	}
}
//找周围雷数(3*3)不含找点
int  GetMinecount(char mine[ROWS][COLS], int x, int y) {
	return (mine[x - 1][y - 1] + mine[x][y-1] + mine[x+1][y + 1] + mine[x+1][y] + mine[x+1][y + 1] + mine[x][y+1] + mine[x-1][y+1] + mine[x-1][y] - 8 * '0');
}
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 - set_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);
				system("cls");
				displayboard(show, ROW, COL);
				win++;
			}
		}
		else {
			printf("非法输入,重新输入\n");
		}
	}
	if (win == row * col - set_count) {
		printf("恭喜你,扫雷成功!!!!\n");
		displayboard(mine, ROW, COL);
	}
}

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];
	char show[ROWS][COLS];
	InitBoard(mine, ROWS,COLS,'0');
	InitBoard(show, ROWS, COLS,'*');
	set_mine(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("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入:\n");
			system("cls");
			break;
		}
	} while (input);
	return 0;
}

PS:使用 <time.h> 头文件中的 time() 函数即可得到当前的时间,利用时间作为参数,每次播种的时间不同,那么生成的种子就不同,最终的随机数也就不同。

若有不足之处,望各位大佬多多指教。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值