From bacd95ca074d456ff915ebb651795b782ccb18f9 Mon Sep 17 00:00:00 2001 From: adrienmalin <41926238+adrienmalin@users.noreply.github.com> Date: Sun, 24 Feb 2019 23:20:19 +0100 Subject: [PATCH] Update terminis.py --- terminis/terminis.py | 197 ++++++++++++++++++++++--------------------- 1 file changed, 101 insertions(+), 96 deletions(-) diff --git a/terminis/terminis.py b/terminis/terminis.py index 656f92b..b7481ec 100644 --- a/terminis/terminis.py +++ b/terminis/terminis.py @@ -36,8 +36,6 @@ Tetris clone for terminal --reset\treset to default controls settings --level=n\tstart at level n (integer between 1 and 15)""" -scheduler = sched.scheduler(time.time, lambda delay: curses.napms(int(delay*1000))) - class Rotation: CLOCKWISE = 1 @@ -57,7 +55,35 @@ class Movement: LEFT = Point(-1, 0) RIGHT = Point(1, 0) DOWN = Point(0, 1) - STILL = Point(0, 0) + + +class Scheduler(sched.scheduler, dict): + def __init__(self): + sched.scheduler.__init__(self, time.time, time.sleep) + dict.__init__(self) + + def repeat(self, name, delay, action, args=tuple(), kwargs={}): + self[name] = sched.scheduler.enter(self, delay, 1, self._repeat, (name, delay, action, args, kwargs)) + + def _repeat(self, name, delay, action, args, kwargs): + self.repeat(name, delay, action, args, kwargs) + action(*args, **kwargs) + + def single_shot(self, name, delay, action, args=tuple(), kwargs={}): + self[name] = sched.scheduler.enter(self, delay, 1, self._single_shot, (name, action, args, kwargs)) + + def _single_shot(self, name, action, args, kwargs): + del(self[name]) + action(*args, **kwargs) + + def cancel(self, name): + try: + sched.scheduler.cancel(self, self.pop(name)) + except (KeyError, ValueError): + pass + + +scheduler = Scheduler() class Tetromino: @@ -90,28 +116,40 @@ class Tetromino: self.orientation = 0 self.rotation_point_5_used = False self.rotated_last = False - self.lock_timer = None - self.fall_timer = None self.hold_enabled = True - def move(self, movement, rotated_minoes_positions=None, lock=False): + def can_move(self, movement, rotated_minoes_positions=None): potential_position = self.position + movement if all( self.matrix.is_free_cell(potential_position+mino_position) for mino_position in rotated_minoes_positions or self.minoes_positions ): + return potential_position + + def move_rotate(self, movement, rotated_minoes_positions=None): + potential_position = self.can_move(movement, rotated_minoes_positions) + if potential_position: self.position = potential_position - if rotated_minoes_positions: - self.minoes_positions = rotated_minoes_positions - self.rotated_last = True - else: - self.rotated_last = False - self.postpone_lock() + if "lock" in scheduler: + scheduler.cancel("lock") + scheduler.single_shot("lock", self.lock_delay, self.matrix.lock) + return True + else: + return False + + def move(self, movement, lock=True): + if self.move_rotate(movement): + self.rotated_last = False self.matrix.refresh() return True else: - if lock and rotated_minoes_positions is None and movement == Movement.DOWN: - self.locking() + if ( + lock + and movement == Movement.DOWN + and "lock" not in scheduler + ): + scheduler.single_shot("lock", self.lock_delay, self.matrix.lock) + self.matrix.refresh() return False def rotate(self, direction): @@ -120,8 +158,11 @@ class Tetromino: for mino_position in self.minoes_positions ) for rotation_point, liberty_degree in enumerate(self.SUPER_ROTATION_SYSTEM[self.orientation][direction], start=1): - if self.move(liberty_degree, rotated_minoes_positions): + if self.move_rotate(liberty_degree, rotated_minoes_positions): + self.minoes_positions = rotated_minoes_positions + self.matrix.refresh() self.orientation = (self.orientation+direction) % 4 + self.rotated_last = False if rotation_point == 5: self.rotation_point_5_used = True return True @@ -133,40 +174,19 @@ class Tetromino: self.matrix.game.stats.piece_dropped(1) def hard_drop(self): - if self.lock_timer: - self.lock_timer = scheduler.cancel(self.lock_timer) + scheduler.cancel("lock") lines = 0 while self.move(Movement.DOWN, lock=False): lines += 2 self.matrix.game.stats.piece_dropped(lines) - self.lock() - - def fall(self): - self.fall_timer = scheduler.enter(self.fall_delay, 2, self.fall, tuple()) - self.move(Movement.DOWN) - - def locking(self): - if not self.lock_timer: - self.lock_timer = scheduler.enter(self.lock_delay, 1, self.lock, tuple()) - self.matrix.refresh() - - def postpone_lock(self): - if self.lock_timer: - scheduler.cancel(self.lock_timer) - self.lock_timer = scheduler.enter(self.lock_delay, 1, self.lock, tuple()) - - def lock(self): - self.lock_timer = None - if not self.move(Movement.DOWN): - if self.fall_timer: - self.fall_timer = scheduler.cancel(self.fall_timer) - self.matrix.lock(self.t_spin()) + self.matrix.lock() def t_spin(self): return "" class O(Tetromino): + SUPER_ROTATION_SYSTEM = tuple() MINOES_POSITIONS = (Point(0, 0), Point(1, 0), Point(0, -1), Point(1, -1)) COLOR = curses.COLOR_YELLOW @@ -246,7 +266,7 @@ class Window: def draw_piece(self): if self.piece: - if self.piece.lock_timer: + if "lock" in scheduler: attr = self.piece.color_pair | curses.A_BLINK | curses.A_REVERSE else: attr = self.piece.color_pair @@ -264,7 +284,7 @@ class Matrix(Window): NB_LINES = 21 WIDTH = NB_COLS*2+2 HEIGHT = NB_LINES+1 - PIECE_POSITION = Point(4, 0) + PIECE_POSITION = Point(4, -1) TITLE = "" def __init__(self, game, begin_x, begin_y): @@ -275,6 +295,7 @@ class Matrix(Window): [None for x in range(self.NB_COLS)] for y in range(self.NB_LINES) ] + self.piece = None Window.__init__(self, self.WIDTH, self.HEIGHT, begin_x, begin_y) def refresh(self, paused=False): @@ -296,24 +317,30 @@ class Matrix(Window): and not (position.y >= 0 and self.cells[position.y][position.x] is not None) ) - def lock(self, t_spin): - for mino_position in self.piece.minoes_positions: - position = mino_position + self.piece.position - if position.y >= 0: - self.cells[position.y][position.x] = self.piece.color_pair - else: - self.game.over() - return - - nb_lines_cleared = 0 - for y, line in enumerate(self.cells): - if all(mino for mino in line): - self.cells.pop(y) - self.cells.insert(0, [None for x in range(self.NB_COLS)]) - nb_lines_cleared += 1 - - self.game.stats.piece_locked(nb_lines_cleared, t_spin) - self.game.start_next_piece() + def lock(self): + if not self.piece.can_move(Movement.DOWN): + scheduler.cancel("fall") + + t_spin = self.piece.t_spin() + + for mino_position in self.piece.minoes_positions: + position = mino_position + self.piece.position + if position.y >= 0: + self.cells[position.y][position.x] = self.piece.color_pair + else: + self.game.over() + return + + nb_lines_cleared = 0 + for y, line in enumerate(self.cells): + if all(mino for mino in line): + self.cells.pop(y) + self.cells.insert(0, [None for x in range(self.NB_COLS)]) + nb_lines_cleared += 1 + + self.game.stats.piece_locked(nb_lines_cleared, t_spin) + self.piece = None + self.game.new_piece() class HoldNext(Window): @@ -383,7 +410,6 @@ class Stats(Window): self.combo = -1 self.time = time.time() self.lines_cleared = 0 - self.clock_timer = None self.strings = [] Window.__init__(self, width, height, begin_x, begin_y) self.new_level() @@ -406,10 +432,6 @@ class Stats(Window): self.window.addstr(y, x, string) self.window.refresh() - def clock(self): - self.clock_timer = scheduler.enter(1, 3, self.clock, tuple()) - self.refresh() - def new_level(self): self.level += 1 if self.level <= 20: @@ -611,12 +633,11 @@ class Game: self.playing = True self.paused = False - self.stats.time = time.time() - self.stats.clock_timer = scheduler.enter(1, 3, self.stats.clock, tuple()) self.random_bag = [] self.next.piece = self.random_piece() - self.start_next_piece() - self.input_timer = scheduler.enter(self.AUTOREPEAT_DELAY, 2, self.process_input, tuple()) + self.new_piece() + scheduler.repeat("clock", 1, self.stats.refresh) + scheduler.repeat("input", self.AUTOREPEAT_DELAY, self.process_input) try: scheduler.run() @@ -629,22 +650,18 @@ class Game: random.shuffle(self.random_bag) return self.random_bag.pop()(self.matrix, Next.PIECE_POSITION) - def start_next_piece(self): - self.matrix.piece = self.next.piece - self.next.piece = self.random_piece() - self.next.refresh() - self.start_piece() - - def start_piece(self): + def new_piece(self): + if not self.matrix.piece: + self.matrix.piece, self.next.piece = self.next.piece, self.random_piece() + self.next.refresh() self.matrix.piece.position = Matrix.PIECE_POSITION - if self.matrix.piece.move(Movement.STILL, lock=False): - self.matrix.piece.fall_timer = scheduler.enter(Tetromino.fall_delay, 2, self.matrix.piece.fall, tuple()) + if self.matrix.piece.move(Movement.DOWN): + scheduler.repeat("fall", Tetromino.fall_delay, self.matrix.piece.move, (Movement.DOWN,)) self.matrix.refresh() else: self.over() def process_input(self): - self.input_timer = scheduler.enter(self.AUTOREPEAT_DELAY, 2, self.process_input, tuple()) try: action = self.actions[self.scr.getkey()] except (curses.error, KeyError): @@ -675,24 +692,18 @@ class Game: def swap(self): if self.matrix.piece.hold_enabled: - if self.matrix.piece.fall_timer: - self.matrix.piece.fall_timer = scheduler.cancel(self.matrix.piece.fall_timer) - if self.matrix.piece.lock_timer: - self.matrix.piece.lock_timer = scheduler.cancel(self.matrix.piece.lock_timer) - + scheduler.cancel("fall") + scheduler.cancel("lock") self.matrix.piece, self.hold.piece = self.hold.piece, self.matrix.piece self.hold.piece.position = self.hold.PIECE_POSITION self.hold.piece.minoes_positions = self.hold.piece.MINOES_POSITIONS self.hold.piece.hold_enabled = False self.hold.refresh() - - if self.matrix.piece: - self.start_piece() - else: - self.start_next_piece() + self.new_piece() def over(self): self.matrix.refresh() + scheduler.cancel("clock") if curses.has_colors(): for tetromino_class in self.TETROMINOES: curses.init_pair(tetromino_class.COLOR, tetromino_class.COLOR, curses.COLOR_BLACK) @@ -713,14 +724,8 @@ class Game: def quit(self): self.playing = False - if self.matrix.piece.fall_timer: - self.matrix.piece.fall_timer = scheduler.cancel(self.matrix.piece.fall_timer) - if self.matrix.piece.lock_timer: - self.matrix.piece.lock_timer = scheduler.cancel(self.matrix.piece.lock_timer) - if self.stats.clock_timer: - self.stats.clock_timer = scheduler.cancel(self.stats.clock_timer) - if self.input_timer: - self.input_timer = scheduler.cancel(self.input_timer) + for name in tuple(scheduler): + scheduler.cancel(name) self.stats.save() print("SCORE\t{:n}".format(self.stats.score)) print("HIGH\t{:n}".format(self.stats.high_score))