欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

扫雷游戏---C实现

程序员文章站 2024-03-18 12:21:58
...

引言

相信扫雷游戏对于这一代的我们来说并不陌生,小学中学阶段在没有网络的学校机房可没少玩吧?!以前,我们是以一个用户的角度来接触它的,那么,作为即将成为程序猿的我们来说,何不妨试试以开发者的角度来接触它呢?!
————————————————————————————————————————————

正题

先来分析一下扫雷程序的大体思路及框架:
1、要实现扫雷程序,需要有一个在每一局中固定的埋雷数组,表示雷的真实分布的位置
2、需要有一个为用户显示所输入的点的周围埋雷的总数
其实,只要完成以上两点并加以填充即可实现扫雷的简单程序,但是,为了使程序更加的合理,我们一般对其还有以下要求:
1、第一次排雷时不能被炸
2、每次排雷过程中都会展开一个区块的的埋雷分布

下面,我们对其进行详解,先看图
扫雷游戏---C实现
我们需要定义两个如此的二维数组,一个存储雷的真实位置,另一个存储点的周围雷区分布情况,并且,对于用户而言,游戏界面是上图中间的蓝区,因为要对边上的点统计以其为中心所在九宫格外围的八个坐标埋雷情况,所以,如此的话,对于问题的求解更加有效!

先附图,程序执行中真实的棋盘打印结果如下:
扫雷游戏---C实现

一、数组的初始化
	char mine[ROW][COL];  //负责存储埋雷坐标,进行判断或修改
	char board[ROW][COL];  //为玩家展示埋雷状况,与玩家进行互动

	//初始化两个数组
	memset(mine, '0', sizeof(mine));  //mine中初始值为'0'
	memset(board, '*', sizeof(board));  //board中初始值为'*'
二、埋雷

void SetMine(char mine[][COL], int row, int col)
{
	srand((unsigned long)time(NULL));  //种子,供rand函数生成随机值
	int count = 0;
	while (count<MINE_NUM){  //生成MINE_NUM个随机坐标
		int x = GetRandIndex(1, 10);
		int y = GetRandIndex(1, 10);
		if (mine[x][y] == '0'){  //当埋雷空间为空时,符合埋雷条件,将其置'1'
			mine[x][y] = '1';
			count++;
		}
	}
}

int GetRandIndex(int start, int end)  //获取随机值作为雷的横纵坐标【1,10】
{
	return rand() % (end - start + 1) + start;  //万能公式,任意闭区间内的随机值
}

三、判断以某点为中心所在九宫格的外围埋雷情况
char GetMines(char mine[][COL],int row,int col)
{  //获取以该坐标为中心的九宫格内的外围八个格子中埋雷的总数
	return mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + \
		mine[row][col - 1] + mine[row][col + 1] + \
		mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] \
		- 8 * '0' + '0';
}  //mine中存储的为'0' / '1',都是以字符形式存储,最后返回的结果为字符'0' - '8'中的之一

四、游戏进行时的主干程序
	while (1){
		ShowBoard(board,ROW,COL);  //先展示“游戏界面”,帮助用户输入坐标
		printf("Please inter a pos<x,y> to clear mine:\n");
		scanf("%d %d", &x, &y);  
		if (x >= 1 && x <= ROW - 2 && y >= 1 && y <= COL - 2){  //输入的点在扫雷空间之内,未越界
			

			if (mine[x][y] == '1' && count == TOTAL) {   //第一次就踩到雷后补救 			
				mine[x][y] = '0';  //补救,置为没有雷
				char num = GetMines(mine, ROW, COL);  //计算周围埋雷总数
				board[x][y] = num ;  //修改为周围埋雷数
				OpenMine(mine, board, x, y);  //展开周围未埋雷的坐标的埋雷情况
				int ret = 1;
				while (ret) {   //在其余有空的地方设置一个雷
					int x = GetRandIndex(1, 10);
					int y = GetRandIndex(1, 10);
					if (mine[x][y] == '0')	{   //找不是雷的地方布雷
						mine[x][y] = '1';
						ret--;
						break;
					}
				}
				break; 			
			}

			if (mine[x][y] == '0'){ //玩家输入的点没有埋雷
				if (board[x][y] != '*'){  //该点之前未被显示出来
					printf("Your input has been exits, Please try again...\n");
					continue;
				}
				else{ 
					char num = GetMines(mine, x, y);
					board[x][y] = num;
					OpenMine(mine, board, x, y);
					count--;
					if (count <= 20){  //排出了所有的雷--->获胜
						printf("===========================You win !===========================\n");
						break;
					}
				}
			}
			else{  //踩到了雷---->输了
				printf("===========================Sorry, You Lost! ===========================\n");
				Sleep(500);
				printf("The Mine is:\n");
				Sleep(500);
				ShowBoard(mine, ROW, COL);
				printf("\n\n");
				break;
			}
		}
		else{
			printf("Your input is out of limited. Please input again...\n");
		}
	}
