-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgrid.py
More file actions
229 lines (198 loc) · 8.48 KB
/
grid.py
File metadata and controls
229 lines (198 loc) · 8.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
import pygame as pg
import sounds
from random import randint
class Grid:
frames = 60
lines_scores = [100, 250, 450, 800] # scores for lines cleared
def __init__(self, columns, rows, block_size, viewport_max_coord, background):
"""
:param columns: number of columns
:param rows: number of rows
:param block_size: integer
:param viewport_max_coord: (x, y)
:param background: background image
"""
self.lines = 0
self.score = 0
self.game_over = False
self.lines_cleared = 0
self.block_size = block_size
self.area_width = columns * self.block_size
self.area_height = rows * self.block_size
# top left corner of the grid
self.min_coord = ((viewport_max_coord[0] - self.area_width - 80),
(viewport_max_coord[1] - self.area_height) / 2)
# bottom right corner of the grid
self.max_coord = (self.min_coord[0] + self.area_width,
self.min_coord[1] + self.area_height)
# center coord of the grid (tetromino start coord)
self.center_coord = (self.min_coord[0] + (columns // 2) * self.block_size,
self.min_coord[1] + self.block_size)
self.columns = columns
self.rows = rows
# rows x columns grid
self.grid = [[-1 for i in range(self.columns)] for j in range(self.rows)]
self.background = pg.transform.scale(background, (self.area_width, self.area_height))
self.background.set_alpha(210)
def overlap(self, tetromino_coords):
"""
:param tetromino_coords: list of coordinates (x, y)
:return: True if blocks overlap or passed the bottom
"""
indexes_list = self._convert_coords(tetromino_coords)
for row_index, column_index in indexes_list:
if row_index >= self.rows or self.grid[row_index][column_index] >= 0:
return True
return False
def is_out_of_bounds(self, tetromino_coords, columns=None):
"""
:param tetromino_coords: list of coordinates (x, y)
:param columns: (start, end) start <= column_index < end
:return: True if a block is out of bounds or above the grid
"""
if columns is None:
x_min = self.min_coord[0]
x_max = self.min_coord[0] + self.columns * self.block_size
else:
x_min = self.min_coord[0] + columns[0] * self.block_size
x_max = self.min_coord[0] + columns[1] * self.block_size
for x, y in tetromino_coords:
if x > x_max - self.block_size or x < x_min or y < self.min_coord[1]:
return True
return False
def is_game_over(self):
return self.game_over
def _convert_coords(self, coords):
"""
Convert coordinates to grid indexes.
:param coords: list of coordinates (x, y)
:return: list of indexes (row_index, column_index)
"""
indexes_list = []
for coord in coords:
column_index = int((coord[0] - self.min_coord[0]) // self.block_size)
row_index = int((coord[1] - self.min_coord[1]) // self.block_size)
indexes_list.append((row_index, column_index))
return indexes_list
def _convert_indexes(self, indexes):
"""
Convert grid indexes to coordinates
:param indexes: list of indexes (row_index, column_index)
:return: list of coordinates (x, y)
"""
coords_list = []
for index in indexes:
x = int(index[1] * self.block_size + self.min_coord[0])
y = int(index[0] * self.block_size + self.min_coord[1])
coords_list.append((x, y))
return coords_list
def update(self, coords, color_index):
self.lines_cleared = 0
"""
Converts coordinates to grid indexes and
assigns color_index to the corresponding cells.
:param coords: list of coordinates (x, y)
:param color_index: tetromino color index
"""
indexes_list = self._convert_coords(coords)
for row_index, column_index in indexes_list:
if row_index >= 0 and column_index >= 0:
self.grid[row_index][column_index] = color_index
# search for full rows
if row_index == 0: # there is a block at the first row
self.game_over = True
full_row = True
for j in range(self.columns):
if self.grid[row_index][j] == -1: # cell is empty
full_row = False
break
# delete the row if it is full
if full_row:
self.lines_cleared += 1
del self.grid[row_index]
# insert a new line at the beginning of the grid
self.grid.insert(0, [-1 for i in range(self.columns)])
self.lines += self.lines_cleared
if self.lines_cleared:
self.score += Grid.lines_scores[self.lines_cleared - 1]
if not sounds.mute:
sounds.clear_sound.play()
def insert_blocks(self, lines):
"""
Insert new lines at the end of the grid
and remove the same number of lines at the top of the grid
:param lines: number of lines
"""
empty_column_index = randint(0, self.columns - 1)
for i in range(lines):
new_line = [7 for i in range(self.columns)]
new_line[empty_column_index] = -1
self.grid.pop(0) # remove first line from the grid
self.grid.append(new_line) # insert the new line at the end of the grid
def get_lines_cleared(self):
"""
Return lines cleared and reset the variable
:return: number of lines cleared
"""
temp = self.lines_cleared
self.lines_cleared = 0
return temp
def get_assist_coords(self, tetromino_coords):
"""
:param tetromino_coords: list of coordinates (x, y)
:return: coordinates of the assist blocks
"""
indexes_list = self._convert_coords(tetromino_coords)
bottom = False
overlap = False
# tetromino reached bottom
for row_index, column_index in indexes_list:
if row_index >= self.rows - 1:
return tetromino_coords
while not bottom and not overlap:
# for every block
for i in range(len(indexes_list)):
row_index, column_index = indexes_list[i]
indexes_list[i] = (row_index + 1, column_index)
# check next row
if self.grid[row_index + 1][column_index] >= 0:
overlap = True
elif row_index + 1 >= self.rows - 1:
bottom = True
if overlap:
i = 0
for row_index, column_index in indexes_list:
indexes_list[i] = (row_index - 1, column_index)
i += 1
return self._convert_indexes(indexes_list)
def show(self, screen, color_blocks):
"""
Display background and all blocks.
:param screen: screen surface
:param color_blocks: block sprites tuple
"""
screen.blit(self.background, self.min_coord)
for i in range(self.rows):
for j in range(self.columns):
if self.grid[i][j] >= 0: # if cell isn't empty (empty -> -1)
# cell value is color index
color_index = self.grid[i][j]
coord_x = self.min_coord[0] + j * self.block_size
coord_y = self.min_coord[1] + i * self.block_size
screen.blit(color_blocks[color_index], (coord_x, coord_y))
def display_message(self, screen, font, color, message):
text_surface = font.render(str(message), True, color).convert_alpha()
text_x = self.min_coord[0] + (self.area_width - text_surface.get_width()) / 2
text_y = (self.min_coord[1] + self.area_height) / 2
screen.blit(text_surface, (text_x, text_y))
def get_lines(self):
return self.lines
def get_score(self):
return self.score
def get_center_coord(self, columns=None):
if columns is None:
return self.center_coord
else:
return (self.min_coord[0] + columns[0] * self.block_size +
((columns[1] - columns[0]) // 2) * self.block_size,
self.min_coord[1] + self.block_size)