Compare commits

..

3 Commits

Author SHA1 Message Date
adrienmalin
80d639fd63 display combos after t-spin/lines cleard 2019-09-29 11:46:52 +02:00
adrienmalin
f0971cd030 ignore nuikta build and dist 2019-09-29 11:31:36 +02:00
adrienmalin
6866d63a32 optimize lock 2019-09-29 11:29:40 +02:00
3 changed files with 122 additions and 101 deletions

2
.gitignore vendored
View File

@ -25,6 +25,8 @@ wheels/
.installed.cfg
*.egg
MANIFEST
Tetrarcade.build/
Tetrarcade.dist/
# PyInstaller
# Usually these files are written by a python script from a template

View File

@ -14,7 +14,7 @@ python -m pip install --user arcade
"""
)
from tetrislogic import TetrisLogic, State, AbstractScheduler
from tetrislogic import TetrisLogic, State, AbstractScheduler, NB_LINES
# Constants
@ -34,19 +34,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 +59,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 +112,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 +179,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 +205,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 +299,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 +308,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 +397,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:

View File

@ -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
@ -406,15 +409,13 @@ class TetrisLogic():
ds *= 100 * self.level
lock_score += ds
lock_strings.append(str(ds))
self.show_text("\n".join(lock_strings))
if self.combo >= 1:
lock_strings.append("COMBO x%d" % self.combo)
ds = (20 if nb_lines_cleared==1 else 50) * self.combo * self.level
lock_score += ds
lock_strings.append(str(ds))
if lock_strings:
self.show_text("\n".join(lock_strings))
self.show_text("COMBO x{:n}\n{:n}".format(self.combo, ds))
self.add_to_score(lock_score)
@ -423,6 +424,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 +492,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 +509,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 +526,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 +536,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.")