Completed
Push — refactor/move-to-classes ( 40b0d3 )
by François
02:57
created

Game.handleEvents()   B

Complexity

Conditions 5

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 15
rs 8.5454
cc 5
1
import pygame
2
import random
3
from itertools import cycle
4
from event import Event
5
from tetrimino import Tetrimino
6
from matrix import Matrix
7
8
"""
9
Handle speed:
10
    Level, Frames/drop, Period (sec/drop), Speed (drops/sec)
11
    ---------------------
12
    0,    48, .799,  1.25
13
    1,    43, .715,  1.40
14
    2,    38, .632,  1.58
15
    3,    33, .549,  1.82
16
    4,    28, .466,  2.15
17
    5,    23, .383,  2.61
18
    6,    18, .300,  3.34
19
    7,    13, .216,  4.62
20
    8,     8, .133,  7.51
21
    9,     6, .100, 10.02
22
    10-12, 5, .083, 12.02
23
    13-15, 4, .067, 15.05
24
    16-18, 3, .050, 20.03
25
    19-28, 2, .033, 30.05
26
    29+,   1, .017, 60.10
27
28
"""
29
SPEEDS = (
30
        799,
31
        715,
32
        632,
33
        549,
34
        466,
35
        383,
36
        300,
37
        216,
38
        133,
39
        100,
40
        83,
41
        67,
42
        50,
43
        33,
44
        17)
45
46
black = (0, 0, 0)
47
white = (255, 255, 255)
48
yellow = (255, 255, 0)
49
cyan = (0, 255, 255)
50
red = (255, 0, 0)
51
blue = (0, 0, 255)
52
green = (0, 255, 0)
53
purple = (255, 0, 255)
54
orange = (255, 140, 0)
55
56
tetriminos_definitions = (
57
    {
58
        'name': 'O',
59
        'color': yellow,
60
        'blocks': ((
61
            (0, 0),
62
            (1, 0),
63
            (1, 1),
64
            (0, 1)
65
        ),)
66
    },
67
    {
68
        'name': 'I',
69
        'color': cyan,
70
        'blocks': ((
71
            (1, 1),
72
            (0, 1),
73
            (2, 1),
74
            (3, 1)
75
        ), (
76
            (1, 1),
77
            (1, 0),
78
            (1, 2),
79
            (1, 3)
80
        ))
81
    },
82
    {
83
        'name': 'J',
84
        'color': blue,
85
        'blocks': ((
86
            (0, 0),
87
            (1, 0),
88
            (2, 0),
89
            (2, 1)
90
        ), (
91
            (2, 0),
92
            (2, 1),
93
            (2, 2),
94
            (1, 2)
95
        ), (
96
            (0, 1),
97
            (0, 2),
98
            (1, 2),
99
            (2, 2)
100
        ), (
101
            (0, 0),
102
            (1, 0),
103
            (0, 1),
104
            (0, 2)
105
        ))
106
    },
107
    {
108
        'name': 'L',
109
        'color': orange,
110
        'blocks': ((
111
            (0, 2),
112
            (1, 2),
113
            (2, 2),
114
            (2, 1)
115
        ), (
116
            (0, 0),
117
            (0, 1),
118
            (0, 2),
119
            (1, 2)
120
        ), (
121
            (0, 0),
122
            (0, 1),
123
            (1, 0),
124
            (2, 0)
125
        ), (
126
            (2, 0),
127
            (2, 1),
128
            (2, 2),
129
            (1, 0)
130
        ))
131
    },
132
    {
133
        'name': 'S',
134
        'color': green,
135
        'blocks': ((
136
            (1, 0),
137
            (0, 1),
138
            (1, 1),
139
            (2, 0)
140
        ), (
141
            (1, 1),
142
            (1, 0),
143
            (2, 1),
144
            (2, 2)
145
        ))
146
    },
147
    {
148
        'name': 'T',
149
        'color': purple,
150
        'blocks': ((
151
            (0, 2),
152
            (1, 1),
153
            (1, 2),
154
            (2, 2)
155
        ), (
156
            (0, 0),
157
            (0, 1),
158
            (1, 1),
159
            (0, 2)
160
        ), (
161
            (0, 0),
162
            (1, 0),
163
            (1, 1),
164
            (2, 0)
165
        ), (
166
            (2, 0),
167
            (1, 1),
168
            (2, 1),
169
            (2, 2)
170
        ))
171
    },
172
    {
173
        'name': 'Z',
174
        'color': red,
175
        'blocks': ((
176
            (1, 0),
177
            (0, 0),
178
            (1, 1),
179
            (2, 1)
180
        ), (
181
            (1, 1),
182
            (1, 0),
183
            (0, 1),
184
            (0, 2)
185
        ))
186
    }
187
)
188
189
pygame.mixer.pre_init(44100, -16, 1, 256)
190
pygame.mixer.init()
191
# sounds
192
sounds = dict()
193
sounds["rotate"] = pygame.mixer.Sound("res/rotate.wav")
194
sounds["fall"] = pygame.mixer.Sound("res/fall.wav")
195
196
197
class Game():
198
    def __init__(self, screen, config):
