From 6866d63a32c67be5b199e64dc073d30313a91c2b Mon Sep 17 00:00:00 2001 From: adrienmalin <41926238+adrienmalin@users.noreply.github.com> Date: Sun, 29 Sep 2019 11:29:40 +0200 Subject: [PATCH] optimize lock --- tetrarcade.py | 132 ++++++++++++++++++++++++++----------------------- tetrislogic.py | 82 +++++++++++++++++------------- 2 files changed, 118 insertions(+), 96 deletions(-) diff --git a/tetrarcade.py b/tetrarcade.py index 208aa14..8f5d571 100644 --- a/tetrarcade.py +++ b/tetrarcade.py @@ -3,6 +3,7 @@ import sys import locale import time import os +import itertools try: import arcade @@ -14,7 +15,7 @@ python -m pip install --user arcade """ ) -from tetrislogic import TetrisLogic, State, AbstractScheduler +from tetrislogic import TetrisLogic, State, AbstractScheduler, NB_LINES # Constants @@ -34,19 +35,19 @@ TEXT_MARGIN = 40 FONT_SIZE = 16 TEXT_HEIGHT = 20.8 HIGHLIGHT_TEXT_FONT_SIZE = 20 -START_TEXT = """TETRARCADE +TITLE_AND_CONTROL_TEXT = """TETRARCADE CONTROLS -MOVE LEFT ← -MOVE RIGHT → -SOFT DROP ↓ -HARD DROP SPACE -ROTATE CLOCKWISE ↑ -ROTATE COUNTERCLOCKWISE Z -HOLD C -PAUSE ESC - -PRESS [ENTER] TO START""" +MOVE LEFT ← +MOVE RIGHT → +SOFT DROP ↓ +HARD DROP SPACE +ROTATE CLOCKWISE ↑ +ROTATE COUNTER Z +HOLD C +PAUSE ESC""" +START_TEXT = TITLE_AND_CONTROL_TEXT + "\n\nPRESS [ENTER] TO START" +PAUSE_TEXT = TITLE_AND_CONTROL_TEXT + "\n\nPRESS [ESC] TO RESUME" STATS_TEXT = """SCORE HIGH SCORE @@ -59,19 +60,6 @@ LINES TIME """ -PAUSE_TEXT = """TETRARCADE - -CONTROLS -MOVE LEFT ← -MOVE RIGHT → -SOFT DROP ↓ -HARD DROP SPACE -ROTATE CLOCKWISE ↑ -ROTATE COUNTERCLOCKWISE Z -HOLD C -RESUME ESC - -PRESS [ESC] TO RESUME""" GAME_OVER_TEXT = """GAME OVER @@ -125,6 +113,16 @@ class ArcadeScheduler(AbstractScheduler): arcade.unschedule(_task) del self._tasks[task] + def restart(self, task, period): + try: + _task = self._tasks[task] + except KeyError: + _task = lambda _: task() + self._tasks[task] = _task + else: + arcade.unschedule(_task) + arcade.schedule(_task, period) + class TetrArcade(TetrisLogic, arcade.Window): @@ -182,15 +180,22 @@ class TetrArcade(TetrisLogic, arcade.Window): antialiasing = False ) + center_x = self.width / 2 + center_y = self.height / 2 self.bg_sprite = arcade.Sprite(WINDOW_BG) - self.matrix_minoes_sprites = arcade.SpriteList() + self.bg_sprite.center_x = center_x + self.bg_sprite.center_y = center_y + self.matrix_minoes_sprites = [] self.held_piece_sprites = arcade.SpriteList() self.current_piece_sprites = arcade.SpriteList() self.ghost_piece_sprites = arcade.SpriteList() self.next_pieces_sprites = arcade.SpriteList() self.matrix_sprite = arcade.Sprite(MATRIX_SPRITE_PATH) self.matrix_sprite.alpha = MATRIX_SRITE_ALPHA - self.on_resize(self.width, self.height) + self.matrix_sprite.center_x = center_x + self.matrix_sprite.center_y = center_y + self.matrix_sprite.left = int(self.matrix_sprite.left) + self.matrix_sprite.top = int(self.matrix_sprite.top) self.general_text = arcade.create_text( text = STATS_TEXT, color = TEXT_COLOR, @@ -201,17 +206,40 @@ class TetrArcade(TetrisLogic, arcade.Window): def new_game(self): self.highlight_texts = [] + self.matrix_minoes_sprites = [] super().new_game() def new_current_piece(self): super().new_current_piece() self.reload_next_pieces() self.reload_current_piece() - self.reload_matrix() def lock(self): super().lock() + def enter_the_matrix(self): + super().enter_the_matrix() + self.update_current_piece() + for mino_coord, mino_sprite in zip( + self.current_piece.minoes_coords, + self.current_piece_sprites + ): + mino_coord += self.current_piece.coord + self.matrix_minoes_sprites[ + mino_coord.y + ].append(mino_sprite) + + def append_new_line_to_matrix(self): + super().append_new_line_to_matrix() + self.matrix_minoes_sprites.append(arcade.SpriteList()) + + def remove_line_of_matrix(self, line): + super().remove_line_of_matrix(line) + self.matrix_minoes_sprites.pop(line) + for line_sprites in self.matrix_minoes_sprites[line:NB_LINES+2]: + for mino_sprite in line_sprites: + mino_sprite.center_y -= mino_sprite.height-1 + def swap(self): super().swap() self.reload_held_piece() @@ -272,19 +300,6 @@ class TetrArcade(TetrisLogic, arcade.Window): self.current_piece_sprites = self.reload_piece(self.current_piece) self.ghost_piece_sprites = self.reload_piece(self.ghost_piece) - def reload_matrix(self): - if self.matrix: - self.matrix_minoes_sprites = arcade.SpriteList() - for y, line in enumerate(self.matrix): - for x, mino_color in enumerate(line): - if mino_color: - mino_sprite_path = MINOES_SPRITES_PATHS[mino_color] - mino_sprite = arcade.Sprite(mino_sprite_path) - mino_sprite.left = self.matrix_sprite.left + x*(mino_sprite.width-1) - mino_sprite.bottom = self.matrix_sprite.bottom + y*(mino_sprite.height-1) - mino_sprite.alpha = 200 - self.matrix_minoes_sprites.append(mino_sprite) - def update_piece(self, piece, piece_sprites): if piece: for mino_sprite, mino_coord in zip( @@ -294,26 +309,30 @@ class TetrArcade(TetrisLogic, arcade.Window): mino_sprite.left = self.matrix_sprite.left + mino_coord.x*(mino_sprite.width-1) mino_sprite.bottom = self.matrix_sprite.bottom + mino_coord.y*(mino_sprite.height-1) + def update_current_piece(self): + self.update_piece(self.current_piece, self.current_piece_sprites) + if self.current_piece.prelocked: + alpha = ( + PRELOCKED_ALPHA + if self.current_piece.prelocked + else NORMAL_ALPHA + ) + for mino_sprite in self.current_piece_sprites: + mino_sprite.alpha = alpha + def on_draw(self): arcade.start_render() self.bg_sprite.draw() if self.state in (State.PLAYING, State.OVER): self.matrix_sprite.draw() - self.matrix_minoes_sprites.draw() + for line in self.matrix_minoes_sprites: + line.draw() self.update_piece(self.held_piece, self.held_piece_sprites) self.held_piece_sprites.draw() - self.update_piece(self.current_piece, self.current_piece_sprites) - if self.current_piece.prelocked: - alpha = ( - PRELOCKED_ALPHA - if self.current_piece.prelocked - else NORMAL_ALPHA - ) - for mino_sprite in self.current_piece_sprites: - mino_sprite.alpha = alpha + self.update_current_piece() self.current_piece_sprites.draw() self.update_piece(self.ghost_piece, self.ghost_piece_sprites) @@ -379,17 +398,6 @@ class TetrArcade(TetrisLogic, arcade.Window): anchor_y = 'center' ) - def on_resize(self, width, height): - center_x = self.width / 2 - center_y = self.height / 2 - self.bg_sprite.center_x = center_x - self.bg_sprite.center_y = center_y - self.matrix_sprite.center_x = center_x - self.matrix_sprite.center_y = center_y - self.matrix_sprite.left = int(self.matrix_sprite.left) - self.matrix_sprite.top = int(self.matrix_sprite.top) - self.reload_matrix() - def load_high_score(self): try: with open(HIGH_SCORE_PATH, "r") as f: diff --git a/tetrislogic.py b/tetrislogic.py index 8e9ccc6..7072a1f 100644 --- a/tetrislogic.py +++ b/tetrislogic.py @@ -76,8 +76,18 @@ class AbstractScheduler: class Tetromino: + random_bag = [] + + + class MetaTetromino(type): + + def __init__(cls, name, bases, dico): + super().__init__(name, bases, dico) + cls.classes.append(cls) + class AbstractTetromino: + # Super rotation system SRS = { Rotation.COUNTERCLOCKWISE: ( @@ -94,6 +104,7 @@ class Tetromino: ) } CAN_SPIN = False + classes = [] def __init__(self): self.coord = NEXT_PIECES_COORDS[-1] @@ -107,7 +118,7 @@ class Tetromino: return self.__class__() - class O(AbstractTetromino): + class O(AbstractTetromino, metaclass=MetaTetromino): SRS = { Rotation.COUNTERCLOCKWISE: (tuple(), tuple(), tuple(), tuple()), @@ -120,7 +131,7 @@ class Tetromino: return False - class I(AbstractTetromino): + class I(AbstractTetromino, metaclass=MetaTetromino): SRS = { Rotation.COUNTERCLOCKWISE: ( @@ -140,43 +151,40 @@ class Tetromino: MINOES_COLOR = "cyan" - class T(AbstractTetromino): + class T(AbstractTetromino, metaclass=MetaTetromino): MINOES_COORDS = (Coord(-1, 0), Coord(0, 0), Coord(0, 1), Coord(1, 0)) MINOES_COLOR = "magenta" CAN_SPIN = True - class L(AbstractTetromino): + class L(AbstractTetromino, metaclass=MetaTetromino): MINOES_COORDS = (Coord(-1, 0), Coord(0, 0), Coord(1, 0), Coord(1, 1)) MINOES_COLOR = "orange" - class J(AbstractTetromino): + class J(AbstractTetromino, metaclass=MetaTetromino): MINOES_COORDS = (Coord(-1, 1), Coord(-1, 0), Coord(0, 0), Coord(1, 0)) MINOES_COLOR = "blue" - class S(AbstractTetromino): + class S(AbstractTetromino, metaclass=MetaTetromino): MINOES_COORDS = (Coord(-1, 0), Coord(0, 0), Coord(0, 1), Coord(1, 1)) MINOES_COLOR = "green" - class Z(AbstractTetromino): + class Z(AbstractTetromino, metaclass=MetaTetromino): MINOES_COORDS = (Coord(-1, 1), Coord(0, 1), Coord(0, 0), Coord(1, 0)) MINOES_COLOR = "red" - TETROMINOES = (O, I, T, L, J, S, Z) - random_bag = [] - def __new__(cls): if not cls.random_bag: - cls.random_bag = list(cls.TETROMINOES) + cls.random_bag = list(Tetromino.AbstractTetromino.classes) random.shuffle(cls.random_bag) return cls.random_bag.pop()() @@ -209,10 +217,9 @@ class TetrisLogic(): self.lock_delay = LOCK_DELAY self.fall_delay = FALL_DELAY - self.matrix = [ - [None for x in range(NB_COLS)] - for y in range(NB_LINES+3) - ] + self.matrix = [] + for y in range(NB_LINES+3): + self.append_new_line_to_matrix() self.next_pieces = [Tetromino() for i in range(NB_NEXT_PIECES)] self.current_piece = None self.held_piece = None @@ -341,8 +348,8 @@ class TetrisLogic(): self.current_piece.prelocked = False self.scheduler.stop(self.lock) if self.pressed_actions: - self.stop_autorepeat() - self.scheduler.start(self.repeat_action, AUTOREPEAT_DELAY) + self.auto_repeat = False + self.scheduler.restart(self.repeat_action, AUTOREPEAT_DELAY) # Game over if all( @@ -352,19 +359,15 @@ class TetrisLogic(): self.game_over() return - # Insert tetromino in the matrix - for mino_coord in self.current_piece.minoes_coords: - coord = mino_coord + self.current_piece.coord - if coord.y <= NB_LINES+3: - self.matrix[coord.y][coord.x] = self.current_piece.MINOES_COLOR + self.enter_the_matrix() # Clear complete lines nb_lines_cleared = 0 for y, line in reversed(list(enumerate(self.matrix))): if all(mino for mino in line): nb_lines_cleared += 1 - self.matrix.pop(y) - self.matrix.append([None for x in range(NB_COLS)]) + self.remove_line_of_matrix(y) + self.append_new_line_to_matrix() if nb_lines_cleared: self.nb_lines += nb_lines_cleared @@ -423,6 +426,19 @@ class TetrisLogic(): else: self.new_current_piece() + def enter_the_matrix(self): + for mino_coord in self.current_piece.minoes_coords: + coord = mino_coord + self.current_piece.coord + if coord.y <= NB_LINES+3: + self.matrix[coord.y][coord.x] = self.current_piece.MINOES_COLOR + + + def append_new_line_to_matrix(self): + self.matrix.append([None for x in range(NB_COLS)]) + + def remove_line_of_matrix(self, line): + self.matrix.pop(line) + def can_move(self, potential_coord, minoes_coords): return all( self.cell_is_free(potential_coord+mino_coord) @@ -478,7 +494,8 @@ class TetrisLogic(): self.scheduler.stop(self.lock) self.scheduler.stop(self.update_time) self.pressed_actions = [] - self.stop_autorepeat() + self.auto_repeat = False + self.scheduler.stop(self.repeat_action) def resume(self): self.state = State.PLAYING @@ -494,15 +511,15 @@ class TetrisLogic(): self.scheduler.stop(self.repeat_action) self.save_high_score() - def update_time(self, delta_time=1): - self.time += delta_time + def update_time(self): + self.time += 1 def do_action(self, action): action() if action in self.autorepeatable_actions: - self.stop_autorepeat() + self.auto_repeat = False self.pressed_actions.append(action) - self.scheduler.start(self.repeat_action, AUTOREPEAT_DELAY) + self.scheduler.restart(self.repeat_action, AUTOREPEAT_DELAY) def repeat_action(self): if self.pressed_actions: @@ -511,7 +528,8 @@ class TetrisLogic(): self.auto_repeat = True self.scheduler.restart(self.repeat_action, AUTOREPEAT_PERIOD) else: - self.stop_autorepeat() + self.auto_repeat = False + self.scheduler.stop(self.repeat_action) def remove_action(self, action): if action in self.autorepeatable_actions: @@ -520,10 +538,6 @@ class TetrisLogic(): except ValueError: pass - def stop_autorepeat(self): - self.auto_repeat = False - self.scheduler.stop(self.repeat_action) - def show_text(self, text): print(text) raise Warning("TetrisLogic.show_text not implemented.")