11 Commits
Author SHA1 Message Date
adrien f013a061b2 fix line remove 2019-10-04 18:03:35 +02:00
adrien f025ad5fd8 change tetrislogic api 2019-10-04 17:48:47 +02:00
adrien 9a7aead918 fix (at last?) music codec warning 2019-10-04 16:15:54 +02:00
adrien b173b6ff73 Warn on music codec not found 2019-10-04 16:07:40 +02:00
adrien ddf7ea0f4e Warn on music codec not found 2019-10-04 15:54:06 +02:00
adrien d308618556 music warning 2019-10-04 12:51:11 +02:00
adrien 9c77096bfb Don't play music if codec not found 2019-10-04 12:24:49 +02:00
adrienmalin 093264c351 enable level choice 2019-10-04 01:23:47 +02:00
adrienmalin 5b3e6ec931 fix setup.py 2019-10-03 21:34:55 +02:00
adrienmalin 0970e1f6df V0.3 Release
New musics
Piece lock optimize
2019-10-03 21:33:39 +02:00
adrien f48f1fc000 redefine consts in TetrArcade.py 2019-10-03 18:14:07 +02:00
16 changed files with 323 additions and 209 deletions

No files matched your search

+110 -75
View File
@@ -1,26 +1,42 @@
# -*- coding: utf-8 -*-
import sys
import locale
import time
import os
import configparser
try:
import arcade
except ImportError as e:
sys.exit(
str(e)
+ """
str(e) + """
This game require arcade library.
You can install it with:
python -m pip install --user arcade"""
)
import pyglet
from tetrislogic import TetrisLogic, Color, State
import locale
import time
import os
import itertools
import configparser
from tetrislogic import TetrisLogic, Color, State, Coord
# Constants
# Matrix
NB_LINES = 20
NB_COLS = 10
NB_NEXT = 5
# Delays (seconds)
LOCK_DELAY = 0.5
FALL_DELAY = 1
AUTOREPEAT_DELAY = 0.300
AUTOREPEAT_PERIOD = 0.010
# Piece init coord
MATRIX_PIECE_COORD = Coord(4, NB_LINES)
NEXT_PIECE_COORDS = [Coord(NB_COLS + 4, NB_LINES - 4 * n - 3) for n in range(NB_NEXT)]
HELD_PIECE_COORD = Coord(-5, NB_LINES - 3)
# Window
WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600
@@ -45,19 +61,19 @@ MINO_SPRITE_SIZE = 21
if getattr(sys, 'frozen', False):
# The application is frozen
DATA_DIR = os.path.dirname(sys.executable)
PROGRAM_DIR = os.path.dirname(sys.executable)
else:
# The application is not frozen
# Change this bit to match where you store your data files:
DATA_DIR = os.path.dirname(__file__)
DATA_DIR = os.path.join(DATA_DIR, "res")
PROGRAM_DIR = os.path.dirname(__file__)
RESOURCES_DIR = os.path.join(PROGRAM_DIR, "resources")
# Sprites
WINDOW_BG_PATH = os.path.join(DATA_DIR, "bg.jpg")
MATRIX_BG_PATH = os.path.join(DATA_DIR, "matrix.png")
HELD_BG_PATH = os.path.join(DATA_DIR, "held.png")
NEXT_BG_PATH = os.path.join(DATA_DIR, "next.png")
MINOES_SPRITES_PATH = os.path.join(DATA_DIR, "minoes.png")
IMAGES_DIR = os.path.join(RESOURCES_DIR, "images")
WINDOW_BG_PATH = os.path.join(IMAGES_DIR, "bg.jpg")
MATRIX_BG_PATH = os.path.join(IMAGES_DIR, "matrix.png")
HELD_BG_PATH = os.path.join(IMAGES_DIR, "held.png")
NEXT_BG_PATH = os.path.join(IMAGES_DIR, "next.png")
MINOES_SPRITES_PATH = os.path.join(IMAGES_DIR, "minoes.png")
Color.PRELOCKED = 7
MINOES_COLOR_ID = {
Color.BLUE: 0,
@@ -74,6 +90,19 @@ TEXTURES = arcade.load_textures(
)
TEXTURES = {color: TEXTURES[i] for color, i in MINOES_COLOR_ID.items()}
# Music
MUSIC_DIR = os.path.join(RESOURCES_DIR, "musics")
MUSICS_PATHS = (entry.path for entry in os.scandir(MUSIC_DIR))
# Text
TEXT_COLOR = arcade.color.BUBBLES
FONT_NAME = os.path.join(RESOURCES_DIR, "fonts/joystix monospace.ttf")
STATS_TEXT_MARGIN = 40
STATS_TEXT_SIZE = 14
STATS_TEXT_WIDTH = 150
HIGHLIGHT_TEXT_COLOR = arcade.color.BUBBLES
HIGHLIGHT_TEXT_SIZE = 20
# User profile path
if sys.platform == "win32":
USER_PROFILE_DIR = os.environ.get("appdata", os.path.expanduser("~\Appdata\Roaming"))
@@ -83,20 +112,9 @@ USER_PROFILE_DIR = os.path.join(USER_PROFILE_DIR, "TetrArcade")
HIGH_SCORE_PATH = os.path.join(USER_PROFILE_DIR, ".high_score")
CONF_PATH = os.path.join(USER_PROFILE_DIR, "TetrArcade.ini")
# Text
TEXT_COLOR = arcade.color.BUBBLES
FONT_NAME = os.path.join(DATA_DIR, "joystix monospace.ttf")
STATS_TEXT_MARGIN = 40
STATS_TEXT_SIZE = 14
STATS_TEXT_WIDTH = 150
HIGHLIGHT_TEXT_COLOR = arcade.color.BUBBLES
HIGHLIGHT_TEXT_SIZE = 20
# Music
MUSIC_PATH = os.path.join(DATA_DIR, "Tetris - Song A.mp3")
class MinoSprite(arcade.Sprite):
def __init__(self, mino, window, alpha):
super().__init__()
self.alpha = alpha
@@ -114,6 +132,7 @@ class MinoSprite(arcade.Sprite):
class MinoesSprites(arcade.SpriteList):
def resize(self, scale):
for sprite in self:
sprite.scale = scale
@@ -121,6 +140,7 @@ class MinoesSprites(arcade.SpriteList):
class TetrominoSprites(MinoesSprites):
def __init__(self, tetromino, window, alpha=NORMAL_ALPHA):
super().__init__()
self.tetromino = tetromino
@@ -136,6 +156,7 @@ class TetrominoSprites(MinoesSprites):
class MatrixSprites(MinoesSprites):
def __init__(self, matrix):
super().__init__()
self.matrix = matrix
@@ -146,10 +167,15 @@ class MatrixSprites(MinoesSprites):
for x, mino in enumerate(line):
if mino:
mino.sprite.refresh(x, y)
self.append(mino.sprite)
def remove_line(self, y):
for mino in self.matrix[y]:
if mino:
self.remove(mino.sprite)
class TetrArcade(TetrisLogic, arcade.Window):
def __init__(self):
locale.setlocale(locale.LC_ALL, "")
self.highlight_texts = []
@@ -166,7 +192,7 @@ class TetrArcade(TetrisLogic, arcade.Window):
self.new_conf()
self.load_conf()
super().__init__()
super().__init__(NB_LINES, NB_COLS, NB_NEXT)
arcade.Window.__init__(
self,
width=self.init_width,
@@ -188,9 +214,18 @@ class TetrArcade(TetrisLogic, arcade.Window):
self.next.bg.alpha = BAR_ALPHA
self.matrix.sprites = MatrixSprites(self.matrix)
self.on_resize(self.init_width, self.init_height)
if self.play_music:
self.music = arcade.Sound(MUSIC_PATH)
self.music_player = None
try:
self.music = pyglet.media.Player()
playlist = itertools.cycle(
pyglet.media.load(path)
for path in MUSICS_PATHS
)
self.music.queue(playlist)
except:
Warning("Can't play music.")
self.play_music = False
def new_conf(self):
self.conf["WINDOW"] = {"width": WINDOW_WIDTH, "height": WINDOW_HEIGHT, "fullscreen": False}
@@ -274,42 +309,47 @@ AGAIN""".format(
self.play_music = self.conf["MUSIC"].getboolean("play")
def new_game(self):
def on_new_game(self):
self.highlight_texts = []
super().new_game()
if self.play_music:
if self.music_player:
self.music_player.seek(0)
self.music_player.play()
else:
self.music_player = self.music.player.play()
self.music_player.loop = True
def new_tetromino(self):
tetromino = super().new_tetromino()
tetromino.sprites = TetrominoSprites(tetromino, self)
return tetromino
def new_matrix_piece(self):
self.matrix.sprites = MatrixSprites(self.matrix)
super().new_matrix_piece()
if self.play_music:
self.music.seek(0)
self.music.play()
def on_new_piece(self, piece):
piece.sprites = TetrominoSprites(piece, self)
def on_new_level(self, level):
self.show_text("LEVEL\n{:n}".format(level))
def on_generation_phase(self, piece):
self.matrix.sprites.refresh()
self.matrix.ghost.sprites = TetrominoSprites(self.matrix.ghost, self, GHOST_ALPHA)
for tetromino in [self.matrix.piece, self.matrix.ghost] + self.next.pieces:
tetromino.sprites.refresh()
def move(self, movement, prelock=True):
moved = super().move(movement, prelock)
def on_falling_phase(self):
self.matrix.piece.sprites.refresh()
if moved:
self.matrix.ghost.sprites.refresh()
return moved
def rotate(self, rotation):
rotated = super().rotate(rotation)
if rotated:
def on_moved(self, moved):
self.matrix.piece.sprites.refresh()
self.matrix.ghost.sprites.refresh()
def on_rotated(self, direction):
for tetromino in (self.matrix.piece, self.matrix.ghost):
tetromino.sprites.refresh()
return rotated
def on_lock_phase(self):
self.matrix.piece.sprites.refresh()
self.matrix.sprites.refresh()
def on_locked(self, piece):
piece.sprites.refresh()
for mino in piece:
self.matrix.sprites.append(mino.sprite)
def on_line_remove(self, y):
self.matrix.sprites.remove_line(y)
def swap(self):
super().swap()
@@ -318,25 +358,20 @@ AGAIN""".format(
if tetromino:
tetromino.sprites.refresh()
def lock(self):
self.matrix.piece.prelocked = False
self.matrix.piece.sprites.refresh()
super().lock()
def pause(self):
super().pause()
if self.play_music:
self.music_player.pause()
self.music.pause()
def resume(self):
super().resume()
if self.play_music:
self.music_player.play()
self.music.play()
def game_over(self):
super().game_over()
if self.play_music:
self.music_player.pause()
self.music.pause()
def on_key_press(self, key, modifiers):
for key_or_modifier in (key, modifiers):
@@ -380,7 +415,7 @@ AGAIN""".format(
if tetromino:
tetromino.sprites.draw()
t = time.localtime(self.time)
t = time.localtime(self.stats.time)
font_size = STATS_TEXT_SIZE * self.scale
for y, text in enumerate(("TIME", "LINES", "GOAL", "LEVEL", "HIGH SCORE", "SCORE")):
arcade.draw_text(
@@ -396,11 +431,11 @@ AGAIN""".format(
for y, text in enumerate(
(
"{:02d}:{:02d}:{:02d}".format(t.tm_hour - 1, t.tm_min, t.tm_sec),
"{:n}".format(self.nb_lines_cleared),
"{:n}".format(self.goal),
"{:n}".format(self.level),
"{:n}".format(self.high_score),
"{:n}".format(self.score),
"{:n}".format(self.stats.lines_cleared),
"{:n}".format(self.stats.goal),
"{:n}".format(self.stats.level),
"{:n}".format(self.stats.high_score),
"{:n}".format(self.stats.score),
)
):
arcade.draw_text(
@@ -521,7 +556,7 @@ High score could not be saved:
def on_close(self):
self.save_high_score()
if self.play_music:
self.music_player.pause()
self.music.pause()
super().on_close()
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -29,13 +29,13 @@ options = {
"build_exe": {
"packages": ["arcade", "pyglet"],
"excludes": excludes,
"include_files": "res",
"include_files": "resources",
"silent": True
}
}
setup(
name = "TetrArcade",
version = "0.2",
version = "0.3",
description = "Tetris clone",
author = "AdrienMalin",
executables = [executable],
+10 -1
View File
@@ -1,16 +1,25 @@
# -*- coding: utf-8 -*-
from TetrArcade import TetrArcade, State
from TetrArcade import TetrArcade, State, MinoSprite
from tetrislogic import Mino, Color, Coord
game = TetrArcade()
game.new_game()
for x in range(game.matrix.collumns):
mino = Mino(Color.ORANGE, Coord(x, 0))
mino.sprite = MinoSprite(mino, game, 200)
game.matrix[0][x] = mino
game.matrix.sprites.append(mino.sprite)
game.move_left()
game.pause()
game.resume()
game.move_right()
game.swap()
game.rotate_clockwise()
game.rotate_counter()
for i in range(12):
game.soft_drop()
game.matrix.sprites.refresh()
game.on_draw()
while game.state != State.OVER:
game.hard_drop()
+2 -2
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from .consts import NB_LINES, NB_COLS, NB_NEXT
from .utils import Movement, Rotation, Color
from .consts import LINES, COLLUMNS, NEXT_PIECES
from .utils import Movement, Rotation, Color, Coord
from .tetromino import Mino, Tetromino
from .tetrislogic import TetrisLogic, State, Matrix
+8 -9
View File
@@ -3,18 +3,17 @@ from .utils import Coord
# Matrix
NB_LINES = 20
NB_COLS = 10
NB_NEXT = 5
LINES = 20
COLLUMNS = 10
NEXT_PIECES = 5
# Delays (seconds)
LOCK_DELAY = 0.5
FALL_DELAY = 1
AUTOREPEAT_DELAY = 0.200 # Official : 0.300
AUTOREPEAT_PERIOD = 0.010 # Official : 0.010
AUTOREPEAT_DELAY = 0.300 # Official : 0.300 s
AUTOREPEAT_PERIOD = 0.010 # Official : 0.010 s
# Piece init coord
CURRENT_COORD = Coord(4, NB_LINES)
NEXT_COORDS = [Coord(NB_COLS + 4, NB_LINES - 4 * n - 3) for n in range(NB_NEXT)]
HELD_COORD = Coord(-5, NB_LINES - 3)
MATRIX_PIECE_COORD = Coord(4, LINES)
NEXT_PIECE_COORDS = [Coord(COLLUMNS + 4, LINES - 4 * n - 3) for n in range(NEXT_PIECES)]
HELD_PIECE_COORD = Coord(-5, LINES - 3)
+191 -120
View File
@@ -5,16 +5,16 @@ import pickle
from .utils import Coord, Movement, Rotation, T_Spin
from .tetromino import Tetromino, T_Tetrimino, I_Tetrimino
from .consts import (
NB_LINES,
NB_COLS,
NB_NEXT,
LINES,
COLLUMNS,
NEXT_PIECES,
LOCK_DELAY,
FALL_DELAY,
AUTOREPEAT_DELAY,
AUTOREPEAT_PERIOD,
CURRENT_COORD,
NEXT_COORDS,
HELD_COORD,
MATRIX_PIECE_COORD,
NEXT_PIECE_COORDS,
HELD_PIECE_COORD,
)
@@ -42,88 +42,58 @@ class HoldQueue(PieceContainer):
class Matrix(list, PieceContainer):
def __init__(self, *args, **kargs):
list.__init__(self, *args, **kargs)
def __init__(self, lines, collumns):
list.__init__(self)
PieceContainer.__init__(self)
self.lines = lines
self.collumns = collumns
def reset(self):
self.clear()
for y in range(self.lines + 3):
self.append_new_line()
def append_new_line(self):
self.append([None for x in range(self.collumns)])
def cell_is_free(self, coord):
return 0 <= coord.x < NB_COLS and 0 <= coord.y and not self[coord.y][coord.x]
return 0 <= coord.x < self.collumns and 0 <= coord.y and not self[coord.y][coord.x]
class NextQueue(PieceContainer):
def __init__(self):
def __init__(self, nb_pieces):
super().__init__()
self.nb_pieces = nb_pieces
self.pieces = []
class TetrisLogic:
class Stats:
NB_LINES = NB_LINES
NB_COLS = NB_COLS
NB_NEXT = NB_NEXT
LOCK_DELAY = LOCK_DELAY
FALL_DELAY = FALL_DELAY
AUTOREPEAT_DELAY = AUTOREPEAT_DELAY
AUTOREPEAT_PERIOD = AUTOREPEAT_PERIOD
CURRENT_COORD = CURRENT_COORD
NEXT_COORDS = NEXT_COORDS
HELD_COORD = HELD_COORD
random_bag = []
def __init__(self):
self.load_high_score()
self.state = State.STARTING
self.held = HoldQueue()
self.matrix = Matrix()
self.matrix.ghost = None
self.next = NextQueue()
self.time = 0
self.autorepeatable_actions = (self.move_left, self.move_right, self.soft_drop)
self.pressed_actions = []
self._score = 0
def get_score(self):
def _get_score(self):
return self._score
def set_score(self, new_score):
def _set_score(self, new_score):
self._score = new_score
if self._score > self.high_score:
self.high_score = self._score
score = property(get_score, set_score)
score = property(_get_score, _set_score)
def new_game(self):
self.level = 0
self.score = 0
self.nb_lines_cleared = 0
self.goal = 0
def __init__(self):
self._score = 0
self.time = 0
self.pressed_actions = []
self.auto_repeat = False
def new_game(self, level):
self.level = level - 1
self.score = 0
self.lines_cleared = 0
self.goal = 0
self.time = 0
self.combo = -1
self.lock_delay = self.LOCK_DELAY
self.fall_delay = self.FALL_DELAY
self.matrix.clear()
for y in range(self.NB_LINES + 3):
self.append_new_line_to_matrix()
self.next.pieces = [self.new_tetromino() for n in range(self.NB_NEXT)]
self.held.piece = None
self.state = State.PLAYING
self.start(self.update_time, 1)
self.new_level()
def new_tetromino(self):
if not self.random_bag:
self.random_bag = list(Tetromino.shapes)
random.shuffle(self.random_bag)
return self.random_bag.pop()()
def append_new_line_to_matrix(self):
self.matrix.append([None for x in range(self.NB_COLS)])
self.lock_delay = LOCK_DELAY
self.fall_delay = FALL_DELAY
def new_level(self):
self.level += 1
@@ -132,23 +102,102 @@ class TetrisLogic:
self.fall_delay = pow(0.8 - ((self.level - 1) * 0.007), self.level - 1)
if self.level > 15:
self.lock_delay = 0.5 * pow(0.9, self.level - 15)
self.show_text("LEVEL\n{:n}".format(self.level))
self.restart(self.fall, self.fall_delay)
self.new_matrix_piece()
def new_matrix_piece(self):
def update_time(self):
self.time += 1
class TetrisLogic:
LINES = LINES
COLLUMNS = COLLUMNS
NEXT_PIECES = NEXT_PIECES
AUTOREPEAT_DELAY = AUTOREPEAT_DELAY
AUTOREPEAT_PERIOD = AUTOREPEAT_PERIOD
MATRIX_PIECE_COORD = MATRIX_PIECE_COORD
NEXT_PIECE_COORDS = NEXT_PIECE_COORDS
HELD_PIECE_COORD = HELD_PIECE_COORD
random_bag = []
def __init__(
self,
lines=LINES,
collumns=COLLUMNS,
next_pieces=NEXT_PIECES,
):
self.stats = Stats()
self.load_high_score()
self.state = State.STARTING
self.held = HoldQueue()
self.matrix = Matrix(lines, collumns)
self.matrix.ghost = None
self.next = NextQueue(next_pieces)
self.autorepeatable_actions = (self.move_left, self.move_right, self.soft_drop)
self.pressed_actions = []
def new_game(self, level=1):
self.stats.new_game(level)
self.pressed_actions = []
self.auto_repeat = False
self.matrix.reset()
self.next.pieces = [self.new_piece() for n in range(self.next.nb_pieces)]
self.held.piece = None
self.state = State.PLAYING
self.start(self.stats.update_time, 1)
self.on_new_game()
self.new_level()
def on_new_game(self):
pass
def new_piece(self):
if not self.random_bag:
self.random_bag = list(Tetromino.shapes)
random.shuffle(self.random_bag)
piece = self.random_bag.pop()()
self.on_new_piece(piece)
return piece
def on_new_piece(self, piece):
pass
def new_level(self):
self.stats.new_level()
self.restart(self.fall, self.stats.fall_delay)
self.on_new_level(self.stats.level)
self.generation_phase()
def on_new_level(self, level):
pass
def generation_phase(self):
self.matrix.piece = self.next.pieces.pop(0)
self.matrix.piece.coord = self.CURRENT_COORD
self.matrix.piece.coord = self.MATRIX_PIECE_COORD
self.matrix.ghost = self.matrix.piece.ghost()
self.move_ghost()
self.next.pieces.append(self.new_tetromino())
self.next.pieces[-1].coord = self.NEXT_COORDS[-1]
for tetromino, coord in zip(self.next.pieces, self.NEXT_COORDS):
self.next.pieces.append(self.new_piece())
self.next.pieces[-1].coord = self.NEXT_PIECE_COORDS[-1]
for tetromino, coord in zip(self.next.pieces, self.NEXT_PIECE_COORDS):
tetromino.coord = coord
self.on_generation_phase(self.matrix.piece)
self.on_falling_phase()
if not self.can_move(self.matrix.piece.coord, (mino.coord for mino in self.matrix.piece)):
self.game_over()
def on_generation_phase(self, piece):
pass
def on_falling_phase(self):
pass
def move_left(self):
self.move(Movement.LEFT)
@@ -171,12 +220,12 @@ class TetrisLogic:
def soft_drop(self):
moved = self.move(Movement.DOWN)
if moved:
self.score += 1
self.stats.score += 1
return moved
def hard_drop(self):
while self.move(Movement.DOWN, prelock=False):
self.score += 2
self.stats.score += 2
self.lock()
def fall(self):
@@ -186,35 +235,47 @@ class TetrisLogic:
potential_coord = self.matrix.piece.coord + movement
if self.can_move(potential_coord, (mino.coord for mino in self.matrix.piece)):
if self.matrix.piece.prelocked:
self.restart(self.lock, self.lock_delay)
self.restart(self.lock, self.stats.lock_delay)
self.matrix.piece.coord = potential_coord
if not movement == Movement.DOWN:
self.matrix.piece.last_rotation_point = None
self.move_ghost()
self.on_moved(movement)
return True
else:
if prelock and not self.matrix.piece.prelocked and movement == Movement.DOWN:
self.matrix.piece.prelocked = True
self.start(self.lock, self.lock_delay)
self.start(self.lock, self.stats.lock_delay)
self.on_lock_phase()
return False
def on_moved(self, movement):
pass
def rotate(self, rotation):
rotated_coords = tuple(Coord(rotation * mino.coord.y, -rotation * mino.coord.x) for mino in self.matrix.piece)
for rotation_point, liberty_degree in enumerate(self.matrix.piece.SRS[rotation][self.matrix.piece.orientation], start=1):
potential_coord = self.matrix.piece.coord + liberty_degree
if self.can_move(potential_coord, rotated_coords):
if self.matrix.piece.prelocked:
self.restart(self.lock, self.lock_delay)
self.restart(self.lock, self.stats.lock_delay)
self.matrix.piece.coord = potential_coord
for mino, coord in zip(self.matrix.piece, rotated_coords):
mino.coord = coord
self.matrix.piece.orientation = (self.matrix.piece.orientation + rotation) % 4
self.matrix.piece.last_rotation_point = rotation_point
self.move_ghost()
self.on_rotated(rotation)
return True
else:
return False
def on_rotated(self, direction):
pass
def on_lock_phase(self):
pass
SCORES = (
{LINES_CLEAR_NAME: "", T_Spin.NONE: 0, T_Spin.MINI: 1, T_Spin.T_SPIN: 4},
{LINES_CLEAR_NAME: "SINGLE", T_Spin.NONE: 1, T_Spin.MINI: 2, T_Spin.T_SPIN: 8},
@@ -232,14 +293,23 @@ class TetrisLogic:
return
# Game over
if all((mino.coord + self.matrix.piece.coord).y >= self.NB_LINES for mino in self.matrix.piece):
if all((mino.coord + self.matrix.piece.coord).y >= self.matrix.lines for mino in self.matrix.piece):
self.game_over()
return
if self.pressed_actions:
self.auto_repeat = False
self.restart(self.repeat_action, self.AUTOREPEAT_DELAY)
self.stop(self.repeat_action)
for mino in self.matrix.piece:
coord = mino.coord + self.matrix.piece.coord
if coord.y <= self.matrix.lines + 3:
self.matrix[coord.y][coord.x] = mino
self.on_locked(self.matrix.piece)
self.pattern_phase()
def pattern_phase(self):
# T-Spin
if type(self.matrix.piece) == T_Tetrimino and self.matrix.piece.last_rotation_point is not None:
a = self.is_t_slot(0)
@@ -255,21 +325,16 @@ class TetrisLogic:
else:
t_spin = T_Spin.NONE
for mino in self.matrix.piece:
coord = mino.coord + self.matrix.piece.coord
del mino.coord
if coord.y <= self.NB_LINES + 3:
self.matrix[coord.y][coord.x] = mino
# Clear complete lines
nb_lines_cleared = 0
lines_cleared = 0
for y, line in reversed(list(enumerate(self.matrix))):
if all(mino for mino in line):
nb_lines_cleared += 1
lines_cleared += 1
self.on_line_remove(y)
self.matrix.pop(y)
self.append_new_line_to_matrix()
if nb_lines_cleared:
self.nb_lines_cleared += nb_lines_cleared
self.matrix.append_new_line()
if lines_cleared:
self.stats.lines_cleared += lines_cleared
# Scoring
lock_strings = []
@@ -277,31 +342,40 @@ class TetrisLogic:
if t_spin:
lock_strings.append(t_spin)
if nb_lines_cleared:
lock_strings.append(self.SCORES[nb_lines_cleared][LINES_CLEAR_NAME])
self.combo += 1
if lines_cleared:
lock_strings.append(self.SCORES[lines_cleared][LINES_CLEAR_NAME])
self.stats.combo += 1
else:
self.combo = -1
self.stats.combo = -1
if nb_lines_cleared or t_spin:
ds = self.SCORES[nb_lines_cleared][t_spin]
self.goal -= ds
ds *= 100 * self.level
if lines_cleared or t_spin:
ds = self.SCORES[lines_cleared][t_spin]
self.stats.goal -= ds
ds *= 100 * self.stats.level
lock_score += ds
lock_strings.append(str(ds))
self.show_text("\n".join(lock_strings))
if self.combo >= 1:
ds = (20 if nb_lines_cleared == 1 else 50) * self.combo * self.level
if self.stats.combo >= 1:
ds = (20 if lines_cleared == 1 else 50) * self.stats.combo * self.stats.level
lock_score += ds
self.show_text("COMBO x{:n}\n{:n}".format(self.combo, ds))
self.show_text("COMBO x{:n}\n{:n}".format(self.stats.combo, ds))
self.score += lock_score
self.stats.score += lock_score
if self.goal <= 0:
if self.stats.goal <= 0:
self.new_level()
else:
self.new_matrix_piece()
self.generation_phase()
if self.pressed_actions:
self.start(self.repeat_action, self.AUTOREPEAT_DELAY)
def on_locked(piece):
pass
def on_line_remove(self, y):
pass
def can_move(self, potential_coord, minoes_coords):
return all(self.matrix.cell_is_free(potential_coord + mino_coord) for mino_coord in minoes_coords)
@@ -318,18 +392,18 @@ class TetrisLogic:
self.matrix.piece.prelocked = False
self.stop(self.lock)
self.matrix.piece, self.held.piece = self.held.piece, self.matrix.piece
self.held.piece.coord = self.HELD_COORD
self.held.piece.coord = self.HELD_PIECE_COORD
if type(self.held.piece) == I_Tetrimino:
self.held.piece.coord += Movement.LEFT
for mino, coord in zip(self.held.piece, self.held.piece.MINOES_COORDS):
mino.coord = coord
if self.matrix.piece:
self.matrix.piece.coord = self.CURRENT_COORD
self.matrix.piece.coord = self.MATRIX_PIECE_COORD
self.matrix.ghost = self.matrix.piece.ghost()
self.move_ghost()
else:
self.new_matrix_piece()
self.generation_phase()
def pause(self):
self.state = State.PAUSED
@@ -340,10 +414,10 @@ class TetrisLogic:
def resume(self):
self.state = State.PLAYING
self.start(self.fall, self.fall_delay)
self.start(self.fall, self.stats.fall_delay)
if self.matrix.piece.prelocked:
self.start(self.lock, self.lock_delay)
self.start(self.update_time, 1)
self.start(self.lock, self.stats.lock_delay)
self.start(self.stats.update_time, 1)
def game_over(self):
self.state = State.OVER
@@ -353,10 +427,7 @@ class TetrisLogic:
def stop_all(self):
self.stop(self.fall)
self.stop(self.lock)
self.stop(self.update_time)
def update_time(self):
self.time += 1
self.stop(self.stats.update_time)
def do_action(self, action):
action()
@@ -364,7 +435,7 @@ class TetrisLogic:
self.auto_repeat = False
self.pressed_actions.append(action)
if action == self.soft_drop:
delay = self.fall_delay / 20
delay = self.stats.fall_delay / 20
else:
delay = self.AUTOREPEAT_DELAY
self.restart(self.repeat_action, delay)
@@ -393,16 +464,16 @@ class TetrisLogic:
def load_high_score(self, crypted_high_score=None):
if crypted_high_score:
crypted_high_score = int(pickle.loads(crypted_high_score))
self.high_score = crypted_high_score ^ CRYPT_KEY
self.stats.high_score = crypted_high_score ^ CRYPT_KEY
else:
raise Warning(
"""TetrisLogic.load_high_score not implemented.
High score is set to 0"""
)
self.high_score = 0
self.stats.high_score = 0
def save_high_score(self):
crypted_high_score = self.high_score ^ CRYPT_KEY
crypted_high_score = self.stats.high_score ^ CRYPT_KEY
crypted_high_score = pickle.dumps(crypted_high_score)
return crypted_high_score