有趣的扫雷游戏,C语言编程实现

你喜欢玩游戏吗?你知道游戏里面有多少细节吗?

你好,程序员。扫雷游戏这一关你不得不过,代码练习你不得不写。

最近,我在思考《扫雷游戏》的逻辑,我知道,想法如果不付诸行动永远都是想法,通过敲代码来实现理论与实践相结合,我发现,行动是检验真理的唯一标准!

你准备好了吗?我想是时候扬帆起航、披荆斩棘了!

 

目录

一.整体框架(先思考,再动手)​编辑

二.main函数(所在.c为后缀的源文件代码)

三.game.h函数(声明各类函数)

四.game.c函数(函数的定义、游戏的具体实现)

①初始化雷的棋盘

②初始化展示界面的棋盘

③布置随机雷​编辑

④打印棋盘

⑤玩游戏​编辑

 五.源代码​编辑


一.整体框架(先思考,再动手)

 先看一下网络上扫雷游戏的界面

首先,和以往代码游戏一样,我们需要一个菜单,来供玩家选择开始游戏或者退出游戏

然后进入游戏界面,我们需要一个二维数组的棋盘

由于正式的扫雷棋盘周围是有空白轮廓的,并且编译器打印出来的棋盘可读性不高,所以我们需要两种类型的标识符常量,一种是ROW行,COL列,另一种是ROW+2行,COL+2列,打印效果如下

 如果现在我们选择了一个坐标,是不是要看他是不是雷,所以,棋盘的初始化需要两个数组,一个来存放雷,一个来展示界面,利用好存放好雷的数组传值给展示界面数组,才能实现扫雷的真正效果。

后续我们既要考虑所排查雷的坐标周围有多少个雷,又要考虑周围没雷时放空以便提高可读性,其次就是踩到雷游戏重新开始的结果了。

二.main函数(所在.c为后缀的源文件代码)

我这里分三个文件,文件的具体意义在上期三子棋超详细讲解里有介绍,链接:三子棋超详细教程

#include "game.h"

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


int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf_s("%d", &input);
		switch (input)
		{
		case 1:
			game(); 
			break;
		case 0:
			printf("退出游戏\n"); break;
		default:
			printf("选择错误,请从新选择\n"); break;
		}
	} while (input);
	return 0;
}

这个是进入游戏前所需要的代码,其中的大概内容、srand与time具体讲解也在上期:三子棋超详细教程,这里我们具体讲解与扫雷关系大的内容。

void game()
{
	char thunder[ROWS][COLS] = { 0 };   //初始化放置雷的二维数组
	char show[ROWS][COLS] = { 0 };     //初始化展示游戏界面的数组
	int ret = 1;
	Initboard_thunder(thunder, ROWS, COLS, '0');//初始化雷的棋盘为'0';
	Initboard_show(show, ROWS, COLS, '*');//游戏界面的初始化
	srandset_thunder(thunder, ROW, COL, '0', '1');//布置随机数雷
	//printboard(thunder, ROWS, COLS);//打印布置好雷的界面
	printboard(show, ROWS, COLS);//打印游戏界面
	while (1)
	{
		ret = playgame(thunder, show, ROW, COL, '0', '1');//玩游戏
		if (!ret)
		{
			printboard(thunder, ROWS, COLS);//打印布置好雷的界面
			break;
		}
		printboard(show, ROWS, COLS);//打印游戏界面
	}
}

由上至下,ROWS与COLS的定义

 这里ROW与COL是参加扫雷游戏的行和列,ROWS与COLS是游戏边框。

thunder初始化雷的棋盘全为字符‘0’的原因是,排查雷时我们如果排查到边缘,边缘为字符'0',不参与周围雷的计算,这里我们设置的是字符‘1’为雷。

show界面的初始化既要打印行和列,也要打印遮盖雷的字符'*',所以我们需要ROWS和COLS实参。

为什么要穿字符’1‘和字符’0‘呐?因为如果在函数内部直接赋值,会把代码写死,不利于深度创造。

playgame函数由于我们需要排查雷,也需要将排查坐标周围的雷统计起来赋值赋给排查的坐标展示界面上,所以我们需要传雷的数组实参和展示界面的数组实参,ret的目的是如果踩到雷了,返回0,为了好理解我把0理解为归零,即从新开始,如果没踩到,游戏继续,返回值为1,逻辑反操作后为假,游戏继续循环。

三.game.h函数(声明各类函数)

