Skip to content

Commit 783c066

Browse files
committed
m
1 parent be33ed5 commit 783c066

File tree

22 files changed

+291
-0
lines changed

22 files changed

+291
-0
lines changed

yeke/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
+ [py-sun](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-sun) :用 Python 动态模拟太阳系的运转
1616
+ [py-aljx](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-aljx) :阿里注册了新公司京西,用 Python 看看网友怎么说
1717
+ [py-teacher](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-teacher) :用 Python 为老师送上节日的祝福
18+
+ [py-minesweep](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-minesweep) :用 Python 实现扫雷小游戏
1819

1920
---
2021

yeke/py-minesweep/__init__.py

Whitespace-only changes.

yeke/py-minesweep/minesweep.py

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
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()

yeke/py-minesweep/resources/0.bmp

1.45 KB
Binary file not shown.

yeke/py-minesweep/resources/1.bmp

1.74 KB
Binary file not shown.

yeke/py-minesweep/resources/2.bmp

1.74 KB
Binary file not shown.

yeke/py-minesweep/resources/3.bmp

1.74 KB
Binary file not shown.

yeke/py-minesweep/resources/4.bmp

1.74 KB
Binary file not shown.

yeke/py-minesweep/resources/5.bmp

1.74 KB
Binary file not shown.

yeke/py-minesweep/resources/6.bmp

1.74 KB
Binary file not shown.

0 commit comments

Comments
 (0)