1+ import sys
2+ import time
3+ import random
4+ import pygame
5+ from pygame .locals import *
6+
7+ BLOCK_WIDTH = 30
8+ BLOCK_HEIGHT = 16
9+ # 块大小
10+ SIZE = 20
11+ # 地雷数
12+ MINE_COUNT = 66
13+ # 未点击
14+ normal = 1
15+ # 已点击
16+ opened = 2
17+ # 地雷
18+ mine = 3
19+ # 标记为地雷
20+ flag = 4
21+ # 标记为问号
22+ ask = 5
23+ # 踩中地雷
24+ bomb = 6
25+ # 被双击的周围
26+ hint = 7
27+ # 正被鼠标左右键双击
28+ double = 8
29+ readied = 1 ,
30+ started = 2 ,
31+ over = 3 ,
32+ win = 4
33+
34+ class Mine :
35+ def __init__ (self , x , y , value = 0 ):
36+ self ._x = x
37+ self ._y = y
38+ self ._value = 0
39+ self ._around_mine_count = - 1
40+ self ._status = normal
41+ self .set_value (value )
42+ def __repr__ (self ):
43+ return str (self ._value )
44+ def get_x (self ):
45+ return self ._x
46+ def set_x (self , x ):
47+ self ._x = x
48+ x = property (fget = get_x , fset = set_x )
49+ def get_y (self ):
50+ return self ._y
51+ def set_y (self , y ):
52+ self ._y = y
53+ y = property (fget = get_y , fset = set_y )
54+ def get_value (self ):
55+ return self ._value
56+ def set_value (self , value ):
57+ if value :
58+ self ._value = 1
59+ else :
60+ self ._value = 0
61+ value = property (fget = get_value , fset = set_value , doc = '0:非地雷 1:雷' )
62+ def get_around_mine_count (self ):
63+ return self ._around_mine_count
64+ def set_around_mine_count (self , around_mine_count ):
65+ self ._around_mine_count = around_mine_count
66+ around_mine_count = property (fget = get_around_mine_count , fset = set_around_mine_count , doc = '四周地雷数量' )
67+ def get_status (self ):
68+ return self ._status
69+ def set_status (self , value ):
70+ self ._status = value
71+ status = property (fget = get_status , fset = set_status , doc = 'BlockStatus' )
72+
73+ class MineBlock :
74+ def __init__ (self ):
75+ self ._block = [[Mine (i , j ) for i in range (BLOCK_WIDTH )] for j in range (BLOCK_HEIGHT )]
76+ # 埋雷
77+ for i in random .sample (range (BLOCK_WIDTH * BLOCK_HEIGHT ), MINE_COUNT ):
78+ self ._block [i // BLOCK_WIDTH ][i % BLOCK_WIDTH ].value = 1
79+ def get_block (self ):
80+ return self ._block
81+ block = property (fget = get_block )
82+ def getmine (self , x , y ):
83+ return self ._block [y ][x ]
84+ def open_mine (self , x , y ):
85+ # 踩到雷了
86+ if self ._block [y ][x ].value :
87+ self ._block [y ][x ].status = bomb
88+ return False
89+ # 先把状态改为 opened
90+ self ._block [y ][x ].status = opened
91+ around = _get_around (x , y )
92+ _sum = 0
93+ for i , j in around :
94+ if self ._block [j ][i ].value :
95+ _sum += 1
96+ self ._block [y ][x ].around_mine_count = _sum
97+ # 如果周围没有雷,那么将周围 8 个未中未点开的递归算一遍
98+ if _sum == 0 :
99+ for i , j in around :
100+ if self ._block [j ][i ].around_mine_count == - 1 :
101+ self .open_mine (i , j )
102+ return True
103+ def double_mouse_button_down (self , x , y ):
104+ if self ._block [y ][x ].around_mine_count == 0 :
105+ return True
106+ self ._block [y ][x ].status = double
107+ around = _get_around (x , y )
108+ # 周围被标记的雷数量
109+ sumflag = 0
110+ for i , j in _get_around (x , y ):
111+ if self ._block [j ][i ].status == flag :
112+ sumflag += 1
113+ # 周边的雷已经全部被标记
114+ result = True
115+ if sumflag == self ._block [y ][x ].around_mine_count :
116+ for i , j in around :
117+ if self ._block [j ][i ].status == normal :
118+ if not self .open_mine (i , j ):
119+ result = False
120+ else :
121+ for i , j in around :
122+ if self ._block [j ][i ].status == normal :
123+ self ._block [j ][i ].status = hint
124+ return result
125+ def double_mouse_button_up (self , x , y ):
126+ self ._block [y ][x ].status = opened
127+ for i , j in _get_around (x , y ):
128+ if self ._block [j ][i ].status == hint :
129+ self ._block [j ][i ].status = normal
130+
131+ # 返回 (x, y) 周围的点坐标
132+ def _get_around (x , y ):
133+ return [(i , j ) for i in range (max (0 , x - 1 ), min (BLOCK_WIDTH - 1 , x + 1 ) + 1 )
134+ for j in range (max (0 , y - 1 ), min (BLOCK_HEIGHT - 1 , y + 1 ) + 1 ) if i != x or j != y ]
135+
136+ # 游戏屏幕的宽
137+ SCREEN_WIDTH = BLOCK_WIDTH * SIZE
138+ # 游戏屏幕的高
139+ SCREEN_HEIGHT = (BLOCK_HEIGHT + 2 ) * SIZE
140+
141+ def print_text (screen , font , x , y , text , fcolor = (255 , 255 , 255 )):
142+ imgText = font .render (text , True , fcolor )
143+ screen .blit (imgText , (x , y ))
144+
145+ def main ():
146+ pygame .init ()
147+ screen = pygame .display .set_mode ((SCREEN_WIDTH , SCREEN_HEIGHT ))
148+ pygame .display .set_caption ('扫雷' )
149+ # 得分的字体
150+ font1 = pygame .font .Font ('resources/a.TTF' , SIZE * 2 )
151+ fwidth , fheight = font1 .size ('999' )
152+ red = (200 , 40 , 40 )
153+
154+ # 加载资源图片,因为资源文件大小不一,所以做了统一的缩放处理
155+ img0 = pygame .image .load ('resources/0.bmp' ).convert ()
156+ img0 = pygame .transform .smoothscale (img0 , (SIZE , SIZE ))
157+ img1 = pygame .image .load ('resources/1.bmp' ).convert ()
158+ img1 = pygame .transform .smoothscale (img1 , (SIZE , SIZE ))
159+ img2 = pygame .image .load ('resources/2.bmp' ).convert ()
160+ img2 = pygame .transform .smoothscale (img2 , (SIZE , SIZE ))
161+ img3 = pygame .image .load ('resources/3.bmp' ).convert ()
162+ img3 = pygame .transform .smoothscale (img3 , (SIZE , SIZE ))
163+ img4 = pygame .image .load ('resources/4.bmp' ).convert ()
164+ img4 = pygame .transform .smoothscale (img4 , (SIZE , SIZE ))
165+ img5 = pygame .image .load ('resources/5.bmp' ).convert ()
166+ img5 = pygame .transform .smoothscale (img5 , (SIZE , SIZE ))
167+ img6 = pygame .image .load ('resources/6.bmp' ).convert ()
168+ img6 = pygame .transform .smoothscale (img6 , (SIZE , SIZE ))
169+ img7 = pygame .image .load ('resources/7.bmp' ).convert ()
170+ img7 = pygame .transform .smoothscale (img7 , (SIZE , SIZE ))
171+ img8 = pygame .image .load ('resources/8.bmp' ).convert ()
172+ img8 = pygame .transform .smoothscale (img8 , (SIZE , SIZE ))
173+ img_blank = pygame .image .load ('resources/blank.bmp' ).convert ()
174+ img_blank = pygame .transform .smoothscale (img_blank , (SIZE , SIZE ))
175+ img_flag = pygame .image .load ('resources/flag.bmp' ).convert ()
176+ img_flag = pygame .transform .smoothscale (img_flag , (SIZE , SIZE ))
177+ img_ask = pygame .image .load ('resources/ask.bmp' ).convert ()
178+ img_ask = pygame .transform .smoothscale (img_ask , (SIZE , SIZE ))
179+ img_mine = pygame .image .load ('resources/mine.bmp' ).convert ()
180+ img_mine = pygame .transform .smoothscale (img_mine , (SIZE , SIZE ))
181+ img_blood = pygame .image .load ('resources/blood.bmp' ).convert ()
182+ img_blood = pygame .transform .smoothscale (img_blood , (SIZE , SIZE ))
183+ img_error = pygame .image .load ('resources/error.bmp' ).convert ()
184+ img_error = pygame .transform .smoothscale (img_error , (SIZE , SIZE ))
185+ face_size = int (SIZE * 1.25 )
186+ img_face_fail = pygame .image .load ('resources/face_fail.bmp' ).convert ()
187+ img_face_fail = pygame .transform .smoothscale (img_face_fail , (face_size , face_size ))
188+ img_face_normal = pygame .image .load ('resources/face_normal.bmp' ).convert ()
189+ img_face_normal = pygame .transform .smoothscale (img_face_normal , (face_size , face_size ))
190+ img_face_success = pygame .image .load ('resources/face_success.bmp' ).convert ()
191+ img_face_success = pygame .transform .smoothscale (img_face_success , (face_size , face_size ))
192+ face_pos_x = (SCREEN_WIDTH - face_size ) // 2
193+ face_pos_y = (SIZE * 2 - face_size ) // 2
194+ img_dict = {0 : img0 , 1 : img1 , 2 : img2 , 3 : img3 , 4 : img4 , 5 : img5 , 6 : img6 , 7 : img7 , 8 : img8 }
195+ bgcolor = (225 , 225 , 225 )
196+ block = MineBlock ()
197+ game_status = readied
198+ # 开始时间
199+ start_time = None
200+ # 耗时
201+ elapsed_time = 0
202+ while True :
203+ screen .fill (bgcolor )
204+ for event in pygame .event .get ():
205+ if event .type == QUIT :
206+ sys .exit ()
207+ elif event .type == MOUSEBUTTONDOWN :
208+ mouse_x , mouse_y = event .pos
209+ x = mouse_x // SIZE
210+ y = mouse_y // SIZE - 2
211+ b1 , b2 , b3 = pygame .mouse .get_pressed ()
212+ if game_status == started :
213+ # 鼠标左右键同时按下,如果已经标记了所有雷,则打开周围一圈;如果还未标记完所有雷,则有一个周围一圈被同时按下的效果
214+ if b1 and b3 :
215+ mine = block .getmine (x , y )
216+ if mine .status == opened :
217+ if not block .double_mouse_button_down (x , y ):
218+ game_status = over
219+ elif event .type == MOUSEBUTTONUP :
220+ if y < 0 :
221+ if face_pos_x <= mouse_x <= face_pos_x + face_size \
222+ and face_pos_y <= mouse_y <= face_pos_y + face_size :
223+ game_status = readied
224+ block = MineBlock ()
225+ start_time = time .time ()
226+ elapsed_time = 0
227+ continue
228+ if game_status == readied :
229+ game_status = started
230+ start_time = time .time ()
231+ elapsed_time = 0
232+ if game_status == started :
233+ mine = block .getmine (x , y )
234+ # 按鼠标左键
235+ if b1 and not b3 :
236+ if mine .status == normal :
237+ if not block .open_mine (x , y ):
238+ game_status = over
239+ # 按鼠标右键
240+ elif not b1 and b3 :
241+ if mine .status == normal :
242+ mine .status = flag
243+ elif mine .status == flag :
244+ mine .status = ask
245+ elif mine .status == ask :
246+ mine .status = normal
247+ elif b1 and b3 :
248+ if mine .status == double :
249+ block .double_mouse_button_up (x , y )
250+ flag_count = 0
251+ opened_count = 0
252+ for row in block .block :
253+ for mine in row :
254+ pos = (mine .x * SIZE , (mine .y + 2 ) * SIZE )
255+ if mine .status == opened :
256+ screen .blit (img_dict [mine .around_mine_count ], pos )
257+ opened_count += 1
258+ elif mine .status == double :
259+ screen .blit (img_dict [mine .around_mine_count ], pos )
260+ elif mine .status == bomb :
261+ screen .blit (img_blood , pos )
262+ elif mine .status == flag :
263+ screen .blit (img_flag , pos )
264+ flag_count += 1
265+ elif mine .status == ask :
266+ screen .blit (img_ask , pos )
267+ elif mine .status == hint :
268+ screen .blit (img0 , pos )
269+ elif game_status == over and mine .value :
270+ screen .blit (img_mine , pos )
271+ elif mine .value == 0 and mine .status == flag :
272+ screen .blit (img_error , pos )
273+ elif mine .status == normal :
274+ screen .blit (img_blank , pos )
275+ print_text (screen , font1 , 30 , (SIZE * 2 - fheight ) // 2 - 2 , '%02d' % (MINE_COUNT - flag_count ), red )
276+ if game_status == started :
277+ elapsed_time = int (time .time () - start_time )
278+ print_text (screen , font1 , SCREEN_WIDTH - fwidth - 30 , (SIZE * 2 - fheight ) // 2 - 2 , '%03d' % elapsed_time , red )
279+ if flag_count + opened_count == BLOCK_WIDTH * BLOCK_HEIGHT :
280+ game_status = win
281+ if game_status == over :
282+ screen .blit (img_face_fail , (face_pos_x , face_pos_y ))
283+ elif game_status == win :
284+ screen .blit (img_face_success , (face_pos_x , face_pos_y ))
285+ else :
286+ screen .blit (img_face_normal , (face_pos_x , face_pos_y ))
287+ pygame .display .update ()
288+
289+ if __name__ == '__main__' :
290+ main ()
0 commit comments