Completed
Push — refactor/move-to-classes ( 920bdd...51e28a )
by François
01:40
created

Game.keypress()   C

Complexity

Conditions 8

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 17
rs 6.6666
cc 8
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
        pause_font_size = self.myfont.size("PAUSED")
253
        self.pause_position = ((self.Z_WIDTH / 2) - (pause_font_size[0] / 2),
254
                               (self.Z_HEIGHT / 2) - pause_font_size[1])
255
        self.pause_label = self.myfont.render("PAUSED", False, white)
256
        screen.blit(level_label, (self.SCREEN_WIDTH - 180, 50))
257
        screen.blit(score_label, (self.SCREEN_WIDTH - 180, 75))
258
        screen.blit(line_label, (self.SCREEN_WIDTH - 180, 100))
259
260
        next_label = self.myfont.render("Next", False, white)
261
        screen.blit(next_label, (self.SCREEN_WIDTH - 120, 200))
262
263
        PAUSE = False
264
        self.lines = 0
265
        self.score = 0
266
        self.level = 0
267
        self.softdrops = 0
268
        self.harddrops = 0
269
270
        self.matrix = matrix
271
272
        pygame.display.update()
273
274
    def pause(self):
275
        if self.paused:
276
            pygame.time.set_timer(self.event.pause, 0)
277
            self.matrix = self.game_matrix
278
            self.paused = False
279
        else:
280
            pygame.time.set_timer(self.event.pause, 1000)
281
            self.paused = True
282
283
            self.game_matrix = self.matrix
284
            unpaused_matrix = self.matrix.copy()
285
            paused_matrix = self.matrix.copy()
286
287
            paused_matrix.blit(self.pause_label, self.pause_position)
288
            self.pause_matrix = cycle((paused_matrix, unpaused_matrix))
289
290
            self.matrix = self.pause_matrix.next()
291
292
    def keypress(self, key):
293
        tetrimino = self.tetrimino
294
        if key == self.config['KEY_LEFT']:
295
            tetrimino.moveLeft()
296
        if key == self.config['KEY_RIGHT']:
297
            tetrimino.moveRight()
298
        if key == self.config['KEY_DOWN']:
299
            moved = tetrimino.moveDown()
300
            if moved:
301
                self.softdrops += 1
302
        if key in self.config['KEY_ROTATE_RIGHT']:
303
            if tetrimino.rotate():
304
                sounds["rotate"].play()
305
                tetrimino.clear(self.matrix)
306
                tetrimino.draw(self.matrix)
307
        if key == self.config['KEY_HARD_DROP']:
308
            self.harddrops += tetrimino.harddrop()
309
310
    def handleKey(self, key):
311
        if key in (pygame.K_ESCAPE, pygame.K_q):
312
            self.running = False
313
        if key == pygame.K_p:
314
            self.pause()
315
        if self.paused:
316
            # do not process any further key
317
            return
318
        elif key in self.config['P_KEYS']:
319
            self.keypress(key)
320
321
    def handleFall(self):
322
        tetrimino = self.tetrimino
323
        tetrimino.moveDown()
324
        if tetrimino.isLocked:
325
            sounds["fall"].play()
326
            self.matrix.sprites.append(tetrimino)
327
            # current from previous next
328
            tetrimino = Tetrimino(
329
                self.next_random,
330
                self.B_SIZE,
331
                self.background,
332
                self.matrix
333
            )
334
            self.tetrimino = tetrimino
335
            tetrimino.center(self.Z_WIDTH)
336
            # random next
337
            self.next_tetrimino.clear(pygame.Surface((self.SCREEN_WIDTH - self.Z_WIDTH, self.SCREEN_HEIGHT)))
338
            self.next_tetrimino.clear(self.screen)
339
            self.next_random = random.choice(tetriminos_definitions)
340
            self.next_tetrimino = Tetrimino(
341
                    self.next_random,
342
                    self.B_SIZE,
343
                    self.full_background,
344
                    self.screen)
345
            self.next_tetrimino.center(self.SCREEN_WIDTH + self.Z_WIDTH * 2,
346
                                       self.SCREEN_HEIGHT / 2)
347
            self.next_tetrimino.draw(self.screen)
348
349
            if tetrimino.isColliding():
350
                # gameover
351
                self.running = False
352
                return
353
354
            empty_lines, points = self.matrix.checkLines()
355
356
            self.score += self.softdrops
357
            self.score += (self.harddrops * 2)
358
            self.harddrops = 0
359
            self.softdrops = 0
360
361
            if empty_lines:
362
                # calculcate and display
363
                self.lines += empty_lines
364
                self.score += points * (self.level + 1)
365
                self.level = int(self.lines / 10)
366
367
                pygame.time.set_timer(self.event.fall, SPEEDS[self.level])
368
369
            level_label = self.myfont.render("Level: {}".format(self.level), False, white)
370
            score_label = self.myfont.render("Score: {}".format(self.score), False, white)
371
            line_label = self.myfont.render("Lines: {}".format(self.lines), False, white)
372
373
            # reset score zone
374
            rect = pygame.Rect(self.SCREEN_WIDTH - 180, 0, self.SCREEN_WIDTH, 200)
375
            self.screen.fill(black, rect)
376
377
            self.screen.blit(level_label, (self.SCREEN_WIDTH - 180, 50))
378
            self.screen.blit(score_label, (self.SCREEN_WIDTH - 180, 75))
379
            self.screen.blit(line_label, (self.SCREEN_WIDTH - 180, 100))
380
381
            tetrimino.draw(self.matrix)
382
        else:
383
            tetrimino.clear(self.matrix)
384
            tetrimino.draw(self.matrix)
385
386
    def handleEvents(self, event):
387
        if event.type == pygame.KEYDOWN:
388
            self.handleKey(event.key)
389
390
        if event.type == self.event.pause:
391
            self.matrix = self.pause_matrix.next()
392
393
        if self.paused:
394
            self.screen.blit(self.matrix, (self.Z_LEFT, 0))
395
            return self.running
396
397
        if event.type == self.event.fall:
398
            self.handleFall()
399
400
        self.screen.blit(self.matrix, (self.Z_LEFT, 0))
401
        return self.running
402