pyhon扫雷演示与讲解

代码已更新

  • 添加了颜色表示数字
  • 画了方格线

  • yanshi

一、运行环境

我的环境:

  1. python 3.6.8
  2. pygame 1.9.6
  3. windows 10

二、游戏规则

介绍

游戏开始,你将会看到一个个整齐排列的小方块,在这些小方块中,埋藏的有若干随机分布的炸弹。每当你点击一个方块,如果不是地雷埋藏点你将会得到一个数字,该数字表示以你点击的方块为中心的九宫格内一共有的炸弹数量,或者它周围一圈的炸弹数量如图:

九宫格图
灰色部分表示未点击的方块,X表示你所得到的数字,最小为0,最大为8,表示周围这些灰色方块中藏有的炸弹数。特别的当你点击的方块在界面的边界处时,如下图红色线所在的方块中,
扫雷边界仍然是以你点击的方块为九宫格中心,只不过边界外的方块炸弹数都认为是零。当你点击的方块为零时,该方块周围的方块都会自动被打开。 如果它周围的方块也出现了一个炸弹为零的方块,该方块周围的方块也会被自动打开。如下图所示:
自动打开方块我们点击箭头所示处,数字为零的格子周围一圈的格子都会自动打开。

游戏失败

扫雷游戏中当你点击的格子是埋藏地雷的位点,游戏便失败。

游戏胜利

当你给所有埋藏地雷的点插上红旗即游戏胜利。

三、代码思路

界面

根据游戏规则,我们需要如下主要界面:

界面描述
主界面包含游戏开始退出等选项的菜单界面
游戏开始界面游戏执行界面,绘制游戏所需图形元素,接收鼠标消息
游戏胜利界面游戏胜利后执行的界面,包含两个简单的选项,重新开始或退出
游戏失败界面游戏失败后执行的界面,包含两个简单的选项,重新开始或退出

图形元素

  1. 扫雷方块,RGB(112, 128, 144);
    扫雷方块

  2. 0-9数字;
    数字1

  3. 炸弹;
    炸弹

  4. 红旗;
    红旗

  5. 主界面背景图;
    主界面背景图

游戏执行流程

流程演示

在整个流程中比较重要的部分是游戏开始,该流程中我们需要绘制扫雷区域,根据用户的行为执行各种游戏判定,包括绘制相应的图形元素,以及这些图形元素背后变量的逻辑变化等。

核心思想

GRID&GRID2数组

我们可以构建两个10x10的二元数组,GRIDGRID2 前者中以10-19 10个数字表示该点周围的炸弹数量0-9,以数字4表示炸弹。后者填充数字 0120表示该方块未被点击,1表示被点击,2表示该位置被插上了红旗。

Draw()函数

代码块中的 Draw() 函数通过读取 GRIDGRID2 中的数字在游戏开始界面上相应位置上绘制对应的图形元素。其中我们规定,只有 GRID2 上数字不为零时,我们才可以在相应位置上绘制炸弹数量和炸弹,其中若为2则表示该位置已经插上红旗,不能点击。

Start()函数

该函数表示游戏的主函数,包括接收鼠标消息判断点击位置并在 GRID&GRID2 对应的索引处更新数字。

鼠标描述
左键点击动作,将GRID2相应索引处修改值为1
右键插旗动作,将GRID2相应索引处修改值为2