五、展开某点周围的埋雷情况
void OpenMine(char mine[ROW][COL], char board[ROW][COL], int x, int y)  //展开选中坐标周围的空间附近埋雷情况
{  //有雷则不显示,没有雷将显示以该点为中心所在九宫格外围八个点的埋雷总数
	if (mine[x - 1][y - 1] == '0')
	{
		board[x - 1][y - 1] = GetMines(mine, x - 1, y - 1);  //左上
	}
	if (mine[x - 1][y] == '0')
	{
		board[x - 1][y] = GetMines(mine, x - 1, y);  //正上
	}
	if (mine[x - 1][y + 1] == '0')
	{
		board[x - 1][y + 1] = GetMines(mine, x - 1, y + 1);  //右上
	}
	if (mine[x][y - 1] == '0')
	{
		board[x][y - 1] = GetMines(mine, x, y - 1);  //正左
	}
	if (mine[x][y + 1] == '0')
	{
		board[x][y + 1] = GetMines(mine, x, y + 1);  //正右
	}
	if (mine[x + 1][y - 1] == '0')
	{
		board[x + 1][y - 1] = GetMines(mine, x + 1, y - 1);  //左下
	}
	if (mine[x + 1][y] == '0')
	{
		board[x + 1][y] = GetMines(mine, x + 1, y);  //正下
	}
	if (mine[x + 1][y + 1] == '0')
	{
		board[x + 1][y + 1] = GetMines(mine, x + 1, y + 1);  //右下
	}
}

如此,就将一个较大的程序分成一个一个子块,只要用一定的游戏逻辑将其相互衔接起来,扫雷程序就完成了,所以,务必将其间的内在逻辑联系搞清楚,就ok了

下面,游戏代码附上:
我将其以分文件形式写成的,三个文件分别为mine.h/mine.c/main.c

头文件mine.h

#ifndef _MINE_H_
#define _MINE_H_

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

#define ROW 12  //行 10+2
#define COL 12  //列 10+2
#define MINE_NUM 20  //买下雷的数目
#define TOTAL 10*10  //10*10有效的空间

#pragma  warning (disable:4996)

void Game();  

int GetRandIndex(int start, int end);

void SetMine(char mine[][COL], int row, int col);

char GetMines(char mine[][COL], int row, int col);

void ShowBoard(char board[][COL], int row, int col);

void OpenMine(char mine[ROW][COL], char board[ROW][COL], int x, int y);


#endif

主文件main.c

#include "mine.h"

void Menu()  //菜单函数, 显示导向
{
	printf("========================================\n");
	printf("=====Welcome To Mine_sweeping Games=====\n");
	printf("========================================\n");
	printf("=====1、PLAY     ======     2、EXIT=====\n");
	printf("========================================\n");
	printf("Please Select...\n");
}

int main()
{
	int select = 0;
	int quit = 0;
	while (!quit){
		Menu();
		scanf("%d", &select);
		system("cls");
		switch (select){
		case 1:  //玩游戏
			Game();
			break;
		case 2:
			printf("Bye-bye...\n");  //退出
			quit = 1;
			break;
		default:
			printf("Your select is no-exit, Please select again!\n");  //输入的选择错误
			break;
		}

	}
	system("pause");
	return 0;
}

游戏文件mine.c

#include "mine.h"

int GetRandIndex(int start, int end)  //获取随机值作为雷的横纵坐标【1,10】
{
	return rand() % (end - start + 1) + start;  //万能公式,任意闭区间内的随机值
}

void SetMine(char mine[][COL], int row, int col)
{
	srand((unsigned long)time(NULL));  //种子,供rand函数生成随机值
	int count = 0;
	while (count<MINE_NUM){  //生成MINE_NUM个随机坐标
		int x = GetRandIndex(1, 10);
		int y = GetRandIndex(1, 10);
		if (mine[x][y] == '0'){  //当埋雷空间为空时,符合埋雷条件,将其置'1'
			mine[x][y] = '1';
			count++;
		}
	}
}

char GetMines(char mine[][COL],int row,int col)
{  //获取以该坐标为中心的九宫格内的外围八个格子中埋雷的总数
	return mine[row - 1][col - 1] + mine[row - 1][col] + mine[row - 1][col + 1] + \
		mine[row][col - 1] + mine[row][col + 1] + \
		mine[row + 1][col - 1] + mine[row + 1][col] + mine[row + 1][col + 1] \
		- 8 * '0' + '0';
}  //mine中存储的为'0' / '1',都是以字符形式存储,最后返回的结果为字符'0' - '8'中的之一

