diff --git a/pyproject.toml b/pyproject.toml index 364d09a..8e3432c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,9 +7,10 @@ readme = "README.md" requires-python = ">=3.11" license = { text = "GPLv3" } dependencies = [ - "loguru==0.7.2", "attrs==23.1.0", + "loguru==0.7.2", "neat-python==0.92", + "numpy==1.26.3", "pygame-ce==2.4.0", ] diff --git a/requirements.txt b/requirements.txt index f3fc5af..56c1f94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ -loguru>=0.7.2 attrs>=23.1.0 +loguru>=0.7.2 neat-python>=0.92 +numpy>=1.26.3 pygame-ce>=2.4.0 . diff --git a/src/game/block.py b/src/game/block.py index ad6e021..9ffd441 100644 --- a/src/game/block.py +++ b/src/game/block.py @@ -1,10 +1,16 @@ +import numpy as np import pygame from utils import CONFIG, Size class Block(pygame.sprite.Sprite): def __init__( - self, /, *, group: pygame.sprite.Group, pos: pygame.Vector2, color: str + self, + /, + *, + group: pygame.sprite.Group, + pos: pygame.Vector2, + color: str, ) -> None: super().__init__(group) self.image = pygame.Surface(CONFIG.game.cell) @@ -16,8 +22,8 @@ class Block(pygame.sprite.Sprite): def update(self) -> None: self.rect.topleft = self.pos * CONFIG.game.cell.width - def vertical_collision(self, x: int) -> bool: - return not 0 <= x < CONFIG.game.columns + def vertical_collision(self, x: int, field: np.ndarray) -> bool: + return not 0 <= x < CONFIG.game.columns or field[int(self.pos.y), x] def horizontal_collision(self, y: int) -> bool: return y >= CONFIG.game.rows diff --git a/src/game/game.py b/src/game/game.py index 5a64dd2..0dab9ff 100644 --- a/src/game/game.py +++ b/src/game/game.py @@ -1,6 +1,10 @@ -import pygame -from utils import CONFIG, Direction, Figure +from typing import Optional +import numpy as np +import pygame +from utils import CONFIG, Direction, Field, Figure + +from .block import Block from .log import log from .tetromino import Tetromino from .timer import Timer, Timers @@ -12,10 +16,17 @@ class Game: self.dispaly_surface = pygame.display.get_surface() self.rect = self.surface.get_rect(topleft=CONFIG.game.pos) + self.sprites: pygame.sprite.Group[Block] = pygame.sprite.Group() + self._create_grid_surface() - self.sprites = pygame.sprite.Group() - self.tetromino = Tetromino(self.sprites, self.create_new_tetromino) + self.field = np.full((CONFIG.game.rows, CONFIG.game.columns), None, dtype=Field) + + self.tetromino = Tetromino( + self.sprites, + self.create_new_tetromino, + self.field, + ) self.timers = Timers( Timer(CONFIG.game.initial_speed, True, self.move_down), @@ -63,7 +74,11 @@ class Game: self.tetromino.move_horizontal(Direction.RIGHT) def create_new_tetromino(self) -> None: - self.tetromino = Tetromino(self.sprites, self.create_new_tetromino) + self.tetromino = Tetromino( + self.sprites, + self.create_new_tetromino, + self.field, + ) def _create_grid_surface(self) -> None: self.grid_surface = self.surface.copy() diff --git a/src/game/tetromino.py b/src/game/tetromino.py index e4d6725..6e6c1f7 100644 --- a/src/game/tetromino.py +++ b/src/game/tetromino.py @@ -1,22 +1,26 @@ from typing import Callable, Optional +import numpy as np import pygame from utils import CONFIG, Direction, Figure, FigureConfig, Size from .block import Block +from .log import log class Tetromino: def __init__( self, group: pygame.sprite.Group, - func: Callable[[None], None], + func: Callable[[], None], + field: np.ndarray, shape: Optional[Figure] = None, ) -> None: self.figure: FigureConfig = shape.value if shape else Figure.random().value self.block_positions: list[pygame.Vector2] = self.figure.shape self.color: str = self.figure.color self.create_new = func + self.field = field self.blocks = [ Block(group=group, pos=pos, color=self.color) @@ -28,6 +32,8 @@ class Tetromino: for block in self.blocks: block.pos.y += 1 else: + for block in self.blocks: + self.field[int(block.pos.y), int(block.pos.x)] = block self.create_new() def move_horizontal(self, direction: Direction) -> None: @@ -39,7 +45,7 @@ class Tetromino: self, blocks: list[Block], direction: Direction ) -> bool: return any( - block.vertical_collision(int(block.pos.x + direction.value)) + block.vertical_collision(int(block.pos.x + direction.value), self.field) for block in self.blocks ) diff --git a/src/game/timer.py b/src/game/timer.py index baedd09..c46e670 100644 --- a/src/game/timer.py +++ b/src/game/timer.py @@ -8,7 +8,7 @@ from attrs import define, field class Timer: duration: int = field(converter=int) repeated: bool = field(default=False) - func: Optional[Callable[[None], None]] = field(default=None) + func: Optional[Callable[[], None]] = field(default=None) start_time: int = 0 active: bool = False diff --git a/src/utils/__init__.py b/src/utils/__init__.py index b37ed9d..954d5af 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -1,5 +1,5 @@ from .config import CONFIG -from .direction import Direction +from .enum import Direction, Field from .figure import Figure, FigureConfig from .log import log from .path import BASE_PATH @@ -13,4 +13,5 @@ __all__ = [ "Figure", "FigureConfig", "Direction", + "Field", ] diff --git a/src/utils/direction.py b/src/utils/enum.py similarity index 62% rename from src/utils/direction.py rename to src/utils/enum.py index 5016766..bff8c2a 100644 --- a/src/utils/direction.py +++ b/src/utils/enum.py @@ -6,3 +6,8 @@ class Direction(Enum): RIGHT = 1 DOWN = 1 UP = -1 + + +class Field(Enum): + EMPTY = None + FILLED = "Block" diff --git a/src/utils/figure.py b/src/utils/figure.py index 15116ac..fb2fb48 100644 --- a/src/utils/figure.py +++ b/src/utils/figure.py @@ -9,7 +9,7 @@ from .colors import TokyoNightNight class FigureConfig(NamedTuple): - shape: list[Vec2, Vec2, Vec2, Vec2] + shape: list[Vec2] color: str