那么如何根据鼠标点击的位置来判断GRID&GRID2的索引值?
我们可以假设,方块的大小为60x60 ,那么游戏开始界面中方块的横纵坐标 (x,y) 便等于数组的列索引 i ,行索引 j (ix60,jx60),相应的,当游戏界面接收到一个鼠标点击消息后,获取鼠标在界面上的点击位点 (X,Y)(X//60,Y//60) 便是数组对应位置的索引。“//”表示取整。

四、完整代码展示

#python3
#author:LEIWEI
#Date:11/11/2020
#update:12/24/2020
import pygame as pg
import random
import time

#------------------------------
#set key vars
SIZE=20
HEIGHT=30
WIDTH=30
BOMBS=15
BLOCKSIZE=60
NDraft=18
GRIDx,GRIDy=0,0
HaveLocate=[]
FLAGLOCATE=[]
USERBOMBS=0
ACTIVE=1
Bounder=[[i+2.5,i+600] for i in range(0,660,60)]

#color vars
SlateGrey=(112, 128, 144)
white=(255,255,255)
red=(255,0,0)
green=(0,255,0)
yellow=(255,255,0)
blue=(0,0,255)
black=(0,0,0)
ColorDict={"0":white,
			"1":blue,
			"2":green,
			"3":yellow,
			"4":red,
			"5":red,
			"6":red,
			"7":red,
			"8":red,}

#Image
BOMBIMAGE=pg.image.load(r"bomb.png")
FLAGIMAGE=pg.image.load(r"flag2.jpg")
BACKIMAGE=pg.image.load(r"backimage.jpg")
BOMBIMAGE=pg.transform.scale(BOMBIMAGE,(55,55))
FLAGIMAGE=pg.transform.scale(FLAGIMAGE,(55,55))
BACKIMAGE=pg.transform.scale(BACKIMAGE,(600,600))

#pygame object init
pg.init()
window=pg.display.set_mode((WIDTH*SIZE+10,HEIGHT*SIZE+10),0,32)

#font drawing the number of bomb in your click pos
N="?"

#the speed of update
normal=30
clock=pg.time.Clock()


#ground
#0->unopen
#1->open:N->the number of bomb in your click pos 
#2->flag
#3->?
#4->bomb
GRID=[[0 for x in range(10)]for y in range(10)]  #remain
GRID2=[[0 for x in range(10)]for y in range(10)] #open and not open flag 0&1&2
		

def GameInit():
	global BOMBI,GRID,HaveLocate,FLAGLOCATE,ACTIVE,GRID2,USERBOMBS,BOMBS
	HaveLocate=[]
	FLAGLOCATE=[]
	USERBOMBS=0
	ACTIVE=1
	GRID=[[0 for x in range(10)]for y in range(10)]  
	GRID2=[[0 for x in range(10)]for y in range(10)] 
	#--------------------
	#the mine pos
	BOMBI=0
	while BOMBI<BOMBS:
		bombx,bomby=random.randrange(10),random.randrange(10)
		if GRID[bomby][bombx]!=4:
			GRID[bomby][bombx]=4
			BOMBI+=1
	
	#--------------------
	#cal bomb num in around
	for y in range(10):
		for x in range(10):
			bombNums=0
			if GRID[y][x]!=4:
				x0,y0=x-1,y-1
				for i in range(3):
					for j in range(3):
						if x0+j>=0 and y0+i>=0 and x0+j<10 and y0+i<10 :
							#print("x0=%d\ty0=%d"%(x0+j,y0+i))
							if GRID[y0+i][x0+j]==4:
								bombNums+=1
				GRID[y][x]=10+bombNums
	
	
def Draw():
	global GRID2,window,pg,ACTIVE,BounderH,BounderV
	window.fill(white)
	for y in range(10):
		for x in range(10):
			
			#unopen<->
			if GRID2[y][x]==0:
				pg.draw.rect(window,SlateGrey,(x*BLOCKSIZE+5,y*BLOCKSIZE+5,BLOCKSIZE-5,BLOCKSIZE-5))
			
			#unopen - flag
			elif GRID2[y][x]==2:
				window.blit(FLAGIMAGE,(x*BLOCKSIZE+5,y*BLOCKSIZE+5,BLOCKSIZE-5,BLOCKSIZE-5))
				
			
			#open
			elif GRID2[y][x]==1:
				if GRID[y][x]>=10:
					N=str(GRID[y][x]-10)
					Nfont=pg.font.SysFont(None,75)
					Ntext=Nfont.render(N,True,ColorDict[N],white)
					pg.draw.rect(window,white,(x*BLOCKSIZE+5,y*BLOCKSIZE+5,BLOCKSIZE-5,BLOCKSIZE-5))
					window.blit(Ntext,(x*BLOCKSIZE+NDraft,y*BLOCKSIZE+5))
				elif GRID[y][x]==4:
					window.blit(BOMBIMAGE,(x*BLOCKSIZE+5,y*BLOCKSIZE+5,BLOCKSIZE-5,BLOCKSIZE-5))
	
	for H in Bounder:
		pg.draw.line(window,black,(H[0],5),(H[0],H[1]))
		pg.draw.line(window,black,(5,H[0]),(H[1],H[0]))
	pg.display.update()
	if ACTIVE==0:
		GameOver()
	pg.display.update()


#--------------------------------------
#This function will be target,while you click pos that its number 
#	>=10,if you get the number zero you will open a lager aera centre
#	you have clicked pos
def OpenAera(x,y,bombs):
	global GRID2,GRID,HaveLocate
	HaveLocate.append([x,y])
	GRID2[y][x]=1
	x0,y0=x-1,y-1
	if bombs==10:
		for i in range(3):
			for j in range(3):
				if x0+j>=0 and y0+i>=0 and x0+j<10 and y0+i<10:
					if GRID2[y0+i][x0+j]!=2:
						GRID2[y0+i][x0+j]=1
					if GRID[y0+i][x0+j]==10 and [x0+j,y0+i] not in HaveLocate:
						OpenAera(x0+j,y0+i,GRID[y0+i][x0+j])

#----------------------------------------
#This function will be target,while you click a bombs.
#	The pos of all bombs in var GRID will turn into 1 in 
#	var GRID2,and draw in Draw Func
def AllBomb():
	global GRID2,GRID
	for y in range(10):
		for x in range(10):
			if GRID[y][x]==4:
				GRID2[y][x]=1
	
def GameOver():
	global pg,font
	Point2=0
	GameOverText=["是","否"]
	pg.draw.rect(window,white,(200,200,300,200))
	restarttext=font.render("失败!!重新开始?",False,blue,white)
	window.blit(restarttext,(250,215))
	GameOverAct=[Main,exit]
	while True:
		for i in range(2):
			if Point2==i:
				text=font.render(GameOverText[i],False,blue,red)
			else:
				text=font.render(GameOverText[i],False,blue,white)
			window.blit(text,(285+(i*50),350))
		
		for event in pg.event.get():
				if event.type==12:
					pg.quit()
					exit()
				if event.type==2:
					if event.key==275:
						Point2+=1
						if Point2==2:
							Point2=0
					elif event.key==276:
						Point2-=1
						if Point2==-1:
							Point2=1
					elif event.key==13:
						GameOverAct[Point2]()
		pg.display.update()

def GameWin():
	global GRID,GRID2,USERBOMBS,BOMBS,pg,font
	if USERBOMBS==BOMBS:
		Point2=0
		GameOverText=["是","否"]
		pg.draw.rect(window,white,(200,200,300,200))
		restarttext=font.render("胜利!!重新开始?",False,blue,white)
		window.blit(restarttext,(250,215))
		GameOverAct=[Main,exit]
		
		while True:
			for i in range(2):
				if Point2==i:
					text=font.render(GameOverText[i],False,blue,red)
				else:
					text=font.render(GameOverText[i],False,blue,white)
				window.blit(text,(285+(i*50),350))
		
			for event in pg.event.get():
					if event.type==12:
						pg.quit()
						exit()
					if event.type==2:
						if event.key==275:
							Point2+=1
							if Point2==2:
								Point2=0
						elif event.key==276:
							Point2-=1
							if Point2==-1:
								Point2=1
						elif event.key==13:
							GameOverAct[Point2]()
			pg.display.update()

#----------------------------
#Game main Func
def Start():
	global GRIDx,GRIDy,GRID,GRID2,ACTIVE,FLAGLOCATE,USERBOMBS
	while True:
		Draw()
		GameWin()
		for event in pg.event.get():
			
			#get destroy message,shutdown window
			if event.type==12:
				pg.quit()
				exit()
			
			#get mouse press message
			if event.type==5:
				
				#get the pos of clicking
				clickpos=event.pos
				GRIDx,GRIDy=clickpos[0]//BLOCKSIZE,clickpos[1]//BLOCKSIZE
				
				#get left button,turn GRID2 corresponding pos into open
				if event.button==1:
					
					#bomb,you get bomb!!! bomb!!! bomb!!!
					if GRID[GRIDy][GRIDx]==4 and GRID2[GRIDy][GRIDx]!=2:
						ACTIVE=0
						AllBomb()
					
					#click flag
					elif GRID2[GRIDy][GRIDx]==2:
						pass
					
					#opened
					elif GRID2[GRIDy][GRIDx]==1:
						pass
					
					#click safe aera 
					elif GRID[GRIDy][GRIDx]>=10:
						ACTIVE=1
						OpenAera(GRIDx,GRIDy,GRID[GRIDy][GRIDx])
						
											
				#get right button
				elif event.button==3:
					if GRID[GRIDy][GRIDx]==4 and [GRIDx,GRIDy] not in FLAGLOCATE:
						USERBOMBS+=1
					FLAGLOCATE.append([GRIDx,GRIDy])
					if GRID2[GRIDy][GRIDx]==0:
						GRID2[GRIDy][GRIDx]=2
					elif GRID2[GRIDy][GRIDx]==2:
						GRID2[GRIDy][GRIDx]=0
					elif GRID2[GRIDy][GRIDx]==1:
						pass
		
		clock.tick(normal)

#---------------------------
#Restart game Func
def Main():
	GameInit()
	Start()

#---------------------------
#Game main menu Func
def Home():
	global Point
	while True:
		window.fill(white)
		window.blit(BACKIMAGE,(5,5,600,600))
		for textI in range(3):
			if Point==textI:
				text=font.render(HomeItemText[textI],True,blue,red)
			else:
				text=font.render(HomeItemText[textI],True,blue,white)
			window.blit(text,(280,400+(textI*50),25,25))
		pg.display.update()
		for event in pg.event.get():
			if event.type==12:
				pg.quit()
				exit()
			if event.type==2:
				if event.key==274:
					Point+=1
					if Point==3:
						Point=0
				elif event.key==273:
					Point-=1
					if Point==-1:
						Point=2
				elif event.key==13:
					HomeAct[Point]()


if __name__=="__main__":
	font=pg.font.SysFont("华文楷体",24,bold=True,italic=True)
	HomeItemText=["开始","关于","退出"]
	HomeAct=[Main,None,exit]
	Point=1
	textL=[]
	Home()




























五、游戏演示

主界面

Home

游戏界面

游戏开始

游戏说明

键盘操作描述
向上移动
向下移动
向左移动
向右移动
Enter确定
鼠标左键打开方块
鼠标右键插上红旗,再点击取消红旗

谢谢各位!!!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

little shark

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值