199
        self.screen = screen
200
        self.config = config
201
        self.running = True
202
        self.paused = False
203
204
        # init fall
205
        self.event = Event()
206
        pygame.time.set_timer(self.event.fall, SPEEDS[0])
207
208
        self.SCREEN_WIDTH, self.SCREEN_HEIGHT = screen.get_size()
209
210
        self.Z_WIDTH, self.Z_HEIGHT = screen.get_size()
211
        self.Z_WIDTH = self.Z_HEIGHT / 2
212
213
        self.B_SIZE = self.Z_WIDTH / 10
214
215
        matrix = Matrix((self.Z_WIDTH, self.Z_HEIGHT))
216
217
        self.full_background = screen.copy()
218
        self.background = matrix.copy()
219
        matrix.background = self.background
220
        matrix.block_size = self.B_SIZE
221
222
        Z_LEFT = screen.get_width() / 2 - matrix.get_width() / 2
223
        self.Z_LEFT = Z_LEFT
224
225
        screen.blit(matrix, (Z_LEFT, 0))
226
227
        # init tetriminos
228
        self.tetrimino = Tetrimino(
229
                random.choice(tetriminos_definitions),
230
                self.B_SIZE,
231
                self.background,
232
                matrix)
233
        self.tetrimino.center(self.Z_WIDTH)
234
        self.tetrimino.draw(matrix)
235
        screen.blit(matrix, (Z_LEFT, 0))
236
237
        self.next_random = random.choice(tetriminos_definitions)
238
        self.next_tetrimino = Tetrimino(
239
                self.next_random,
240
                self.B_SIZE,
241
                self.full_background,
242
                screen)
243
        self.next_tetrimino.center(self.SCREEN_WIDTH + self.Z_WIDTH * 2,
244
                                   self.SCREEN_HEIGHT / 2)
245
        self.next_tetrimino.draw(screen)
246
247
        self.myfont = pygame.font.Font("res/VCR_OSD_MONO_1.001.ttf", 15)
248
249
        level_label = self.myfont.render("Level: {}".format(0), False, white)
250
        score_label = self.myfont.render("Score: {}".format(0), False, white)
251
        line_label = self.myfont.render("Lines: {}".format(0), False, white)
252
        screen.blit(level_label, (self.SCREEN_WIDTH - 180, 50))
253
        screen.blit(score_label, (self.SCREEN_WIDTH - 180, 75))
254
        screen.blit(line_label, (self.SCREEN_WIDTH - 180, 100))
255
256
        next_label = self.myfont.render("Next", False, white)
257
        screen.blit(next_label, (self.SCREEN_WIDTH - 120, 200))
258
259
        self.pause_font_size = self.myfont.size("PAUSED")
260
        self.pause_label = self.myfont.render("PAUSED", False, white)
261
262
        PAUSE = False
263
        self.lines = 0
264
        self.score = 0
265
        self.level = 0
266
        self.softdrops = 0
267
        self.harddrops = 0
268
269
        self.matrix = matrix
270
271
        pygame.display.update()
272
273
    def handleKey(self, key):
274
        tetrimino = self.tetrimino
275
        if key in (pygame.K_ESCAPE, pygame.K_q):
276
            self.running = False
277
        if key == pygame.K_p:
278
            if self.paused:
279
                pygame.time.set_timer(self.event.pause, 0)
280
                self.matrix = self.game_matrix
281
                self.paused = False
282
            else:
283
                pygame.time.set_timer(self.event.pause, 1000)
284
                self.paused = True
285
286
                self.game_matrix = self.matrix
287
                unpaused_matrix = self.matrix.copy()
288
                paused_matrix = self.matrix.copy()
289
290
                paused_matrix.blit(self.pause_label,
291
                                   ((self.Z_WIDTH / 2) - (self.pause_font_size[0] / 2),
292
                                    (self.Z_HEIGHT / 2) - self.pause_font_size[1]))
293
                self.pause_matrix = cycle((paused_matrix, unpaused_matrix))
294
295
                self.matrix = self.pause_matrix.next()
296
        if self.paused:
297
            # do not process any further key
298
            return
299
        if key == self.config['KEY_LEFT']:
300
            tetrimino.moveLeft()
