代码已更新
- 添加了颜色表示数字
- 画了方格线
- …
一、运行环境
我的环境:
- python 3.6.8
- pygame 1.9.6
- windows 10
二、游戏规则
介绍
游戏开始,你将会看到一个个整齐排列的小方块,在这些小方块中,埋藏的有若干随机分布的炸弹。每当你点击一个方块,如果不是地雷埋藏点你将会得到一个数字,该数字表示以你点击的方块为中心的九宫格内一共有的炸弹数量,或者它周围一圈的炸弹数量如图:
灰色部分表示未点击的方块,X表示你所得到的数字,最小为0,最大为8,表示周围这些灰色方块中藏有的炸弹数。特别的当你点击的方块在界面的边界处时,如下图红色线所在的方块中,
仍然是以你点击的方块为九宫格中心,只不过边界外的方块炸弹数都认为是零。当你点击的方块为零时,该方块周围的方块都会自动被打开。 如果它周围的方块也出现了一个炸弹为零的方块,该方块周围的方块也会被自动打开。如下图所示:
我们点击箭头所示处,数字为零的格子周围一圈的格子都会自动打开。
游戏失败
扫雷游戏中当你点击的格子是埋藏地雷的位点,游戏便失败。
游戏胜利
当你给所有埋藏地雷的点插上红旗即游戏胜利。
三、代码思路
界面
根据游戏规则,我们需要如下主要界面:
界面 | 描述 |
---|---|
主界面 | 包含游戏开始退出等选项的菜单界面 |
游戏开始界面 | 游戏执行界面,绘制游戏所需图形元素,接收鼠标消息 |
游戏胜利界面 | 游戏胜利后执行的界面,包含两个简单的选项,重新开始或退出 |
游戏失败界面 | 游戏失败后执行的界面,包含两个简单的选项,重新开始或退出 |
图形元素
-
扫雷方块,RGB(112, 128, 144);
-
0-9数字;
-
炸弹;
-
红旗;
-
主界面背景图;
游戏执行流程
在整个流程中比较重要的部分是游戏开始,该流程中我们需要绘制扫雷区域,根据用户的行为执行各种游戏判定,包括绘制相应的图形元素,以及这些图形元素背后变量的逻辑变化等。
核心思想
GRID&GRID2数组
我们可以构建两个10x10的二元数组,GRID 和 GRID2 前者中以10-19 10个数字表示该点周围的炸弹数量0-9,以数字4表示炸弹。后者填充数字 0 、1和2,0表示该方块未被点击,1表示被点击,2表示该位置被插上了红旗。
Draw()函数
代码块中的 Draw() 函数通过读取 GRID 和 GRID2 中的数字在游戏开始界面上相应位置上绘制对应的图形元素。其中我们规定,只有 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()
五、游戏演示
主界面
游戏界面
游戏说明
键盘操作 | 描述 |
---|---|
↑ | 向上移动 |
↓ | 向下移动 |
← | 向左移动 |
→ | 向右移动 |
Enter | 确定 |
鼠标左键 | 打开方块 |
鼠标右键 | 插上红旗,再点击取消红旗 |