用Python和C实现扫雷游戏

实现扫雷游戏的过程

扫雷游戏是一款经典的单人益智游戏,任务是通过揭开方块来避免踩雷。今天,我将向大家详细介绍如何使用Python编程语言与C编程语言来实现这个有趣的游戏。这整个游戏的核心其实就是对二维数组的遍历和使用。下面我将用代码逐步实现。

首先是游戏设计的一些思路

扫雷游戏的功能说明:

1 .  使⽤控制台实现经典的扫雷游戏。

2 .  游戏可以通过菜单实现继续玩或者退出游戏。

3 .  扫雷的棋盘是n*n的格⼦(棋盘大小可自定义)。

4 .  默认随机布置10个雷(雷的个数也可自定义)。

5 .  可以排查雷

  1. 如果位置不是雷,就显⽰周围有⼏个雷。
  2. 如果位置是雷,就炸死游戏结束。
  3. 把棋盘上的未知位置全部排查完,排雷成功,游戏结束。如果排查位置不是雷,周围也没有雷,可以展开周围的⼀⽚。并且当未揭示位置数量等于该位置周围的雷的数量时且自动标记雷的位置。

游戏界面:


如图整个游戏在控制台的实现过程。M是自动标记的雷。


游戏的分析和设计 :

        为了存储扫雷游戏中布置的雷和排查出的雷的信息,我们需要使用一种适合的数据结构来保存这些信息。考虑到游戏是在nxn的棋盘上进行的,最直观的方法就是创建一个nxn的数组来存储这些信息(为了在实现的过程中简单一些这里选用的是9*9棋盘)。这个数组将用于表示每个方块的状态,包括是否是雷区、已排查或未排查的状态。

创建一个9x9的二维数组,并使用0和1来表示是否布置雷的情况 。如图将指定位置的值设置为1表示该位置布置了雷,否则为0表示没有布置雷。通过使用这样的二维数组,这样可以方便地存储和管理扫雷游戏中每个方块是否布置了雷的状态信息。再通过遍历二维数组每个位置的值的判定来实现整个游戏。


        游戏实现的过程中需要创建两个数组,一个用来布置雷不显示给玩家  一个用来显示排查雷的信息反馈给玩家两个数组大小相同,布雷区域和显示出的区域也相同。

        假设我们排查mine数组(4,5)这个坐标时,我们访问周围的⼀圈8个⻩⾊位置,统计周围雷的个数是1 。假设我们排查mine数组(9,8)这个坐标时,我们访问周围的⼀圈8个⻩⾊位置,统计周围雷的个数时,最下⾯的三 个坐标就会越界,为了防⽌越界,我们在设计的时候,给数组扩⼤⼀圈,雷还是布置在中间的9*9的坐 标上,周围⼀圈不去布置雷就⾏,这样就完美解决了越界的问题。所以我们将存放数据的数组创建成11*11 是⽐较合适。同时再控制台打印数组的时候,我们就只打印show数组的绿色部分。

         每次排雷所操作的数组都是mine数组,再根据mine数组反馈出来的信息得出是否踩到雷,如果没有踩到雷,我们排查该坐标周围是不是有雷 ,如果有雷通过show数组给玩家反馈出雷的个数。玩家再通过这些雷的个数去判断下一次排雷的位置。如果该坐标周围八个坐标点都没有雷就展示出整片区域。

         到控制台就如下图所示。并且当未揭示位置数量等于该位置周围的雷的数量时且自动标记雷的位置。

     


 整个游戏的代码实现

 再写代码前 我着重介绍着个揭示函数,这个函数的功能对应的就是如图所展示的这种效果。

 因为写的是C语言和python 所以我将展示两种编程语言分别所对应的方法。当玩家点击某个方块时,该函数会根据方块周围的雷的数量进行不同的操作。

首先,函数检查该位置是否已经被揭示过,如果是,则返回-1。

