Compare commits
11
Commits
0.2
...
f013a061b2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f013a061b2 | ||
|
|
f025ad5fd8 | ||
|
|
9a7aead918 | ||
|
|
b173b6ff73 | ||
|
|
ddf7ea0f4e | ||
|
|
d308618556 | ||
|
|
9c77096bfb | ||
|
|
093264c351 | ||
|
|
5b3e6ec931 | ||
|
|
0970e1f6df | ||
|
|
f48f1fc000 |
No files matched your search
+112
-77
@@ -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:
|
||||
for tetromino in (self.matrix.piece, self.matrix.ghost):
|
||||
tetromino.sprites.refresh()
|
||||
return 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()
|
||||
|
||||
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.
@@ -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],
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user