301
            if tetrimino.isColliding():
302
                tetrimino.moveRight()
303
            else:
304
                tetrimino.clear(self.matrix)
305
                tetrimino.draw(self.matrix)
306
        if key == self.config['KEY_RIGHT']:
307
            tetrimino.moveRight()
308
            if tetrimino.isColliding():
309
                tetrimino.moveLeft()
310
            else:
311
                tetrimino.clear(self.matrix)
312
                tetrimino.draw(self.matrix)
313
        if key == self.config['KEY_DOWN']:
314
            tetrimino.moveDown()
315
            if tetrimino.isColliding():
316
                tetrimino.moveUp()
317
            else:
318
                self.softdrops += 1
319
                tetrimino.clear(self.matrix)
320
                tetrimino.draw(self.matrix)
321
        if key in self.config['KEY_ROTATE_RIGHT']:
322
            if tetrimino.rotate():
323
                sounds["rotate"].play()
324
                tetrimino.clear(self.matrix)
325
                tetrimino.draw(self.matrix)
326
        if key == self.config['KEY_HARD_DROP']:
327
            while not tetrimino.isColliding():
328
                self.harddrops += 1
329
                tetrimino.moveDown()
330
            sounds["fall"].play()
331
            tetrimino.moveUp()
332
            self.harddrops -= 1
333
            tetrimino.clear(self.matrix)
334
            tetrimino.draw(self.matrix)
335
336
    def handleFall(self):
337
        tetrimino = self.tetrimino
338
        tetrimino.moveDown()
339
        if tetrimino.isColliding():
340
            sounds["fall"].play()
341
            tetrimino.moveUp()
342
            if tetrimino.isColliding():
343
                self.running = False
344
                return
345
            self.matrix.sprites.append(tetrimino)
346
            # current from previous next
347
            tetrimino = Tetrimino(
348
                self.next_random,
349
                self.B_SIZE,
350
                self.background,
351
                self.matrix
352
            )
353
            self.tetrimino = tetrimino
354
            tetrimino.center(self.Z_WIDTH)
355
            # random next
356
            self.next_tetrimino.clear(pygame.Surface((self.SCREEN_WIDTH - self.Z_WIDTH, self.SCREEN_HEIGHT)))
357
            self.next_tetrimino.clear(self.screen)
358
            self.next_random = random.choice(tetriminos_definitions)
359
            self.next_tetrimino = Tetrimino(
360
                    self.next_random,
361
                    self.B_SIZE,
362
                    self.full_background,
363
                    self.screen)
364
            self.next_tetrimino.center(self.SCREEN_WIDTH + self.Z_WIDTH * 2,
365
                                       self.SCREEN_HEIGHT / 2)
366
            self.next_tetrimino.draw(self.screen)
367
368
            if tetrimino.isColliding():
369
                # gameover
370
                self.running = False
371
                return
372
373
            empty_lines, points = self.matrix.checkLines()
374
375
            self.score += self.softdrops
376
            self.score += (self.harddrops * 2)
377
            self.harddrops = 0
378
            self.softdrops = 0
379
380
            if empty_lines:
381
                # calculcate and display
382
                self.lines += empty_lines
383
                self.score += points * (self.level + 1)
384
                self.level = int(self.lines / 10)
385
386
                pygame.time.set_timer(self.event.fall, SPEEDS[self.level])
387
388
            level_label = self.myfont.render("Level: {}".format(self.level), False, white)
389
            score_label = self.myfont.render("Score: {}".format(self.score), False, white)
390
            line_label = self.myfont.render("Lines: {}".format(self.lines), False, white)
391
392
            # reset score zone
393
            rect = pygame.Rect(self.SCREEN_WIDTH - 180, 0, self.SCREEN_WIDTH, 200)
394
            self.screen.fill(black, rect)
395
396
            self.screen.blit(level_label, (self.SCREEN_WIDTH - 180, 50))
397
            self.screen.blit(score_label, (self.SCREEN_WIDTH - 180, 75))
398
            self.screen.blit(line_label, (self.SCREEN_WIDTH - 180, 100))
399
400
            tetrimino.draw(self.matrix)
401
        else:
402
            tetrimino.clear(self.matrix)
403
            tetrimino.draw(self.matrix)
404
405
    def handleEvents(self, event):
406
        if event.type == pygame.KEYDOWN:
407
            self.handleKey(event.key)
408
409
        if event.type == self.event.pause:
410
            self.matrix = self.pause_matrix.next()
411
412
        if self.paused:
413
            return self.running
414
415
        if event.type == self.event.fall:
416
            self.handleFall()
417
418
        self.screen.blit(self.matrix, (self.Z_LEFT, 0))
419
        return self.running
420