C语言--扫雷游戏

目录

一、什么是扫雷游戏?

二、游戏主逻辑

三、扫雷游戏的过程实现

1. 棋盘实现

2. 初始化棋盘

3. 打印棋盘

4. 布置雷

5.排查雷

四、游戏代码


一、什么是扫雷游戏?

下面是一个网页版的9*9的扫雷游戏:

所以这个扫雷游戏其实就是在一个9*9的格子里排找出10个雷,同时每当在找了一个空格时,都会统计在这个空格的周围(这个空格的一周)有几个雷,并将数字储存在这个空格中,若没有,则什么都不显示。

下面我们来看一下使用编译器来实现的扫雷游戏:

可以看到,我们是通过键盘一步步输入坐标来排查雷的。在上述图中,雷是用字符‘1’来表示的,不是雷则是用字符‘0’来表示的。

二、游戏主逻辑

每实现一个游戏,都离不开实现游戏的逻辑:

1.打印菜单

2.输入选择是否开始游戏(可以运用switch语句)

        (1)开始游戏

        (2)退出游戏

        (3)输入的选项是否准确,考虑重新输入。

3.要考虑游戏可以反复玩(do...while循环)。

#include <stdio.h>
//menu为实现菜单打印的函数
void menu()
{
    printf("******************************\n");
    printf("**********   1.play   ********\n");
    printf("**********   0.eixt   ********\n");
    printf("******************************\n");
}
 
int main()
{
    int input = 0;
    do
    {
        //打印菜单
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            printf("玩游戏\n");
            game();//这里用来存放扫雷游戏的过程
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,请重新输入\n");
            break;
        }
 
    } while (input);
    return 0;
}

这里我们用一个game函数(自己写的函数)来实现扫雷游戏的整个逻辑。

下面就让我们来看看这个函数(game)是怎么实现的。

三、扫雷游戏的过程实现

首先,我们来仔细看扫雷游戏的规则:

        在一个9*9的棋盘中,存放有10个雷,玩家需要从键盘输入坐标来逐步排查整个9*9棋盘的每一个棋子。直到将这整个9*9的棋盘中的71个空格全排完,则玩家胜利,游戏结束;若在中途减排输入的坐标所对应格子是雷,则玩家就输了,游戏结束。当排查到的不是雷时,则需要提示排查雷的信息,即会统计出这个坐标周围一圈的雷的数目,并将该数字存放在该坐标中,同时显示这个数字。

所以这个游戏就是要先打印棋盘,然后输入坐标,再打印棋盘(已显示排查坐标周围雷的数目),后面继续重复上述操作(即:输入坐标,再打印棋盘),直到排到雷或者将空格全排完,才游戏结束。

1. 棋盘实现

知道了扫雷游戏的过程,那么要怎么实现这个棋盘呢?

首先,必须要用一个二位数组来存放雷,也要用一个二维数组来存放排查雷的信息(排查雷的信息即在这个坐标周围的雷的个数。这个数组当作棋盘,需要打印)。

但是,是要用9*9的数组吗?

这时我们就要考虑统计雷数目的操作了。

当输入的坐标再棋盘内部时:

但是当输入的坐标再棋盘的边缘时呢?

这时实际需要统计的并未满足周围8个的,但是当我们统计时却需要统计8个,这样才好便于编写,也可以保证数组不越界。既然要这样,那我们就可以使用一个(9+2)*(9+2)的二维数组了,使用这样的数组,就可以当输入再这个9*9棋盘的边缘的坐标时,也可以统计8个数据(除了再9*9棋盘内的数据,其余都表示没有雷)。

使用宏定义,便于操作:

#define ROW 9
#define COL 9

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

所以在后续操作中都应用11*11的数组(除了打印9*9数组时),那么需要的数组就为:

char mine[ROWS][COLS] = { 0 };//用来存放布置雷的信息
char show[ROWS][COLS] = { 0 };//用来存放排查雷的信息,要打印

2. 初始化棋盘

这里我们先规定:

字符:‘ 1 ’ 表示

字符:‘ 0 ’ 表示没有雷

字符:‘ * ’ 表示空格,用来隐藏雷

在没有开始排雷时,需要把9*9的棋盘打印出来。 

所以,用来存放雷的数组就可以先初始化为  ‘ 0 ’  (后面再布置雷);

而用来存放雷的信息数组就可以先初始化为  ‘ * ’   。

声明初始化数组的函数:

//声明初始化棋盘的函数
void InitBoard(char board[ROWS][COLS], int row, int col, char set);//set表示要初始化的数据

使用这样的函数就可以将两个数组分别初始化为‘0’和‘*’了;

//分别初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');

则函数实现:

void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}

3. 打印棋盘

虽然所用的数组是11*11的二维数组,但是所打印的棋盘需要以9*9的二维数组呈现出来。

所以这个函数为:

//声明打印棋盘的函数
void PrintBoard(char board[ROWS][COLS], int row, int col);

函数实现:

void PrintBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("----------扫雷----------\n");//这句代码是为了美观
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("----------扫雷----------\n");
}

4. 布置雷

在9*9棋盘中一般是只布置10个雷,但是也可以布置其他数目的雷,所以可以用宏定义表示雷数目,更加便于修改。

运用宏定义(雷的数目):

//雷的数量
#define COUNT 20

那怎样布置雷呢?下面以10个雷为例:

首先,需要产生10个不同的随机坐标,在这些坐标上布置雷。