#pragma once
#include <stdio.h>         
#include <time.h>          //time
#include <stdlib.h>        //srand  与  rand
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define thunder_NUM 10      //要排查雷的个数,用于设置随机雷
//初始化雷的棋盘
void Initboard_thunder(char thunder[ROWS][COLS], int rows, int cols, char set);  
//初始化展示界面的棋盘
void Initboard_show(char show[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void printboard(char board[ROWS][COLS], int rows, int cols);
//设置随机雷的函数
void srandset_thunder(char thunder[ROWS][COLS], int row, int col, char set1, char set2);
//玩游戏的函数
int playgame(char thunder[ROWS][COLS], char show[ROWS][COLS], int row, int col, char set0, char set1);

四.game.c函数(函数的定义、游戏的具体实现)

至于函数的定义与声明概念的讲解,也在上期:三子棋超详细教程,这里不多一一介绍。

为了方便你细看代码的内容,我把代码分开并标明了注释

①初始化雷的棋盘

void Initboard_thunder(char thunder[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			thunder[i][j] = set;       //set为了不写死,传参默认传的是字符'0'
		}
	}
}

②初始化展示界面的棋盘

void Initboard_show(char show[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			show[i][j] = set;       //默认传的是字符'*'
		}
	}
}

③布置随机雷

void srandset_thunder(char thunder[ROWS][COLS], int row, int col, char set0, char set1)
{
	int num = thunder_NUM;   //在头文件内标识符常量 雷的个数 默认为10
	while (num)     
	{
		int i = rand() % row + 1;//设置随机数x y范围1~ROW(默认为9)
		int j = rand() % col + 1;//%一个数得到的数小于这一个数
		if (thunder[i][j] == set0)
		{
			thunder[i][j] = set1;
			num--;
		}
	}
}

④打印棋盘

void printboard(char board[ROWS][COLS], int rows, int cols)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < cols - 1; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i < cols - 1; i++)
	{
		printf("%d ", i);
		for (j = 1; j < cols - 1; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}

效果:

⑤玩游戏

int showthunder_num(char thunder[ROWS][COLS], int x, int y)
{
	return (thunder[x - 1][y - 1] +
		thunder[x - 1][y] +
		thunder[x - 1][y + 1] +
		thunder[x][y - 1] +
		thunder[x][y + 1] +
		thunder[x + 1][y - 1] +
		thunder[x + 1][y] +
		thunder[x + 1][y + 1]) - 8 * '0';//thunder[x-1][y-1]如果为字符'1',1的ACSII码值为49,其他的如果为字符'0',减去8个字符'0',相当于1个49减去1个48,得到的是整数1
}

void is_empty(char show[ROWS][COLS], char thunder[ROWS][COLS], int x, int y, int row, int col, char set0, char set1, int* win)
{
	int i = 0;
	int j = 0;
	for (i = x - 1; i <= x + 1; i++)  //一圈包括本身全部置位空格
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (thunder[i][j] == set0 && show[i][j] != set0 && i >= 1 && i <= row && j >= 1 && j <= col)//范围的限制是防止将空格赋值给展示棋盘的坐标显示
			{
                (*win)++;
				show[i][j] = ' ';
			}
		}
	}
    (*win)--; //减去重复的中间值
}

int playgame(char thunder[ROWS][COLS], char show[ROWS][COLS], int row, int col, char set0, char set1)
{
	int x = 0;
	int y = 0;
    static int win = 0;
	if (ROW * COL - thunder_NUM == win)
	{
		printf("恭喜你,排雷成功\n");
		return 0;
	}
	while (1)
	{
		printf("请输入要排查雷的坐标:>");
		scanf_s("%d %d", &x, &y);
                                                     //这里的判断是否为空格是为了避免排查到 
                                              的坐标,周围没有雷时后放空,导致这些坐标为空格
		if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != ' ')
		{
			if (thunder[x][y] == set1)  //为了好辨认0和1,set1默认为1,set0为0
			{
				printf("boom!你被炸死了\n");
				return 0;   //逻辑反0,为真break跳出循环重新开始新的游戏
			}
		    else
			{
                win++;//除了雷其他的点位
				int tmp = showthunder_num(thunder,x,y);//排查周围雷的个数

				show[x][y] = tmp + '0';  //由于tmp为整形0~8,所以加个字符'0'会变为字符型

				if (show[x][y] == '0')//条件:所选坐标周围没有雷,则执行放空此坐标周围棋盘函数
				{
					is_empty(show, thunder, x, y, row, col, set0, set1, &win);//需要排雷函数,需要展示函数
				}

				return 1;
			}
			
		}

		else
		{
			printf("坐标非法\n");
		}

	}

}

 五.源代码