void ShowBoard(char board[][COL],int row,int col)  //展示“游戏界面”
{
	printf("=============================================\n");
	printf(" \\ ");
	int i;
	for (i = 1; i <= 10; i++){
		printf("|%-3d", i);
	}
	printf(" ||\n");
	printf("----");
	for (i = 1; i <= 10; i++){
		printf("----");
	}
	printf("||\n");
	for (i = 1; i <= 10; i++){
		printf("%-3d", i);
		int j;
		for (j = 1; j <= 10; j++){
			printf("|%-3c", board[i][j]);
		}
		printf(" ||\n----");
		int k;
		for (k = 1; k <= 10; k++){
			printf("----");
		}
		printf("||\n");
	}
	printf("=============================================\n");

}

void OpenMine(char mine[ROW][COL], char board[ROW][COL], int x, int y)  //展开选中坐标周围的空间附近埋雷情况
{  //有雷则不显示,没有雷将显示以该点为中心所在九宫格外围八个点的埋雷总数
	if (mine[x - 1][y - 1] == '0')
	{
		board[x - 1][y - 1] = GetMines(mine, x - 1, y - 1);  //左上
	}
	if (mine[x - 1][y] == '0')
	{
		board[x - 1][y] = GetMines(mine, x - 1, y);  //正上
	}
	if (mine[x - 1][y + 1] == '0')
	{
		board[x - 1][y + 1] = GetMines(mine, x - 1, y + 1);  //右上
	}
	if (mine[x][y - 1] == '0')
	{
		board[x][y - 1] = GetMines(mine, x, y - 1);  //正左
	}
	if (mine[x][y + 1] == '0')
	{
		board[x][y + 1] = GetMines(mine, x, y + 1);  //正右
	}
	if (mine[x + 1][y - 1] == '0')
	{
		board[x + 1][y - 1] = GetMines(mine, x + 1, y - 1);  //左下
	}
	if (mine[x + 1][y] == '0')
	{
		board[x + 1][y] = GetMines(mine, x + 1, y);  //正下
	}
	if (mine[x + 1][y + 1] == '0')
	{
		board[x + 1][y + 1] = GetMines(mine, x + 1, y + 1);  //右下
	}
}


void Game()
{
	int count = TOTAL;  
	char mine[ROW][COL];  //负责存储埋雷坐标,进行判断或修改
	char board[ROW][COL];  //为玩家展示埋雷状况,与玩家进行互动

	//初始化两个数组
	memset(mine, '0', sizeof(mine));  //mine中初始值为'0'
	memset(board, '*', sizeof(board));  //board中初始值为'*'

	SetMine(mine, ROW, COL);  //埋雷
	int x = 0;
	int y = 0;
	while (1){
		Sleep(500);
		ShowBoard(board,ROW,COL);  //先展示“游戏界面”,帮助用户输入坐标
		Sleep(500);
		printf("Please inter a pos<x,y> to clear mine:\n");
		scanf("%d %d", &x, &y);  
		if (x >= 1 && x <= ROW - 2 && y >= 1 && y <= COL - 2){  //输入的点在扫雷空间之内,未越界
			if (mine[x][y] == '1' && count == TOTAL)  //第一次就踩到雷后补救
			{
				mine[x][y] = '0';  //补救,置为没有雷
				char num = GetMines(mine, ROW, COL);  //计算周围埋雷总数
				board[x][y] = num ;  //修改为周围埋雷数
				OpenMine(mine, board, x, y);  //展开周围未埋雷的坐标的埋雷情况
				int ret = 1;
				while (ret)  //在其余有空的地方设置一个雷
				{
					int x = GetRandIndex(1, 10);
					int y = GetRandIndex(1, 10);
					if (mine[x][y] == '0')  //找不是雷的地方布雷
					{
						mine[x][y] = '1';
						ret--;
						break;
					}
				}break;
			}

			if (mine[x][y] == '0'){ //玩家输入的点没有埋雷
				if (board[x][y] != '*'){  //该点之前未被显示出来
					printf("Your input has been exits, Please try again...\n");
					continue;
				}
				else{ 
					char num = GetMines(mine, x, y);
					board[x][y] = num;
					OpenMine(mine, board, x, y);
					count--;
					if (count <= 20){  //排出了所有的雷--->获胜
						printf("===========================You win !===========================\n");
						break;
					}
				}
			}
			else{  //踩到了雷---->输了
				printf("===========================Sorry, You Lost! ===========================\n");
				Sleep(500);
				printf("The Mine is:\n");
				Sleep(500);
				ShowBoard(mine, ROW, COL);
				printf("\n\n");
				break;
			}
		}
		else{
			printf("Your input is out of limited. Please input again...\n");
		}
	}
	Sleep(1000);
	system("cls");
}

THE END