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

Tetrimino.harddrop()   A

Complexity

Conditions 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 10
rs 9.4285
cc 2
1
import pygame
2
import math
3
from itertools import cycle
4
5
white = (255, 255, 255)
6
7
D_UP = 0
8
9
10
class Tetrimino(pygame.sprite.OrderedUpdates):
11
    def __init__(self, definition, size, background, matrix):
12
        pygame.sprite.OrderedUpdates.__init__(self)
13
        self.blocks = list()
14
        self.color = white
15
        self.outline = 1
16
        self.direction = D_UP
17
        # XXX maybe have 3 separate parameters in init
18
        self.setName(definition['name'])
19
        self.setColor(definition['color'])
20
        self.all_blocks = definition['blocks']
21
        self.blocks_cycle = cycle(self.all_blocks)
22
        self.size = size
23
        self.pivot = (0, 0)
24
        self.setBlocks(next(self.blocks_cycle))
25
        self.background = background
26
        self.matrix = matrix
27
        self.isLocked = False
28
29
    def setName(self, name):
30
        self.name = name
31
        return self
32
33
    def setBlocks(self, blocks):
34
        """Build sprites group"""
35
        # use first block to draw an image
36
        self.blocks = blocks
37
        block = pygame.Rect(blocks[0][0] * self.size,
38
                            blocks[0][1] * self.size,
39
                            self.size,
40
                            self.size)
41
        image = self.buildImage(block)
42
        for block in blocks:
43
            sprite = pygame.sprite.Sprite()
44
            sprite.rect = pygame.Rect(block[0] * self.size + self.pivot[0],
45
                                      block[1] * self.size + self.pivot[1],
46
                                      self.size,
47
                                      self.size)
48
            sprite.image = image
49
            sprite.add(self)
50
        return self
51
52
    def buildImage(self, block):
53
        block.top, block.left = 0, 0
54
        image = pygame.Surface(block.size)
55
        # inner block is a little smaller
56
        innerblock = pygame.Rect(self.outline,
57
                                 self.outline,
58
                                 block.size[0] - self.outline * 2,
59
                                 block.size[1] - self.outline * 2)
60
        pygame.draw.rect(image, self.color, innerblock, self.outline)
61
        return image
62
63
    def setColor(self, color):
64
        """Set the color of the sprites"""
65
        self.color = color
66
        return self
67
68
    def clear(self, matrix):
69
        pygame.sprite.OrderedUpdates.clear(self, matrix, self.background)
70
71
    def moveUp(self):
72
        self._move('up')
73
        return self
74
75
    def moveDown(self):
76
        moved = False
77
        self._move('down')
78
        if self.isColliding():
79
            self._move('up')
80
            self.isLocked = True
81
        else:
82
            moved = True
83
            self._redraw()
84
        return moved
85
86
    def moveLeft(self):
87
        self._move('left')
88
        if self.isColliding():
89
            self._move('right')
90
        else:
91
            self._redraw()
92
        return self
93
94
    def moveRight(self):
95
        self._move('right')
96
        if self.isColliding():
97
            self._move('left')
98
        else:
99
            self._redraw()
100
        return self
101
102
    def _move(self, direction):
103
        if direction == 'up':
104
            for sprite in self.sprites():
105
                sprite.rect.top -= self.size
106
        elif direction == 'down':
107
            for sprite in self.sprites():
108
                sprite.rect.top += self.size
109
        elif direction == 'left':
110
            for sprite in self.sprites():
111
                sprite.rect.left -= self.size
112
        elif direction == 'right':
113
            for sprite in self.sprites():
114
                sprite.rect.left += self.size
115
116
    def harddrop(self):
117
        harddrops = 0
118
        while not self.isColliding():
119
            self._move('down')
120
            harddrops += 1
121
        self._move('up')
122
        self._redraw()
123
        self.isLocked = True
124
        harddrops -= 1
125
        return harddrops
126
127
    def _redraw(self):
128
        self.clear(self.matrix)
129
        self.draw(self.matrix)
130
131
    def rotate(self, test_collision=True):
132
        # previous set of blocks base positions
133
        previous_positions = self.blocks
134
        # get current position
135
        positions = list()
136
        for sprite in self.sprites():
137
            positions.append((sprite.rect.left / self.size,
138
                              sprite.rect.top / self.size))
139
        # empty sprite list
140
        self.empty()
141
        # get next set of blocks
142
        self.setBlocks(next(self.blocks_cycle))
143
        new_positions = self.blocks
144
        # set new calculated positions
145
        for index, sprite in enumerate(self.sprites()):
146
            sprite.rect.left = (positions[index][0] -
147
                                previous_positions[index][0] +
148
                                new_positions[index][0]) * self.size
149
            sprite.rect.top = (positions[index][1] -
150
                               previous_positions[index][1] +
151
                               new_positions[index][1]) * self.size
152
        if test_collision and self.isColliding():
153
            # handle matrix collisions
154
            if self.colliding == 'left':
155
                left = min(sprite.rect.left for sprite
156
                           in self.sprites()) / self.size
157
                if left < 0:
158
                    for _ in range(0, left * - 1):
159
                        self.moveRight()
160
            elif self.colliding == 'right':
161
                right = max(sprite.rect.right for sprite
162
                            in self.sprites()) / self.size
163
                if right > 10:
164
                    for _ in range(0, right - 10):
165
                        self.moveLeft()
166
            # handle sprite group collisions
167
            for _ in range(1, len(self.all_blocks)):
168
                self.rotate(False)
169
            # use previous positions
170
            for index, sprite in enumerate(self.sprites()):
171
                sprite.rect.left = positions[index][0] * self.size
172
                sprite.rect.top = positions[index][1] * self.size
173
            return self.isColliding()
174
        return True
175
176
    def isColliding(self):
177
        # Test collisions between sprites
178
        for group in self.matrix.sprites:
179
            if pygame.sprite.groupcollide(group, self, False, False):
180
                return True
181
        # Test collisions with boundaries
182
        matrix = self.matrix.get_rect()
183
        bottom = max(sprite.rect.bottom for sprite in self.sprites())
184
        if bottom > matrix.bottom:
185
            self.colliding = 'bottom'
186
            return True
187
        left = min(sprite.rect.left for sprite in self.sprites())
188
        if left < matrix.left:
189
            self.colliding = 'left'
190
            return True
191
        right = max(sprite.rect.right for sprite in self.sprites())
192
        if right > matrix.right:
193
            self.colliding = 'right'
194
            return True
195
        self.colliding = None
196
        return False
197
198
    def center(self, width, height=0):
199
        top = min(sprite.rect.top for sprite in self.sprites()) / self.size
200
        if top > 0:
201
            self.moveUp()
202
        groupwidth = max(sprite.rect.right for sprite
203
                         in self.sprites()) / self.size
204
        matrixcenter = (width / self.size) / 2
205
        start = matrixcenter - int(math.ceil(float(groupwidth) / 2))
206
        for sprite in self.sprites():
207
            sprite.rect.left += (start * self.size)
208
            sprite.rect.top += height
209
        return self
210