当然,这就要用到产生随机数的方法了:

首先需要调用以下代码放在主函数中(要保证整个过程它只调用一次):

srand((unsigned int)time(NULL));

同时需要包含头文件(stdlib.h、time.h)然后才能调用rand函数参数随机值,就像这样:

int x = rand() % row + 1;
int y = rand() % col + 1;

此时的x与y坐标的范围要在1~9内,其原因如下图所示:

当然,产生的随机坐标对应的位置是否已经布置了雷,若是,则要循环重新生成,直到此产生的不同的坐标有10个,即完成操作。

所以这个函数为:

//数目布置雷的函数
void SetMine(char board[ROWS][COLS], int row, int col);

函数实现:

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = COUNT;
	while (count)
	{
		//1.产生随机的坐标1~9
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//2.在坐标中放置雷
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}	
	}
}

5.排查雷

当雷布置完了之后,就要开始排雷了。那么排雷有要怎么操作呢?

其操作如下:

a. 输入要排的坐标。

b. 判断坐标合理性(即是否在1~9范围内)。

c. 判断坐标是否被排查过(即重复排查)。

d. 判断坐标是否是雷,若是则被炸死,若不是,则需统计周围雷的个数,然后继续排雷。

e. 打印含有排查坐标中存放数字信息的棋盘。 

f. 重复a~e的操作,直到雷被排完,则游戏胜利,同时将整个棋盘雷的位置信息打印出来。

其中,统计周围雷的个数的操作也可以用一个函数来表示:

int get_mine_count(char mine[ROWS][COLS], int x, int y);

由于雷是用字符1表示的,不是雷使用字符0表示的,并且函数返回的是整型,所以可以通过排查坐标周围的8个字符相加后,再减去8个字符0的值 就是周围雷的个数。

这是通过ASCLL码值来计算的:

技巧:因为字符0的ASCLL值为48,字符1的ASCLL码值为49,字符2的ASCLL码值为50,......,所以字符1减去字符0就是整型1,字符2减去字符0就是整型2,......

故函数实现:

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

所以排查雷的整个函数就可以为:

//声明排查雷的函数
void FindMine(char board[ROWS][COLS], char show[ROWS][COLS], int row, int col);

函数实现:

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 - COUNT))
	{
		printf("请输入要排查的坐标:> ");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					PrintBoard(mine, ROW, COL);
					break;
				}
				else
				{
					int n = get_mine_count(mine, x, y);
                    //由于数组中的都是字符,所以这里需要加上字符0
					show[x][y] = n + '0';
					PrintBoard(show, ROW, COL);
					win++;
				}
			}
			else
			{
				printf("该坐标被排查过了\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}

	}
    //当 所排雷的数目 等于 棋盘数减去雷数的数目 时,游戏才结束
	if (win == (row * col - COUNT))
	{
		printf("恭喜你,排雷成功\n");
		PrintBoard(mine, ROW, COL);
	}
}

四、游戏代码

经过上面分析可以发现,扫雷游戏时一个比较多的代码项目,所以,使用分文件写,才比较方便,易懂。

game.h 用于声明游戏的函数

game.c 实现游戏过程的代码

time.c 用于测试游戏的逻辑

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 COUNT 20

//实现扫雷游戏中的函数声明
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col,char set);

//打印棋盘
void PrintBoard(char board[ROWS][COLS], int row, int col);

//布置雷
void SetMine(char board[ROWS][COLS], int row, int col);

//排查雷
void FindMine(char board[ROWS][COLS], char show[ROWS][COLS], int row, int col);

time.c

#define  _CRT_SECURE_NO_WARNINGS

#include "game.h"

void menu();
void test();
void game();

int main()
{
	//实现游戏的主逻辑
	test();
	return 0;
}
void menu()
{
	printf("**********************************\n");
	printf("**********    1.play     *********\n");
	printf("**********    0.exit     *********\n");
	printf("**********************************\n");
}
void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{	//打印菜单
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("扫雷游戏\n");
			//实现扫雷游戏的逻辑
			game();			
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
void game()
{
	//9*9棋盘
	//需要存放布置雷的信息,需要存放排查雷的信息--2个数组
	//排查时,防止坐标越界,要给数组行,列各加2
	char mine[ROWS][COLS] = { 0 };//用来存放布置雷的信息
	char show[ROWS][COLS] = { 0 };//用来存放排查雷的信息
	//1.初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//2.打印棋盘
	PrintBoard(show, ROW, COL);

	//3.布置雷
	SetMine(mine, ROW, COL);

	//4.排查雷
	FindMine(mine, show, ROW, COL);
	
}

game.c

#define  _CRT_SECURE_NO_WARNINGS

#include "game.h"

void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = set;
		}
	}
}

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

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = COUNT;
	while (count)
	{
		//1.产生随机的坐标1~9
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//2.在坐标中放置雷
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}	
	}
}

int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1]
		+ mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 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 - COUNT))
	{
		printf("请输入要排查的坐标:> ");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					PrintBoard(mine, ROW, COL);
					break;
				}
				else
				{
					int n = get_mine_count(mine, x, y);
					show[x][y] = n + '0';
					PrintBoard(show, ROW, COL);
					win++;
				}
			}
			else
			{
				printf("该坐标被排查过了\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}

	}
	if (win == (row * col - COUNT))
	{
		printf("恭喜你,排雷成功\n");
		PrintBoard(mine, ROW, COL);
	}
}

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值