I'm making a Tetris clone in Python and am having difficulty ironing out my function to clear completed rows. While it works in most cases, when multiple, non-consecutive rows are completed by the same block, some of them will not be shifted down as many rows in the grid as expected. This leaves empty rows at the bottom of the grid.
For example, if "0" is a placed block and "." is an empty space:
.......
.......
..00...
000000.
00.000.
000000.
dropping a line piece on the right side, I would expect two lines to be cleared and the grid to now look like:
.......
.......
.......
.......
..00..0
00.0000
Instead, the result I end up with is:
.......
.......
.......
..00..0
00.0000
.......
with the rows only being shifted 1 row down instead of the expected 2 rows.
Here is my current implementation of the clear_rows
function responsible for handling the clearing and shifting:
def clear_rows(grid, locked_positions):
cleared_rows = 0
for i in range(len(grid) -1, -1, -1):
row = grid[i]
if BLACK not in row and WHITE not in row:
cleared_rows += 1
index = i
for j in range(len(row)):
del locked_positions[(j, i)]
if cleared_rows > 0:
pg.mixer.Sound.play(LINE_SOUND)
for key in sorted(list(locked_positions), key=lambda x: x[1])[::-1]:
x, y = key
if y < index:
new_key = (x, y + cleared_rows)
locked_positions[new_key] = locked_positions.pop(key)
if cleared_rows == 4:
pg.mixer.Sound.play(NUT)
return cleared_rows
grid
is a list of 20 lists of 10 tuples, each representing the color of one 30x30 pixel square in the play area of the Tetris game. Each block is (0, 0, 0) by default unless it is instructed to draw them otherwise according to the locked_positions
.
locked_positions
is a dictionary that is passed to the grid when it gets drawn containing keys that are the (x, y) positions of pieces that have previously landed, and values that are the RGB of those blocks.
If multiple rows must be removed, locked_positions
must be adjusted for each of them (or once in a rather complicated way). The code should therefore roughly look like (untested):
def clear_rows(grid, locked_positions):
cleared_rows = 0
for i in range(len(grid)) #was: range(len(grid) -1, -1, -1):
row = grid[i]
if BLACK not in row and WHITE not in row:
cleared_rows += 1
index = i
for j in range(len(row)):
del locked_positions[(j, i)]
for key in sorted(list(locked_positions), key=lambda x: x[1])[::-1]:
x, y = key
if y < index:
new_key = (x, y + 1)
locked_positions[new_key] = locked_positions.pop(key)
if cleared_rows > 0:
pg.mixer.Sound.play(LINE_SOUND)
if cleared_rows == 4:
pg.mixer.Sound.play(NUT)
return cleared_rows
Mainly the third for-loop was moved into the first and only cares about the currently removed row.
Update: The for i
loop should run top-down because with bottom-up for multiple consecutive rows the row with the same index would have to be processed two or more times.