1
+ import os
2
+ import sys
3
+ import time
4
+ import pygame
5
+ import random
6
+
7
+ WIDTH = 400
8
+ HEIGHT = 400
9
+ NUMGRID = 8
10
+ GRIDSIZE = 36
11
+ XMARGIN = (WIDTH - GRIDSIZE * NUMGRID ) // 2
12
+ YMARGIN = (HEIGHT - GRIDSIZE * NUMGRID ) // 2
13
+ ROOTDIR = os .getcwd ()
14
+ FPS = 30
15
+
16
+ # 拼图类
17
+ class Puzzle (pygame .sprite .Sprite ):
18
+ def __init__ (self , img_path , size , position , downlen , ** kwargs ):
19
+ pygame .sprite .Sprite .__init__ (self )
20
+ self .image = pygame .image .load (img_path )
21
+ self .image = pygame .transform .smoothscale (self .image , size )
22
+ self .rect = self .image .get_rect ()
23
+ self .rect .left , self .rect .top = position
24
+ self .downlen = downlen
25
+ self .target_x = position [0 ]
26
+ self .target_y = position [1 ] + downlen
27
+ self .type = img_path .split ('/' )[- 1 ].split ('.' )[0 ]
28
+ self .fixed = False
29
+ self .speed_x = 10
30
+ self .speed_y = 10
31
+ self .direction = 'down'
32
+ # 拼图块移动
33
+ def move (self ):
34
+ if self .direction == 'down' :
35
+ self .rect .top = min (self .target_y , self .rect .top + self .speed_y )
36
+ if self .target_y == self .rect .top :
37
+ self .fixed = True
38
+ elif self .direction == 'up' :
39
+ self .rect .top = max (self .target_y , self .rect .top - self .speed_y )
40
+ if self .target_y == self .rect .top :
41
+ self .fixed = True
42
+ elif self .direction == 'left' :
43
+ self .rect .left = max (self .target_x , self .rect .left - self .speed_x )
44
+ if self .target_x == self .rect .left :
45
+ self .fixed = True
46
+ elif self .direction == 'right' :
47
+ self .rect .left = min (self .target_x , self .rect .left + self .speed_x )
48
+ if self .target_x == self .rect .left :
49
+ self .fixed = True
50
+ # 获取坐标
51
+ def getPosition (self ):
52
+ return self .rect .left , self .rect .top
53
+ # 设置坐标
54
+ def setPosition (self , position ):
55
+ self .rect .left , self .rect .top = position
56
+
57
+ # 游戏类
58
+ class Game ():
59
+ def __init__ (self , screen , font , gem_imgs , ** kwargs ):
60
+ self .screen = screen
61
+ self .font = font
62
+ self .gem_imgs = gem_imgs
63
+ self .reset ()
64
+ # 开始游戏
65
+ def start (self ):
66
+ clock = pygame .time .Clock ()
67
+ # 遍历整个游戏界面更新位置
68
+ overall_moving = True
69
+ # 指定某些对象个体更新位置
70
+ individual_moving = False
71
+ gem_selected_xy = None
72
+ gem_selected_xy2 = None
73
+ swap_again = False
74
+ add_score = 0
75
+ add_score_showtimes = 10
76
+ time_pre = int (time .time ())
77
+ # 游戏主循环
78
+ while True :
79
+ for event in pygame .event .get ():
80
+ if event .type == pygame .QUIT or (event .type == pygame .KEYUP and event .key == pygame .K_ESCAPE ):
81
+ pygame .quit ()
82
+ sys .exit ()
83
+ elif event .type == pygame .MOUSEBUTTONUP :
84
+ if (not overall_moving ) and (not individual_moving ) and (not add_score ):
85
+ position = pygame .mouse .get_pos ()
86
+ if gem_selected_xy is None :
87
+ gem_selected_xy = self .checkSelected (position )
88
+ else :
89
+ gem_selected_xy2 = self .checkSelected (position )
90
+ if gem_selected_xy2 :
91
+ if self .swapGem (gem_selected_xy , gem_selected_xy2 ):
92
+ individual_moving = True
93
+ swap_again = False
94
+ else :
95
+ gem_selected_xy = None
96
+ if overall_moving :
97
+ overall_moving = not self .dropGems (0 , 0 )
98
+ # 移动一次可能拼出多个 3 连块
99
+ if not overall_moving :
100
+ res_match = self .isMatch ()
101
+ add_score = self .removeMatched (res_match )
102
+ if add_score > 0 :
103
+ overall_moving = True
104
+ if individual_moving :
105
+ gem1 = self .getGemByPos (* gem_selected_xy )
106
+ gem2 = self .getGemByPos (* gem_selected_xy2 )
107
+ gem1 .move ()
108
+ gem2 .move ()
109
+ if gem1 .fixed and gem2 .fixed :
110
+ res_match = self .isMatch ()
111
+ if res_match [0 ] == 0 and not swap_again :
112
+ swap_again = True
113
+ self .swapGem (gem_selected_xy , gem_selected_xy2 )
114
+ else :
115
+ add_score = self .removeMatched (res_match )
116
+ overall_moving = True
117
+ individual_moving = False
118
+ gem_selected_xy = None
119
+ gem_selected_xy2 = None
120
+ self .screen .fill ((255 , 255 , 220 ))
121
+ self .drawGrids ()
122
+ self .gems_group .draw (self .screen )
123
+ if gem_selected_xy :
124
+ self .drawBlock (self .getGemByPos (* gem_selected_xy ).rect )
125
+ if add_score :
126
+ self .drawAddScore (add_score )
127
+ add_score_showtimes -= 1
128
+ if add_score_showtimes < 1 :
129
+ add_score_showtimes = 10
130
+ add_score = 0
131
+ self .remaining_time -= (int (time .time ()) - time_pre )
132
+ time_pre = int (time .time ())
133
+ self .showRemainingTime ()
134
+ self .drawScore ()
135
+ if self .remaining_time <= 0 :
136
+ return self .score
137
+ pygame .display .update ()
138
+ clock .tick (FPS )
139
+ # 初始化
140
+ def reset (self ):
141
+ # 随机生成各个块
142
+ while True :
143
+ self .all_gems = []
144
+ self .gems_group = pygame .sprite .Group ()
145
+ for x in range (NUMGRID ):
146
+ self .all_gems .append ([])
147
+ for y in range (NUMGRID ):
148
+ gem = Puzzle (img_path = random .choice (self .gem_imgs ), size = (GRIDSIZE , GRIDSIZE ), position = [XMARGIN + x * GRIDSIZE , YMARGIN + y * GRIDSIZE - NUMGRID * GRIDSIZE ], downlen = NUMGRID * GRIDSIZE )
149
+ self .all_gems [x ].append (gem )
150
+ self .gems_group .add (gem )
151
+ if self .isMatch ()[0 ] == 0 :
152
+ break
153
+ # 得分
154
+ self .score = 0
155
+ # 拼出一个的奖励
156
+ self .reward = 10
157
+ # 设置总时间
158
+ self .remaining_time = 500
159
+ # 显示剩余时间
160
+ def showRemainingTime (self ):
161
+ remaining_time_render = self .font .render ('倒计时: %ss' % str (self .remaining_time ), 1 , (85 , 65 , 0 ))
162
+ rect = remaining_time_render .get_rect ()
163
+ rect .left , rect .top = (WIDTH - 190 , 15 )
164
+ self .screen .blit (remaining_time_render , rect )
165
+ # 显示得分
166
+ def drawScore (self ):
167
+ score_render = self .font .render ('分数:' + str (self .score ), 1 , (85 , 65 , 0 ))
168
+ rect = score_render .get_rect ()
169
+ rect .left , rect .top = (55 , 15 )
170
+ self .screen .blit (score_render , rect )
171
+ # 显示加分
172
+ def drawAddScore (self , add_score ):
173
+ score_render = self .font .render ('+' + str (add_score ), 1 , (255 , 100 , 100 ))
174
+ rect = score_render .get_rect ()
175
+ rect .left , rect .top = (250 , 250 )
176
+ self .screen .blit (score_render , rect )
177
+ # 生成新的拼图块
178
+ def generateNewGems (self , res_match ):
179
+ if res_match [0 ] == 1 :
180
+ start = res_match [2 ]
181
+ while start > - 2 :
182
+ for each in [res_match [1 ], res_match [1 ]+ 1 , res_match [1 ]+ 2 ]:
183
+ gem = self .getGemByPos (* [each , start ])
184
+ if start == res_match [2 ]:
185
+ self .gems_group .remove (gem )
186
+ self .all_gems [each ][start ] = None
187
+ elif start >= 0 :
188
+ gem .target_y += GRIDSIZE
189
+ gem .fixed = False
190
+ gem .direction = 'down'
191
+ self .all_gems [each ][start + 1 ] = gem
192
+ else :
193
+ gem = Puzzle (img_path = random .choice (self .gem_imgs ), size = (GRIDSIZE , GRIDSIZE ), position = [XMARGIN + each * GRIDSIZE , YMARGIN - GRIDSIZE ], downlen = GRIDSIZE )
194
+ self .gems_group .add (gem )
195
+ self .all_gems [each ][start + 1 ] = gem
196
+ start -= 1
197
+ elif res_match [0 ] == 2 :
198
+ start = res_match [2 ]
199
+ while start > - 4 :
200
+ if start == res_match [2 ]:
201
+ for each in range (0 , 3 ):
202
+ gem = self .getGemByPos (* [res_match [1 ], start + each ])
203
+ self .gems_group .remove (gem )
204
+ self .all_gems [res_match [1 ]][start + each ] = None
205
+ elif start >= 0 :
206
+ gem = self .getGemByPos (* [res_match [1 ], start ])
207
+ gem .target_y += GRIDSIZE * 3
208
+ gem .fixed = False
209
+ gem .direction = 'down'
210
+ self .all_gems [res_match [1 ]][start + 3 ] = gem
211
+ else :
212
+ gem = Puzzle (img_path = random .choice (self .gem_imgs ), size = (GRIDSIZE , GRIDSIZE ), position = [XMARGIN + res_match [1 ]* GRIDSIZE , YMARGIN + start * GRIDSIZE ], downlen = GRIDSIZE * 3 )
213
+ self .gems_group .add (gem )
214
+ self .all_gems [res_match [1 ]][start + 3 ] = gem
215
+ start -= 1
216
+ # 移除匹配的元素
217
+ def removeMatched (self , res_match ):
218
+ if res_match [0 ] > 0 :
219
+ self .generateNewGems (res_match )
220
+ self .score += self .reward
221
+ return self .reward
222
+ return 0
223
+ # 游戏界面的网格绘制
224
+ def drawGrids (self ):
225
+ for x in range (NUMGRID ):
226
+ for y in range (NUMGRID ):
227
+ rect = pygame .Rect ((XMARGIN + x * GRIDSIZE , YMARGIN + y * GRIDSIZE , GRIDSIZE , GRIDSIZE ))
228
+ self .drawBlock (rect , color = (255 , 165 , 0 ), size = 1 )
229
+ # 画矩形 block 框
230
+ def drawBlock (self , block , color = (255 , 0 , 0 ), size = 2 ):
231
+ pygame .draw .rect (self .screen , color , block , size )
232
+ # 下落特效
233
+ def dropGems (self , x , y ):
234
+ if not self .getGemByPos (x , y ).fixed :
235
+ self .getGemByPos (x , y ).move ()
236
+ if x < NUMGRID - 1 :
237
+ x += 1
238
+ return self .dropGems (x , y )
239
+ elif y < NUMGRID - 1 :
240
+ x = 0
241
+ y += 1
242
+ return self .dropGems (x , y )
243
+ else :
244
+ return self .isFull ()
245
+ # 是否每个位置都有拼图块了
246
+ def isFull (self ):
247
+ for x in range (NUMGRID ):
248
+ for y in range (NUMGRID ):
249
+ if not self .getGemByPos (x , y ).fixed :
250
+ return False
251
+ return True
252
+ # 检查有无拼图块被选中
253
+ def checkSelected (self , position ):
254
+ for x in range (NUMGRID ):
255
+ for y in range (NUMGRID ):
256
+ if self .getGemByPos (x , y ).rect .collidepoint (* position ):
257
+ return [x , y ]
258
+ return None
259
+ # 是否有连续一样的三个块
260
+ def isMatch (self ):
261
+ for x in range (NUMGRID ):
262
+ for y in range (NUMGRID ):
263
+ if x + 2 < NUMGRID :
264
+ if self .getGemByPos (x , y ).type == self .getGemByPos (x + 1 , y ).type == self .getGemByPos (x + 2 , y ).type :
265
+ return [1 , x , y ]
266
+ if y + 2 < NUMGRID :
267
+ if self .getGemByPos (x , y ).type == self .getGemByPos (x , y + 1 ).type == self .getGemByPos (x , y + 2 ).type :
268
+ return [2 , x , y ]
269
+ return [0 , x , y ]
270
+ # 根据坐标获取对应位置的拼图对象
271
+ def getGemByPos (self , x , y ):
272
+ return self .all_gems [x ][y ]
273
+ # 交换拼图
274
+ def swapGem (self , gem1_pos , gem2_pos ):
275
+ margin = gem1_pos [0 ] - gem2_pos [0 ] + gem1_pos [1 ] - gem2_pos [1 ]
276
+ if abs (margin ) != 1 :
277
+ return False
278
+ gem1 = self .getGemByPos (* gem1_pos )
279
+ gem2 = self .getGemByPos (* gem2_pos )
280
+ if gem1_pos [0 ] - gem2_pos [0 ] == 1 :
281
+ gem1 .direction = 'left'
282
+ gem2 .direction = 'right'
283
+ elif gem1_pos [0 ] - gem2_pos [0 ] == - 1 :
284
+ gem2 .direction = 'left'
285
+ gem1 .direction = 'right'
286
+ elif gem1_pos [1 ] - gem2_pos [1 ] == 1 :
287
+ gem1 .direction = 'up'
288
+ gem2 .direction = 'down'
289
+ elif gem1_pos [1 ] - gem2_pos [1 ] == - 1 :
290
+ gem2 .direction = 'up'
291
+ gem1 .direction = 'down'
292
+ gem1 .target_x = gem2 .rect .left
293
+ gem1 .target_y = gem2 .rect .top
294
+ gem1 .fixed = False
295
+ gem2 .target_x = gem1 .rect .left
296
+ gem2 .target_y = gem1 .rect .top
297
+ gem2 .fixed = False
298
+ self .all_gems [gem2_pos [0 ]][gem2_pos [1 ]] = gem1
299
+ self .all_gems [gem1_pos [0 ]][gem1_pos [1 ]] = gem2
300
+ return True
301
+ def __repr__ (self ):
302
+ return self .info
303
+
304
+ # 初始化游戏
305
+ def gameInit ():
306
+ pygame .init ()
307
+ screen = pygame .display .set_mode ((WIDTH , HEIGHT ))
308
+ pygame .display .set_caption ('消消乐' )
309
+ # 加载字体
310
+ font = pygame .font .Font (os .path .join (ROOTDIR , 'resources/simsun.ttc' ), 25 )
311
+ # 加载图片
312
+ gem_imgs = []
313
+ for i in range (1 , 8 ):
314
+ gem_imgs .append (os .path .join (ROOTDIR , 'resources/images/gem%s.png' % i ))
315
+ game = Game (screen , font , gem_imgs )
316
+ while True :
317
+ score = game .start ()
318
+ flag = False
319
+ # 设置退出、重新开始
320
+ while True :
321
+ for event in pygame .event .get ():
322
+ if event .type == pygame .QUIT :
323
+ pygame .quit ()
324
+ sys .exit ()
325
+ if event .type == pygame .KEYUP and event .key == pygame .K_r :
326
+ flag = True
327
+ if flag :
328
+ break
329
+ screen .fill ((255 , 255 , 220 ))
330
+ text0 = '最终得分: %s' % score
331
+ text1 = '按 R 键重新开始'
332
+ y = 140
333
+ for idx , text in enumerate ([text0 , text1 ]):
334
+ text_render = font .render (text , 1 , (85 , 65 , 0 ))
335
+ rect = text_render .get_rect ()
336
+ if idx == 0 :
337
+ rect .left , rect .top = (100 , y )
338
+ elif idx == 1 :
339
+ rect .left , rect .top = (100 , y )
340
+ y += 60
341
+ screen .blit (text_render , rect )
342
+ pygame .display .update ()
343
+ game .reset ()
344
+
345
+ if __name__ == '__main__' :
346
+ gameInit ()
0 commit comments