//test.c

#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 thunder[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	int ret = 1;
	Initboard_thunder(thunder, ROWS, COLS, '0');//初始化雷的棋盘为'0';
	Initboard_show(show, ROWS, COLS, '*');//游戏界面的初始化
	srandset_thunder(thunder, ROW, COL, '0', '1');//布置随机数雷
	//printboard(thunder, ROWS, COLS);//打印布置好雷的界面
	printboard(show, ROWS, COLS);//打印游戏界面
	while (1)
	{
		ret = playgame(thunder, show, ROW, COL, '0', '1');//玩游戏
		if (!ret)
		{
			printboard(thunder, ROWS, COLS);//打印布置好雷的界面
			break;
		}
		printboard(show, ROWS, COLS);//打印游戏界面
	}
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf_s("%d", &input);
		switch (input)
		{
		case 1:
			game(); 
			break;
		case 0:
			printf("退出游戏\n"); break;
		default:
			printf("选择错误,请从新选择\n"); break;
		}
	} while (input);
	return 0;
}
//game.h

#pragma once
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <Windows.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define thunder_NUM 10
void Initboard_thunder(char thunder[ROWS][COLS], int rows, int cols, char set);
void Initboard_show(char show[ROWS][COLS], int rows, int cols, char set);
void printboard(char board[ROWS][COLS], int rows, int cols);
void srandset_thunder(char thunder[ROWS][COLS], int row, int col, char set1, char set2);
int playgame(char thunder[ROWS][COLS], char show[ROWS][COLS], int row, int col, char set0, char set1);
//game.c

#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void Initboard_thunder(char thunder[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			thunder[i][j] = set;
		}
	}
}
void Initboard_show(char show[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			show[i][j] = set;
		}
	}
}
void printboard(char board[ROWS][COLS], int rows, int cols)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < cols - 1; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i < cols - 1; i++)
	{
		printf("%d ", i);
		for (j = 1; j < cols - 1; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
}
void srandset_thunder(char thunder[ROWS][COLS], int row, int col, char set0, char set1)
{
	int num = thunder_NUM;
	while (num)
	{
		int i = rand() % row + 1;//设置随机数x y范围1~ROW(默认为9)
		int j = rand() % col + 1;
		if (thunder[i][j] == set0)
		{
			thunder[i][j] = set1;
			num--;
		}
	}
}

int showthunder_num(char thunder[ROWS][COLS], int x, int y)
{
	return (thunder[x - 1][y - 1] +
		thunder[x - 1][y] +
		thunder[x - 1][y + 1] +
		thunder[x][y - 1] +
		thunder[x][y + 1] +
		thunder[x + 1][y - 1] +
		thunder[x + 1][y] +
		thunder[x + 1][y + 1]) - 8 * '0';
}
void is_empty(char show[ROWS][COLS], char thunder[ROWS][COLS], int x, int y, int row, int col, char set0, char set1, int* win)
{
	int i = 0;
	int j = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (thunder[i][j] == set0 && show[i][j] != set0 && i >= 1 && i <= row && j >= 1 && j <= col)
			{
				(*win)++;
				show[i][j] = ' ';
			}
		}
	}
	(*win)--; //减去重复的中间值
}
int playgame(char thunder[ROWS][COLS], char show[ROWS][COLS], int row, int col, char set0, char set1)
{
	int x = 0;
	int y = 0;
	static int win = 0;
	if (ROW * COL - thunder_NUM == win)
	{
		printf("恭喜你,排雷成功\n");
		return 0;
	}
	while (1)
	{
		printf("请输入要排查雷的坐标:>");
		scanf_s("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] != ' ')
		{
			if (thunder[x][y] == set1)
			{
				printf("boom!你被炸死了\n");
				return 0;
			}
		    else
			{
				win++;//除了雷其他的点位
				int tmp = showthunder_num(thunder,x,y);
				show[x][y] = tmp + '0';
				if (show[x][y] == '0')//条件:所选坐标周围没有雷,则执行放空此坐标周围棋盘函数
				{
					is_empty(show, thunder, x, y, row, col, set0, set1, &win);
				}
				return 1;
			}
			
		}
		else
		{
			printf("坐标非法\n");
		}
	}
}

内容不多,知识自己学会了才是自己的,最好自己写一遍,手动敲代码与思考代码才能加快进步的帆船。

 若内容、代码需要补充、改正,请多多斧正、指点,分享你我他。

  • 30
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值