This commit is contained in:
adrienmalin 2019-09-28 05:06:59 +02:00
parent 30e5bf30c3
commit 9ca1233027
2 changed files with 118 additions and 86 deletions

@ -13,7 +13,7 @@ python -m pip install --user arcade
""" """
) )
from tetris import Tetris, Status from tetris import Tetris, Status, Scheduler
# Constants # Constants
@ -35,6 +35,10 @@ TEXT_MARGIN = 40
FONT_SIZE = 10 FONT_SIZE = 10
HIGHLIGHT_TEXT_FONT_SIZE = 20 HIGHLIGHT_TEXT_FONT_SIZE = 20
TEXT_HEIGHT = 13.2 TEXT_HEIGHT = 13.2
START_TEXT = """PRESS
[ENTER]
TO
START"""
STATS_TEXT = """SCORE STATS_TEXT = """SCORE
HIGH SCORE HIGH SCORE
TIME TIME
@ -89,14 +93,40 @@ GHOST_ALPHA = 50
MATRIX_SRITE_ALPHA = 100 MATRIX_SRITE_ALPHA = 100
class ArcadeScheduler(Scheduler):
def __init__(self):
self._tasks = {}
def start(self, task, period):
def _task(delta_time):
task()
self._tasks[task] = _task
arcade.schedule(_task, period)
def stop(self, task):
try:
_task = self._tasks[task]
except KeyError:
pass
else:
arcade.unschedule(_task)
del self._tasks[task]
class TetrArcade(Tetris, arcade.Window): class TetrArcade(Tetris, arcade.Window):
scheduler = ArcadeScheduler()
def __init__(self): def __init__(self):
super().__init__() super().__init__()
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, '')
self.actions = { self.actions = {
Status.STARTING: {
arcade.key.ENTER: self.new_game
},
Status.PLAYING: { Status.PLAYING: {
arcade.key.LEFT: self.move_left, arcade.key.LEFT: self.move_left,
arcade.key.NUM_4: self.move_left, arcade.key.NUM_4: self.move_left,
@ -171,7 +201,7 @@ class TetrArcade(Tetris, arcade.Window):
self.reload_matrix() self.reload_matrix()
if self.pressed_actions: if self.pressed_actions:
self.stop_autorepeat() self.stop_autorepeat()
arcade.schedule(self.repeat_action, AUTOREPEAT_DELAY) self.scheduler.start(self.repeat_action, AUTOREPEAT_DELAY)
def lock(self, _=0): def lock(self, _=0):
super().lock() super().lock()
@ -191,7 +221,7 @@ class TetrArcade(Tetris, arcade.Window):
def game_over(self): def game_over(self):
super().game_over() super().game_over()
self.unschedule(self.repeat_action) self.scheduler.stop(self.repeat_action)
def on_key_press(self, key, modifiers): def on_key_press(self, key, modifiers):
for key_or_modifier in (key, modifiers): for key_or_modifier in (key, modifiers):
@ -204,7 +234,7 @@ class TetrArcade(Tetris, arcade.Window):
if action in self.autorepeatable_actions: if action in self.autorepeatable_actions:
self.stop_autorepeat() self.stop_autorepeat()
self.pressed_actions.append(action) self.pressed_actions.append(action)
arcade.schedule(self.repeat_action, AUTOREPEAT_DELAY) self.scheduler.start(self.repeat_action, AUTOREPEAT_DELAY)
def on_key_release(self, key, modifiers): def on_key_release(self, key, modifiers):
try: try:
@ -220,31 +250,32 @@ class TetrArcade(Tetris, arcade.Window):
else: else:
if not self.pressed_actions: if not self.pressed_actions:
self.stop_autorepeat() self.stop_autorepeat()
arcade.schedule(self.repeat_action, AUTOREPEAT_DELAY) self.scheduler.start(self.repeat_action, AUTOREPEAT_DELAY)
def repeat_action(self, delta_time=0): def repeat_action(self, _=0):
if self.pressed_actions: if self.pressed_actions:
self.pressed_actions[-1]() self.pressed_actions[-1]()
if not self.auto_repeat: if not self.auto_repeat:
self.auto_repeat = True self.auto_repeat = True
arcade.unschedule(self.repeat_action) self.scheduler.stop(self.repeat_action)
arcade.schedule(self.repeat_action, AUTOREPEAT_INTERVAL) self.scheduler.start(self.repeat_action, AUTOREPEAT_INTERVAL)
else: else:
self.auto_repeat = False self.auto_repeat = False
arcade.unschedule(self.repeat_action) self.scheduler.stop(self.repeat_action)
def stop_autorepeat(self): def stop_autorepeat(self):
self.auto_repeat = False self.auto_repeat = False
self.unschedule(self.repeat_action) self.scheduler.stop(self.repeat_action)
def show_text(self, text): def show_text(self, text):
self.highlight_texts.append(text) self.highlight_texts.append(text)
self.schedule(self.del_highlight_text, HIGHLIGHT_TEXT_DISPLAY_DELAY) self.scheduler.start(self.del_highlight_text, HIGHLIGHT_TEXT_DISPLAY_DELAY)
def del_highlight_text(self, _=0): def del_highlight_text(self):
if self.highlight_texts:
self.highlight_texts.pop(0) self.highlight_texts.pop(0)
if not self.highlight_texts: else:
self.unschedule(self.del_highlight_text) self.scheduler.stop(self.del_highlight_text)
def reload_piece(self, piece): def reload_piece(self, piece):
piece_sprites = arcade.SpriteList() piece_sprites = arcade.SpriteList()
@ -296,8 +327,9 @@ class TetrArcade(Tetris, arcade.Window):
def on_draw(self): def on_draw(self):
arcade.start_render() arcade.start_render()
self.bg_sprite.draw() self.bg_sprite.draw()
if self.status in (Status.PLAYING, Status.OVER):
self.matrix_sprite.draw() self.matrix_sprite.draw()
if not self.status == Status.PAUSED:
self.matrix_minoes_sprites.draw() self.matrix_minoes_sprites.draw()
self.update_piece(self.held_piece, self.held_piece_sprites) self.update_piece(self.held_piece, self.held_piece_sprites)
@ -351,7 +383,9 @@ class TetrArcade(Tetris, arcade.Window):
font_name = FONT_NAME, font_name = FONT_NAME,
anchor_x = 'right' anchor_x = 'right'
) )
highlight_text = { highlight_text = {
Status.STARTING: START_TEXT,
Status.PLAYING: self.highlight_texts[0] if self.highlight_texts else "", Status.PLAYING: self.highlight_texts[0] if self.highlight_texts else "",
Status.PAUSED: PAUSE_TEXT, Status.PAUSED: PAUSE_TEXT,
Status.OVER: GAME_OVER_TEXT Status.OVER: GAME_OVER_TEXT
@ -380,16 +414,9 @@ class TetrArcade(Tetris, arcade.Window):
self.matrix_sprite.top = int(self.matrix_sprite.top) self.matrix_sprite.top = int(self.matrix_sprite.top)
self.reload_matrix() self.reload_matrix()
def schedule(self, task, period):
arcade.schedule(task, period)
def unschedule(self, task):
arcade.unschedule(task)
def main(): def main():
tetrarcade = TetrArcade() TetrArcade()
tetrarcade.new_game()
arcade.run() arcade.run()
if __name__ == "__main__": if __name__ == "__main__":

@ -25,10 +25,10 @@ class Coord:
# Piece init position # Piece init position
MATRIX_PIECE_INIT_POSITION = Coord(4, NB_LINES) MATRIX_PIECE_INIT_POSITION = Coord(4, NB_LINES)
NEXT_PIECES_POSITIONS = [ NEXT_PIECES_POSITIONS = [
Coord(NB_COLS+3, NB_LINES-4*n-3) Coord(NB_COLS+6, NB_LINES-4*n-3)
for n in range(NB_NEXT_PIECES) for n in range(NB_NEXT_PIECES)
] ]
HELD_PIECE_POSITION = Coord(-4, NB_LINES-3) HELD_PIECE_POSITION = Coord(-7, NB_LINES-3)
HELD_I_POSITION = Coord(-5, NB_LINES-3) HELD_I_POSITION = Coord(-5, NB_LINES-3)
@ -167,8 +167,18 @@ class Tetromino:
return cls.random_bag.pop()() return cls.random_bag.pop()()
class Scheduler:
def start(task, period):
raise NotImplementedError
def stop(self, task):
raise NotImplementedError
class Tetris(): class Tetris():
T_SLOT = (Coord(-1, 1), Coord(1, 1), Coord(1, -1), Coord(-1, -1)) T_SLOT = (Coord(-1, 1), Coord(1, 1), Coord(1, -1), Coord(-1, -1))
SCORES = ( SCORES = (
{"name": "", T_Spin.NO_T_SPIN: 0, T_Spin.MINI_T_SPIN: 1, T_Spin.T_SPIN: 4}, {"name": "", T_Spin.NO_T_SPIN: 0, T_Spin.MINI_T_SPIN: 1, T_Spin.T_SPIN: 4},
@ -177,6 +187,7 @@ class Tetris():
{"name": "TRIPLE", T_Spin.NO_T_SPIN: 5, T_Spin.T_SPIN: 16}, {"name": "TRIPLE", T_Spin.NO_T_SPIN: 5, T_Spin.T_SPIN: 16},
{"name": "TETRIS", T_Spin.NO_T_SPIN: 8} {"name": "TETRIS", T_Spin.NO_T_SPIN: 8}
) )
scheduler = Scheduler()
def __init__(self): def __init__(self):
self.high_score = 0 self.high_score = 0
@ -205,7 +216,7 @@ class Tetris():
self.current_piece = None self.current_piece = None
self.held_piece = None self.held_piece = None
self.status = Status.PLAYING self.status = Status.PLAYING
self.schedule(self.clock, 1) self.scheduler.start(self.clock, 1)
self.new_level() self.new_level()
def new_level(self): def new_level(self):
@ -216,7 +227,7 @@ class Tetris():
if self.level > 15: if self.level > 15:
self.lock_delay = 0.5 * pow(0.9, self.level-15) self.lock_delay = 0.5 * pow(0.9, self.level-15)
self.show_text("Level\n{:n}".format(self.level)) self.show_text("Level\n{:n}".format(self.level))
self.schedule(self.drop, self.fall_delay) self.scheduler.start(self.drop, self.fall_delay)
self.new_current_piece() self.new_current_piece()
def new_current_piece(self): def new_current_piece(self):
@ -250,8 +261,8 @@ class Tetris():
potential_position = self.current_piece.position + movement potential_position = self.current_piece.position + movement
if self.can_move(potential_position, self.current_piece.minoes_positions): if self.can_move(potential_position, self.current_piece.minoes_positions):
if self.current_piece.prelocked: if self.current_piece.prelocked:
self.unschedule(self.lock) self.scheduler.stop(self.lock)
self.schedule(self.lock, self.lock_delay) self.scheduler.start(self.lock, self.lock_delay)
self.current_piece.position = potential_position self.current_piece.position = potential_position
self.current_piece.last_rotation_point_used = None self.current_piece.last_rotation_point_used = None
self.move_ghost() self.move_ghost()
@ -262,13 +273,13 @@ class Tetris():
and movement == Movement.DOWN and movement == Movement.DOWN
): ):
self.current_piece.prelocked = True self.current_piece.prelocked = True
self.schedule(self.lock, self.lock_delay) self.scheduler.start(self.lock, self.lock_delay)
return False return False
def move_left(self, delta_time=0): def move_left(self):
self.move(Movement.LEFT) self.move(Movement.LEFT)
def move_right(self, delta_time=0): def move_right(self):
self.move(Movement.RIGHT) self.move(Movement.RIGHT)
def rotate(self, direction): def rotate(self, direction):
@ -283,8 +294,8 @@ class Tetris():
potential_position = self.current_piece.position + liberty_degree potential_position = self.current_piece.position + liberty_degree
if self.can_move(potential_position, rotated_minoes_positions): if self.can_move(potential_position, rotated_minoes_positions):
if self.current_piece.prelocked: if self.current_piece.prelocked:
self.unschedule(self.lock) self.scheduler.stop(self.lock)
self.schedule(self.lock, self.lock_delay) self.scheduler.start(self.lock, self.lock_delay)
self.current_piece.position = potential_position self.current_piece.position = potential_position
self.current_piece.minoes_positions = rotated_minoes_positions self.current_piece.minoes_positions = rotated_minoes_positions
self.current_piece.orientation = ( self.current_piece.orientation = (
@ -296,10 +307,10 @@ class Tetris():
else: else:
return False return False
def rotate_counterclockwise(self, delta_time=0): def rotate_counterclockwise(self):
self.rotate(Rotation.COUNTERCLOCKWISE) self.rotate(Rotation.COUNTERCLOCKWISE)
def rotate_clockwise(self, delta_time=0): def rotate_clockwise(self):
self.rotate(Rotation.CLOCKWISE) self.rotate(Rotation.CLOCKWISE)
def move_ghost(self): def move_ghost(self):
@ -311,7 +322,7 @@ class Tetris():
): ):
self.ghost_piece.position += Movement.DOWN self.ghost_piece.position += Movement.DOWN
def drop(self, _=0): def drop(self):
self.move(Movement.DOWN) self.move(Movement.DOWN)
def add_to_score(self, ds): def add_to_score(self, ds):
@ -335,6 +346,9 @@ class Tetris():
if self.move(Movement.DOWN): if self.move(Movement.DOWN):
return return
self.current_piece.prelocked = False
self.scheduler.stop(self.lock)
if all( if all(
(mino_position + self.current_piece.position).y >= NB_LINES (mino_position + self.current_piece.position).y >= NB_LINES
for mino_position in self.current_piece.minoes_positions for mino_position in self.current_piece.minoes_positions
@ -342,8 +356,6 @@ class Tetris():
self.game_over() self.game_over()
return return
self.unschedule(self.lock)
for mino_position in self.current_piece.minoes_positions: for mino_position in self.current_piece.minoes_positions:
position = mino_position + self.current_piece.position position = mino_position + self.current_piece.position
if position.y <= NB_LINES+3: if position.y <= NB_LINES+3:
@ -409,7 +421,7 @@ class Tetris():
self.add_to_score(lock_score) self.add_to_score(lock_score)
if self.goal <= 0: if self.goal <= 0:
self.unschedule(self.drop) self.scheduler.stop(self.drop)
self.new_level() self.new_level()
else: else:
self.new_current_piece() self.new_current_piece()
@ -418,7 +430,7 @@ class Tetris():
if self.current_piece.hold_enabled: if self.current_piece.hold_enabled:
self.current_piece.hold_enabled = False self.current_piece.hold_enabled = False
self.current_piece.prelocked = False self.current_piece.prelocked = False
self.unschedule(self.lock) self.scheduler.stop(self.lock)
self.current_piece, self.held_piece = self.held_piece, self.current_piece self.current_piece, self.held_piece = self.held_piece, self.current_piece
if self.held_piece.__class__ == Tetromino.I: if self.held_piece.__class__ == Tetromino.I:
self.held_piece.position = HELD_I_POSITION self.held_piece.position = HELD_I_POSITION
@ -432,35 +444,28 @@ class Tetris():
else: else:
self.new_current_piece() self.new_current_piece()
def pause(self, _=0): def pause(self):
self.status = Status.PAUSED self.status = Status.PAUSED
self.unschedule(self.drop) self.scheduler.stop(self.drop)
self.unschedule(self.lock) self.scheduler.stop(self.lock)
self.unschedule(self.clock) self.scheduler.stop(self.clock)
self.pressed_actions = [] self.pressed_actions = []
self.stop_autorepeat() self.stop_autorepeat()
def resume(self, delta_time=0): def resume(self):
self.status = Status.PLAYING self.status = Status.PLAYING
self.schedule(self.drop, self.fall_delay) self.scheduler.start(self.drop, self.fall_delay)
if self.current_piece.prelocked: if self.current_piece.prelocked:
self.schedule(self.lock, self.lock_delay) self.scheduler.start(self.lock, self.lock_delay)
self.schedule(self.clock, 1) self.scheduler.start(self.clock, 1)
def clock(self, delta_time=1): def clock(self, delta_time=1):
self.time += delta_time self.time += delta_time
def game_over(self): def game_over(self):
self.status = Status.OVER self.status = Status.OVER
self.unschedule(self.lock) self.scheduler.stop(self.drop)
self.unschedule(self.drop) self.scheduler.stop(self.clock)
self.unschedule(self.clock)
def schedule(task, period):
raise NotImplementedError
def unschedule(self, task):
raise NotImplementedError
def show_text(self, text): def show_text(self, text):
print(text) print(text)