mirror of
https://github.com/kristoferssolo/Tetris.git
synced 2025-10-21 20:00:35 +00:00
refactor(game): pass mypy
This commit is contained in:
parent
bb80400ad7
commit
0afe1ed3cb
4
main.py
4
main.py
@ -67,10 +67,10 @@ def main(args: argparse.ArgumentParser) -> None:
|
||||
if args.train is not None:
|
||||
ai.log.debug("Training the AI")
|
||||
# ai.train(*args.train)
|
||||
game.Main(GameMode.AI_TRAINING).run()
|
||||
game.Game(GameMode.AI_TRAINING).run()
|
||||
else:
|
||||
game.log.debug("Running the game")
|
||||
game.Main(GameMode.PLAYER).run()
|
||||
game.Game(GameMode.PLAYER).run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -1,20 +1,9 @@
|
||||
from .block import Block
|
||||
from .game import Game
|
||||
from .log import log
|
||||
from .main import Main
|
||||
from .preview import Preview
|
||||
from .score import Score
|
||||
from .tetromino import Tetromino
|
||||
from .timer import Timer, Timers
|
||||
from .screens import Game, Menu, Tetris
|
||||
|
||||
__all__ = [
|
||||
"log",
|
||||
"Main",
|
||||
"Game",
|
||||
"Score",
|
||||
"Block",
|
||||
"Tetromino",
|
||||
"Preview",
|
||||
"Timer",
|
||||
"Timers",
|
||||
]
|
||||
|
||||
7
src/game/screens/__init__.py
Normal file
7
src/game/screens/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from .game import Game
|
||||
from .menu import Menu
|
||||
from .preview import Preview
|
||||
from .score import Score
|
||||
from .tetris import Tetris
|
||||
|
||||
__all__ = ["Tetris", "Game", "Preview", "Score", "Menu"]
|
||||
57
src/game/screens/base.py
Normal file
57
src/game/screens/base.py
Normal file
@ -0,0 +1,57 @@
|
||||
from abc import ABC, ABCMeta, abstractmethod
|
||||
|
||||
import pygame
|
||||
|
||||
|
||||
class BaseScreen(ABC, metaclass=ABCMeta):
|
||||
"""Base screen class."""
|
||||
|
||||
@abstractmethod
|
||||
def update(self, *args, **kwargs) -> None:
|
||||
"""Update the screen."""
|
||||
|
||||
@abstractmethod
|
||||
def draw(self, *args, **kwargs) -> None:
|
||||
"""Draw the screen."""
|
||||
|
||||
@abstractmethod
|
||||
def run(self, *args, **kwargs) -> None:
|
||||
"""Run the screen."""
|
||||
|
||||
|
||||
class SceenElement(ABC, metaclass=ABCMeta):
|
||||
@abstractmethod
|
||||
def _draw_background(self) -> None:
|
||||
"""Draw the background."""
|
||||
|
||||
@abstractmethod
|
||||
def _draw_border(self) -> None:
|
||||
"""Draw the border."""
|
||||
|
||||
@abstractmethod
|
||||
def _initialize_surface(self) -> None:
|
||||
"""Initialize the surface."""
|
||||
|
||||
@abstractmethod
|
||||
def _initialize_rect(self) -> None:
|
||||
"""Initialize the rectangle."""
|
||||
|
||||
@abstractmethod
|
||||
def _update_diplaysurface(self) -> None:
|
||||
"""Update the display surface."""
|
||||
|
||||
|
||||
class TextScreen(ABC, metaclass=ABCMeta):
|
||||
"""Base screen class for text."""
|
||||
|
||||
@abstractmethod
|
||||
def _initialize_font(self) -> None:
|
||||
"""Initialize the font."""
|
||||
|
||||
@abstractmethod
|
||||
def _draw_text(self) -> None:
|
||||
"""Draw the text on the surface."""
|
||||
|
||||
@abstractmethod
|
||||
def _display_text(self, *args, **kwargs) -> None:
|
||||
"""Display the text."""
|
||||
@ -3,16 +3,18 @@ import sys
|
||||
import pygame
|
||||
from utils import CONFIG, Figure, GameMode
|
||||
|
||||
from .game import Game
|
||||
from .log import log
|
||||
from game.log import log
|
||||
|
||||
from .base import BaseScreen
|
||||
from .preview import Preview
|
||||
from .score import Score
|
||||
from .tetris import Tetris
|
||||
from .tetromino import Tetromino
|
||||
|
||||
|
||||
class Main:
|
||||
class Game(BaseScreen):
|
||||
"""
|
||||
Main class for the game.
|
||||
Game class.
|
||||
|
||||
Attributes:
|
||||
display_surface: Pygame display surface.
|
||||
@ -46,11 +48,12 @@ class Main:
|
||||
self.draw()
|
||||
self.handle_events()
|
||||
|
||||
self.game.run()
|
||||
self.tetris.run()
|
||||
self.score.run()
|
||||
self.preview.run(self.next_figure)
|
||||
self.preview.update(self.next_figure)
|
||||
self.preview.run()
|
||||
|
||||
pygame.display.update()
|
||||
self.draw()
|
||||
self.clock.tick(CONFIG.fps)
|
||||
|
||||
def handle_events(self) -> None:
|
||||
@ -71,14 +74,14 @@ class Main:
|
||||
"""Initialize game-related components."""
|
||||
self.next_figure: Figure = self._generate_next_figure()
|
||||
|
||||
self.game = Game(self._get_next_figure, self._update_score)
|
||||
self.tetris = Tetris(self._get_next_figure, self._update_score)
|
||||
self.score = Score()
|
||||
self.preview = Preview()
|
||||
|
||||
def mute(self) -> None:
|
||||
"""Mute the game."""
|
||||
self.music.set_volume(0)
|
||||
self.game.mute()
|
||||
self.tetris.mute()
|
||||
|
||||
def _update_score(self, lines: int, score: int, level: int) -> None:
|
||||
"""
|
||||
7
src/game/screens/menu.py
Normal file
7
src/game/screens/menu.py
Normal file
@ -0,0 +1,7 @@
|
||||
from game.log import log
|
||||
|
||||
from .base import BaseScreen
|
||||
|
||||
|
||||
class Menu(BaseScreen):
|
||||
pass
|
||||
@ -1,8 +1,10 @@
|
||||
import pygame
|
||||
from utils import CONFIG, Figure, Size
|
||||
|
||||
from .base import BaseScreen, SceenElement
|
||||
|
||||
class Preview:
|
||||
|
||||
class Preview(BaseScreen, SceenElement):
|
||||
"""
|
||||
Class representing the preview of upcoming figures on the sidebar.
|
||||
|
||||
@ -17,15 +19,25 @@ class Preview:
|
||||
self._initialize_surface()
|
||||
self._initialize_rect()
|
||||
|
||||
def run(self, next_figure: Figure) -> None:
|
||||
def run(self) -> None:
|
||||
"""Run the preview by updating the display and drawing next figure."""
|
||||
self.draw()
|
||||
|
||||
def update(self, next_figure: Figure) -> None:
|
||||
"""
|
||||
Run the preview by updating the display and drawing next figures.
|
||||
Update the preview information.
|
||||
|
||||
Args:
|
||||
next_figures (list[Figure]): List of upcoming figures.
|
||||
next_figures: Next figure.
|
||||
"""
|
||||
self.dispaly_surface.blit(self.surface, self.rect)
|
||||
self._draw_preview(next_figure)
|
||||
self.next_figure = next_figure
|
||||
|
||||
def draw(self) -> None:
|
||||
"""Draw the preview on the preview surface."""
|
||||
self._update_diplaysurface()
|
||||
self._draw_background()
|
||||
self._draw_border()
|
||||
self._draw_figure()
|
||||
|
||||
def _draw_border(self) -> None:
|
||||
"""Draw the border around the preview surface."""
|
||||
@ -37,7 +49,7 @@ class Preview:
|
||||
CONFIG.game.border_radius,
|
||||
)
|
||||
|
||||
def _draw_figure(self, figure: Figure) -> None:
|
||||
def _draw_figure(self) -> None:
|
||||
"""
|
||||
Draw a single upcoming figure on the preview surface.
|
||||
|
||||
@ -45,23 +57,12 @@ class Preview:
|
||||
figure (Figure): The upcoming figure to draw.
|
||||
idx (int): Index of the figure in the list.
|
||||
"""
|
||||
figure_surface = figure.value.image
|
||||
figure_surface = self.next_figure.value.image
|
||||
x = self.surface.get_width() / 2
|
||||
y = self.surface.get_height() / 2
|
||||
rect = figure_surface.get_rect(center=(x, y))
|
||||
self.surface.blit(figure_surface, rect)
|
||||
|
||||
def _draw_preview(self, next_figure: Figure) -> None:
|
||||
"""
|
||||
Draw the preview with the background, border, and next figure.
|
||||
|
||||
Args:
|
||||
next_figures (list[Figure]): List of upcoming figures.
|
||||
"""
|
||||
self._draw_background()
|
||||
self._draw_border()
|
||||
self._draw_figure(next_figure)
|
||||
|
||||
def _draw_background(self) -> None:
|
||||
"""Draw the background of the preview."""
|
||||
self.surface.fill(CONFIG.colors.bg_sidebar)
|
||||
@ -79,3 +80,7 @@ class Preview:
|
||||
CONFIG.window.padding,
|
||||
)
|
||||
)
|
||||
|
||||
def _update_diplaysurface(self) -> None:
|
||||
"""Update the display surface."""
|
||||
self.dispaly_surface.blit(self.surface, self.rect)
|
||||
@ -1,8 +1,10 @@
|
||||
import pygame
|
||||
from utils import CONFIG, Size
|
||||
|
||||
from .base import BaseScreen, SceenElement, TextScreen
|
||||
|
||||
class Score:
|
||||
|
||||
class Score(BaseScreen, SceenElement, TextScreen):
|
||||
"""
|
||||
Class representing the score on the sidebar.
|
||||
|
||||
@ -24,7 +26,7 @@ class Score:
|
||||
|
||||
def run(self) -> None:
|
||||
"""Display the score on the game surface."""
|
||||
self.dispaly_surface.blit(self.surface, self.rect)
|
||||
self._update_diplaysurface()
|
||||
self.draw()
|
||||
|
||||
def update(self, lines: int, score: int, level: int) -> None:
|
||||
@ -55,7 +57,7 @@ class Score:
|
||||
y = self.increment_height / 2 + idx * self.increment_height
|
||||
self._display_text(text, (x, y))
|
||||
|
||||
def _display_text(self, text: tuple[str, int], pos: tuple[int, int]) -> None:
|
||||
def _display_text(self, text: tuple[str, int], pos: tuple[float, float]) -> None:
|
||||
"""
|
||||
Display a single text element on the score surface.
|
||||
|
||||
@ -101,3 +103,7 @@ class Score:
|
||||
def _initialize_increment_height(self) -> None:
|
||||
"""Initialize the increment height for positioning text elements."""
|
||||
self.increment_height = self.surface.get_height() / 3
|
||||
|
||||
def _update_diplaysurface(self) -> None:
|
||||
"""Update the display surface."""
|
||||
self.dispaly_surface.blit(self.surface, self.rect)
|
||||
@ -1,16 +1,18 @@
|
||||
from typing import Callable, Optional
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import numpy as np
|
||||
import pygame
|
||||
from utils import CONFIG, Direction, Field, Figure, Rotation
|
||||
|
||||
from .block import Block
|
||||
from .log import log
|
||||
from .tetromino import Tetromino
|
||||
from .timer import Timer, Timers
|
||||
from game.log import log
|
||||
from game.sprites.block import Block
|
||||
from game.sprites.tetromino import Tetromino
|
||||
from game.timer import Timer, Timers
|
||||
|
||||
from .base import BaseScreen
|
||||
|
||||
|
||||
class Game:
|
||||
class Tetris(BaseScreen):
|
||||
"""
|
||||
Game class for managing the game state.
|
||||
|
||||
@ -79,7 +81,7 @@ class Game:
|
||||
|
||||
def handle_event(self) -> None:
|
||||
"""Handle player input events."""
|
||||
keys: list[bool] = pygame.key.get_pressed()
|
||||
keys: pygame.key.ScancodeWrapper = pygame.key.get_pressed()
|
||||
|
||||
self._handle_movement_keys(keys)
|
||||
self._handle_rotation_keys(keys)
|
||||
@ -215,9 +217,11 @@ class Game:
|
||||
for block in self.sprites:
|
||||
self.field[int(block.pos.y), int(block.pos.x)] = block
|
||||
|
||||
def _generate_empty_field(self) -> np.ndarray:
|
||||
def _generate_empty_field(self) -> np.ndarray[Field, Any]:
|
||||
"""Generate an empty game field."""
|
||||
return np.full((CONFIG.game.rows, CONFIG.game.columns), None, dtype=Field)
|
||||
return np.full(
|
||||
(CONFIG.game.rows, CONFIG.game.columns), Field.EMPTY, dtype=Field
|
||||
)
|
||||
|
||||
def _calculate_score(self, rows_deleted: int) -> None:
|
||||
"""Calculate and update the game score."""
|
||||
@ -308,7 +312,7 @@ class Game:
|
||||
"""Fill the game surface with background color."""
|
||||
self.surface.fill(CONFIG.colors.bg_float)
|
||||
|
||||
def _handle_movement_keys(self, keys: list[bool]) -> None:
|
||||
def _handle_movement_keys(self, keys: pygame.key.ScancodeWrapper) -> None:
|
||||
"""
|
||||
Handle movement keys.
|
||||
|
||||
@ -326,7 +330,7 @@ class Game:
|
||||
self.move_right()
|
||||
self.timers.horizontal.activate()
|
||||
|
||||
def _handle_rotation_keys(self, keys: list[bool]) -> None:
|
||||
def _handle_rotation_keys(self, keys: pygame.key.ScancodeWrapper) -> None:
|
||||
"""
|
||||
Handle rotation keys.
|
||||
|
||||
@ -353,7 +357,7 @@ class Game:
|
||||
self.rotate_reverse()
|
||||
self.timers.rotation.activate()
|
||||
|
||||
def _handle_down_key(self, keys: list[bool]) -> None:
|
||||
def _handle_down_key(self, keys: pygame.key.ScancodeWrapper) -> None:
|
||||
"""Handle the down key [K_DOWN, K_s, K_j]."""
|
||||
down_keys = keys[pygame.K_DOWN] or keys[pygame.K_s] or keys[pygame.K_j]
|
||||
if not self.down_pressed and down_keys:
|
||||
@ -364,7 +368,7 @@ class Game:
|
||||
self.down_pressed = False
|
||||
self.timers.vertical.duration = self.initial_block_speed
|
||||
|
||||
def _handle_drop_key(self, keys: list[bool]) -> None:
|
||||
def _handle_drop_key(self, keys: pygame.key.ScancodeWrapper) -> None:
|
||||
"""Handle the drop key [K_SPACE]."""
|
||||
drop_keys = keys[pygame.K_SPACE]
|
||||
|
||||
4
src/game/sprites/__init__.py
Normal file
4
src/game/sprites/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .block import Block
|
||||
from .tetromino import Tetromino
|
||||
|
||||
__all__ = ["Block", "Tetromino"]
|
||||
@ -1,3 +1,5 @@
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
import pygame
|
||||
from utils import CONFIG, Rotation, Size
|
||||
@ -22,7 +24,7 @@ class Block(pygame.sprite.Sprite):
|
||||
self,
|
||||
/,
|
||||
*,
|
||||
group: pygame.sprite.Group,
|
||||
group: pygame.sprite.Group[Any],
|
||||
pos: pygame.Vector2,
|
||||
color: str,
|
||||
) -> None:
|
||||
@ -32,9 +34,13 @@ class Block(pygame.sprite.Sprite):
|
||||
|
||||
def update(self) -> None:
|
||||
"""Updates the block's position on the screen."""
|
||||
self.rect.topleft = self.pos * CONFIG.game.cell.width
|
||||
if self.rect:
|
||||
self.rect.topleft = (
|
||||
self.pos.x * CONFIG.game.cell.width,
|
||||
self.pos.y * CONFIG.game.cell.width,
|
||||
)
|
||||
|
||||
def vertical_collision(self, x: int, field: np.ndarray) -> bool:
|
||||
def vertical_collision(self, x: int, field: np.ndarray[int, Any]) -> bool:
|
||||
"""
|
||||
Checks for vertical collision with the game field.
|
||||
|
||||
@ -47,7 +53,7 @@ class Block(pygame.sprite.Sprite):
|
||||
"""
|
||||
return not 0 <= x < CONFIG.game.columns or field[int(self.pos.y), x]
|
||||
|
||||
def horizontal_collision(self, y: int, field: np.ndarray) -> bool:
|
||||
def horizontal_collision(self, y: int, field: np.ndarray[int, Any]) -> bool:
|
||||
"""
|
||||
Checks for horizontal collision with the game field.
|
||||
|
||||
@ -91,4 +97,5 @@ class Block(pygame.sprite.Sprite):
|
||||
pos: Initial position of the block.
|
||||
"""
|
||||
self.pos = pygame.Vector2(pos) + CONFIG.game.offset
|
||||
self.rect = self.image.get_rect(topleft=self.pos * CONFIG.game.cell.width)
|
||||
if self.image:
|
||||
self.rect = self.image.get_rect(topleft=self.pos * CONFIG.game.cell.width)
|
||||
@ -1,4 +1,4 @@
|
||||
from typing import Callable, Optional
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import numpy as np
|
||||
import pygame
|
||||
@ -29,9 +29,9 @@ class Tetromino:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
group: pygame.sprite.Group,
|
||||
group: pygame.sprite.Group[Any],
|
||||
create_new: Callable[[], None],
|
||||
field: np.ndarray,
|
||||
field: np.ndarray[int, Any],
|
||||
shape: Optional[Figure] = None,
|
||||
) -> None:
|
||||
self.figure: Figure = self._generate_figure(shape)
|
||||
@ -156,7 +156,7 @@ class Tetromino:
|
||||
for pos in new_positions
|
||||
)
|
||||
|
||||
def _initialize_blocks(self, group: pygame.sprite.Group) -> list[Block]:
|
||||
def _initialize_blocks(self, group: pygame.sprite.Group[Any]) -> list[Block]:
|
||||
"""
|
||||
Initializes Tetromino blocks.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user