如果该位置还未被揭示,函数会计算该位置周围的雷的数量,并将其保存在变量count中。

如果count等于0,说明该位置周围没有雷,需要继续揭示周围的方块。函数将该位置的显示矩阵show中的值设置为一个空格。

然后,函数使用一个循环遍历周围的8个方向,计算出周围方块的坐标(nx, ny)。如果坐标在合法范围内(1 <= nx <= ROW1 <= ny <= COL),则递归调用Reveal函数揭示该方块。

如果count大于0,说明该位置周围有雷,函数将该位置的显示矩阵show中的值设置为雷的数量。

C对应的方法。

//此函数用于把当被排查坐标周围8个坐标都没有雷的框展开并且把这八个坐标变成空白
int Reveal(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int row, int col)
{
	int arr[8][2] = { {-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	if (show[x][y] != '*')
	{
		return -1;
	}
	char count = GetMineCount(mine, x, y);
	if (count + '0' == '0')
	{
		if (mine[x][y] != '1')
		{
			show[x][y] = ' ';
		}
		for (int i = 0; i < sz; i++)
		{
			int nx = x + arr[i][0];
			int ny = y + arr[i][1];
			if ((1 <= nx && nx <= row) && (1 <= ny && ny <= col))
			{
				Reveal(mine, show, nx, ny, row, col);
			}
		}
	}
	else
	{
		show[x][y] = count + '0';
	}
	return 0;
}

python对应的方法。

    def Reveal(self, mine, show, x, y):
        if show[x][y] != '*':  # 该位置已经被揭示过
            return -1
        count = self.GetMineCount(mine, x, y)
        if count == 0:
            if mine[x][y] != 1:
                show[x][y] = ' '
            for d in [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)]:
                nx = x + d[0]
                ny = y + d[1]
                if 1 <= nx <= self.ROW and 1 <= ny <= self.COL:
                    self.Reveal(mine, show, nx, ny)
        else:
            show[x][y] = count

C语言代码实现

C语言代码是分文件的 主要有三个文件。

fun.h文件 主要定义常量和声明函数。

#define _CRT_SECURE_NO_WARNINGS 1

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

//定义常用的量
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10

//函数声明

//菜单函数
void menu();

//初始化棋盘函数
void InitBoard(char arr[ROWS][COLS],int rows,int cols,char c);

//棋盘打印函数
void DisplayBoard(char arr[ROWS][COLS], int row, int col);

//布置雷函数
void Setmine(char arr[ROWS][COLS], int row, int col);

//此函数用于找出你输入坐标周围的雷
char GetMineCount(char mine[ROWS][COLS], int x, int y);

//此函数用于把九个坐标都没有雷的框展开并且把它变成空白
int Reveal(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int row, int col);

// 这个函数用于判断是否输赢
int Or_Win(char show[ROWS][COLS], int row, int col);

//未揭示位置数量等于该位置周围的雷的数量时 自动标记该雷的位置
void GetMine_no_Count(int i, int j, char show[ROWS][COLS], int x);

//排查每一个坐标
void fun(char show[ROWS][COLS], int row, int col);

test.c 文件 游戏的整个逻辑大致过程。

#define _CRT_SECURE_NO_WARNINGS 1


#include"fun.h"

char mine[ROWS][COLS], show[ROWS][COLS];

//游戏函数用与实现游戏运行的一些逻辑
void game()
{
	InitBoard(mine, ROWS, COLS,'0');
	InitBoard(show, ROWS, COLS,'*');
	//棋盘打印
	DisplayBoard(show, ROW, COL);

	//布置雷
	Setmine(mine, ROW, COL);

	while (1)
	{
		int x = 0, y = 0, win = 1;
		printf("请输入你需要排查的坐标:");
		scanf("%d%d", &x, &y);
		if ((1 <= x && x <= ROW) && (1 <= y && y <= COL))
		{
			if (mine[x][y] == '1')
			{
				printf("\n很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			if (Reveal(mine, show, x, y, ROW, COL) == -1)
			{
				printf("该坐标已被排查\n");
				continue;
			}
			fun(show, COL, ROW);
			DisplayBoard(show, ROW, COL);
			if (Or_Win(show, ROW, COL))
			{
				printf("恭喜你,排雷成功");
				DisplayBoard(mine, ROW, COL);
				break;
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}


}

//主函数 程序开始的地方
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出游戏");
			break;
		case 1:
			game();
			break;
		default:
			printf("选择错误请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

fun.c文件 每个方法也就是函数的实现。

#define _CRT_SECURE_NO_WARNINGS 1
 
#include"fun.h"

//函数实现
void menu()
{
	printf("***********************\n");
	printf("****** 1. play ******\n");
	printf("****** 0. exit ******\n");
	printf("***********************\n");
}

//初始化棋盘函数
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char c)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			arr[i][j] = c;
		}
	}
}

//棋盘打印函数
void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	printf("\n----------扫雷游戏----------\n\n");
	for (int i = 0; i < row+1; i++)
		printf("%2d ", i);
	printf("\n");
	for (int i = 1; i <= row; i++)
	{
		printf("%2d ", i);
		for (int j = 1; j <= col; j++)
		{
			printf("%2c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}

//布置雷
void Setmine(char arr[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;//布置雷的数量
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % row + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count--;
		}
	}
}


//此函数用于找出你输入坐标周围的雷
char GetMineCount(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');
}

//此函数用于把当被排查坐标周围8个坐标都没有雷的框展开并且把这八个坐标变成空白
int Reveal(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int row, int col)
{
	int arr[8][2] = { {-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	if (show[x][y] != '*')
	{
		return -1;
	}
	char count = GetMineCount(mine, x, y);
	if (count + '0' == '0')
	{
		if (mine[x][y] != '1')
		{
			show[x][y] = ' ';
		}
		for (int i = 0; i < sz; i++)
		{
			int nx = x + arr[i][0];
			int ny = y + arr[i][1];
			if ((1 <= nx && nx <= row) && (1 <= ny && ny <= col))
			{
				Reveal(mine, show, nx, ny, row, col);
			}
		}
	}
	else
	{
		show[x][y] = count + '0';
	}
	return 0;
}

// 这个函数用于判断是否输赢
int Or_Win(char show[ROWS][COLS], int row, int col)
{
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
			{
				return 0;
			}
		}
	}
	return 1;
}

//未揭示位置数量等于该位置周围的雷的数量时 自动标记该雷的位置
void GetMine_no_Count(int i, int j, char show[ROWS][COLS], int x)
{
	if (show[i][j] == '*' || show[i][j] == ' ' || show[i][j] == 'M')
	{
		return;
	}

	char n = 0;
	int list1[8][2];
	memset(list1, 0, sizeof(list1));

	int directions[8][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {-1, -1}, {1, -1}, {-1, 1} };

	for (int k = 0; k < 8; ++k)
	{
		int ni = i + directions[k][0];
		int nj = j + directions[k][1];
		if (show[ni][nj] == '*' || show[ni][nj] == 'M')
		{
			list1[n][0] = ni;
			list1[n][1] = nj;
			n++;
		}
	}
	int num = atoi(&x);
	//printf("n = %d x =%c\n", n, x);
	if (num == n) 
	{
		for (int k = 0; k < n; ++k)
		{
			show[list1[k][0]][list1[k][1]] = 'M';
		}
	}
}

//排查每一个坐标
void fun(char show[ROWS][COLS], int row, int col)
{
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			GetMine_no_Count(i, j, show, show[i][j]);
		}
	}
}

python代码实现

这里采用面向对象的风格 所以代码行数相对于C语言的面向过程相对于简单一点。代码量也少很多。

import random


class Mine:
    def __init__(self):
        # 设定雷区大小:这里设定为9*9的矩阵
        self.ROW = 9
        self.COL = 9
        # 设定实际显示区域,比雷区每边都多1
        self.ROWS = self.ROW + 2
        self.COLS = self.COL + 2
        # 设定雷的数量:这里设定为10
        self.EASY_COUNT = 5
        self.main()

    # 设定菜单,方便用户选择
    def menu(self):
        print("***********************")
        print("******* 1. play *******")
        print("******* 0. exit *******")
        print("***********************")

    # 显示矩阵,就是打印出布雷地图
    def DisplayBoard(self, arr):
        print(int(self.ROWS / 2) * ' -' + '  扫雷  ' + int(self.COLS / 2) * '- ')
        a = 1
        # 打印列数
        for i in range(len(arr) - 1):
            print(i, end='  ')
        print()
        # 打印行数和每个位置的值
        for i in arr[1:-1]:
            print(a, end='  ')
            a += 1
            for j in i[1:-1]:
                print(j, end='  ')
            print()

    # 布置雷
    def Set_mine(self, mine):
        count = self.EASY_COUNT  # 布置雷的数量
        while count:
            x = random.randint(1, self.ROW)
            y = random.randint(1, self.COL)
            if mine[x][y] == 0:
                mine[x][y] = 1
                count -= 1

    # 计算周围雷的数量
    def GetMineCount(self, mine, x, 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])

    def GetMine_no_Count(self, show, i, j, x):
        if x in ['*', ' ', 'M']:
            return
        list1 = []
        n = 0
        for d in [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)]:
            ni = i + d[0]
            nj = j + d[1]
            if show[ni][nj] in ['*', 'M']:
                list1.append((ni, nj))
                n += 1
        if x == n:
            for i in list1:
                show[i[0]][i[1]] = 'M'
        return

    def fun(self, show):
        for i in range(1, len(show) - 1):
            for j in range(1, len(show) - 1):
                self.GetMine_no_Count(show, i, j, show[i][j])

    def Reveal(self, mine, show, x, y):
        if show[x][y] != '*':  # 该位置已经被揭示过
            return -1
        count = self.GetMineCount(mine, x, y)
        if count == 0:
            if mine[x][y] != 1:
                show[x][y] = ' '
            for d in [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (-1, -1), (1, -1), (-1, 1)]:
                nx = x + d[0]
                ny = y + d[1]
                if 1 <= nx <= self.ROW and 1 <= ny <= self.COL:
                    self.Reveal(mine, show, nx, ny)
        else:
            show[x][y] = count

    # 主游戏过程
    def game(self):
        mine = [[0 for _ in range(self.COLS)] for _ in range(self.ROWS)]
        show = [['*' for _ in range(self.COLS)] for _ in range(self.ROWS)]
        self.DisplayBoard(show)
        self.Set_mine(mine)
        # self.DisplayBoard(mine)
        while True:
            x, y = map(int, input("\n请输入你需要排查的坐标:").split())
            print()
            if 1 <= x <= self.ROW and 1 <= y <= self.COL:
                if mine[x][y] == 1:
                    print("很遗憾,你被炸死了")
                    self.DisplayBoard(mine)
                    break
                if self.Reveal(mine, show, x, y) == -1:
                    print('该坐标已经排除')
                    continue
                # self.DisplayBoard(mine)
                self.fun(show)
                self.DisplayBoard(show)

                if all([s != '*' for row in show[1:-1] for s in row[1:-1]]):
                    print('恭喜你,排雷成功')
                    self.DisplayBoard(mine)
                    break
            else:
                print('坐标非法,请重新输入')

    def main(self):
        input_new = 1
        while input_new:
            # 在控制台显示菜单
            self.menu()
            input_new = int(input("请输入:"))
            if input_new == 0:
                print('退出游戏')
                break
            elif input_new == 1:
                self.game()
            else:
                print('输入错误请重新输入')


if __name__ == '__main__':
    m = Mine()

以上是这个游戏实现的完整代码 ,如有缺陷请多多指教。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值