fix imports

This commit is contained in:
Kristofers Solo 2024-01-06 21:25:42 +02:00
parent e9cd973360
commit 080ba1b9cb
10 changed files with 136 additions and 48 deletions

View File

@ -1,9 +1,4 @@
from .log import log from .log import log
from .screens import Game, Main, Tetris from .screens import Game, Main, Preview, Score, Tetris
__all__ = [ __all__ = ["Main", "Game", "Preview", "Score", "Tetris", "log"]
"log",
"Main",
"Game",
"Preview",
]

View File

@ -4,7 +4,7 @@ import pygame
from utils import CONFIG, Figure, GameMode from utils import CONFIG, Figure, GameMode
from game.log import log from game.log import log
from game.sprites.tetromino import Tetromino from game.sprites import Tetromino
from .base import BaseScreen from .base import BaseScreen
from .preview import Preview from .preview import Preview
@ -24,7 +24,7 @@ class Game(BaseScreen):
score: Score object. score: Score object.
preview: Preview object. preview: Preview object.
next_figures: List of upcoming figures. next_figures: List of upcoming figures.
music: Pygame music that plays in the background. music: Music that plays in the background.
""" """
def __init__(self, game_mode: GameMode) -> None: def __init__(self, game_mode: GameMode) -> None:

View File

@ -5,8 +5,7 @@ import pygame
from utils import CONFIG, Direction, Figure, GameMode, Rotation from utils import CONFIG, Direction, Figure, GameMode, Rotation
from game.log import log from game.log import log
from game.sprites.block import Block from game.sprites import Block, Tetromino
from game.sprites.tetromino import Tetromino
from game.timer import Timer, Timers from game.timer import Timer, Timers
from .base import BaseScreen, SceenElement from .base import BaseScreen, SceenElement
@ -91,31 +90,61 @@ class Tetris(BaseScreen):
self._handle_down_key(keys) self._handle_down_key(keys)
self._handle_drop_key(keys) self._handle_drop_key(keys)
def move_down(self) -> None: def move_down(self) -> bool:
"""Move the current tetromino down.""" """
self.tetromino.move_down() Move the current tetromino down.
def move_left(self) -> None: Returns:
"""Move the current tetromino to the left.""" True if the movement was successful, False otherwise.
self.tetromino.move_horizontal(Direction.LEFT) """
return self.tetromino.move_down()
def move_right(self) -> None: def move_left(self) -> bool:
"""Move the current tetromino to the right.""" """
self.tetromino.move_horizontal(Direction.RIGHT) Move the current tetromino to the left.
def rotate(self) -> None: Returns:
"""Rotate the current tetromino clockwise.""" True if the movement was successful, False otherwise.
self.tetromino.rotate() """
return self.tetromino.move_horizontal(Direction.LEFT)
def rotate_reverse(self) -> None: def move_right(self) -> bool:
"""Rotate the current tetromino counter-clockwise.""" """
self.tetromino.rotate(Rotation.COUNTER_CLOCKWISE) Move the current tetromino to the right.
def drop(self) -> None: Returns:
"""Drop the current tetromino.""" True if the movement was successful, False otherwise.
self.tetromino.drop() """
return self.tetromino.move_horizontal(Direction.RIGHT)
def create_new_tetromino(self) -> None: def rotate(self) -> bool:
"""
Rotate the current tetromino clockwise.
Returns:
True if the rotation was successful, False otherwise.
"""
return self.tetromino.rotate()
def rotate_reverse(self) -> bool:
"""
Rotate the current tetromino counter-clockwise.
Returns:
True if the rotation was successful, False otherwise.
"""
return self.tetromino.rotate(Rotation.COUNTER_CLOCKWISE)
def drop(self) -> bool:
"""
Drop the current tetromino.
Returns:
True if the movement was successful, False otherwise.
"""
return self.tetromino.drop()
def create_new_tetromino(self, shape: Optional[Figure] = None) -> Tetromino:
"""Create a new tetromino and perform necessary actions.""" """Create a new tetromino and perform necessary actions."""
self._play_landing_sound() self._play_landing_sound()
self._check_finished_rows() self._check_finished_rows()
@ -128,9 +157,11 @@ class Tetris(BaseScreen):
self.sprites, self.sprites,
self.create_new_tetromino, self.create_new_tetromino,
self.field, self.field,
self.get_next_figure(), shape or self.get_next_figure(),
) )
return self.tetromino
def _check_game_over(self) -> bool: def _check_game_over(self) -> bool:
""" """
Check if the game is over. Check if the game is over.

View File

@ -31,7 +31,7 @@ class Tetromino:
def __init__( def __init__(
self, self,
group: pygame.sprite.Group, group: pygame.sprite.Group,
create_new: Callable[[], None], create_new: Callable[[Optional[Figure]], "Tetromino"],
field: np.ndarray[Optional[Block], Any], field: np.ndarray[Optional[Block], Any],
shape: Optional[Figure] = None, shape: Optional[Figure] = None,
) -> None: ) -> None:
@ -42,32 +42,44 @@ class Tetromino:
self.field = field self.field = field
self.blocks = self._initialize_blocks(group) self.blocks = self._initialize_blocks(group)
def move_down(self) -> None: def move_down(self) -> bool:
""" """
Moves the Tetromino down. Moves the Tetromino down.
If there is a collision, the Tetromino is placed on the field, and a new one is created. If there is a collision, the Tetromino is placed on the field, and a new one is created.
Returns:
True if the movement was successful, False otherwise.
""" """
if not self._check_horizontal_collision(self.blocks, Direction.DOWN): if not self._check_horizontal_collision(self.blocks, Direction.DOWN):
for block in self.blocks: for block in self.blocks:
block.pos.y += 1 block.pos.y += 1
else: return True
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: for block in self.blocks:
self.field[int(block.pos.y), int(block.pos.x)] = block
self.create_new(None)
return False
def move_horizontal(self, direction: Direction) -> bool:
""" """
Moves the Tetromino horizontally. Moves the Tetromino horizontally.
Args: Args:
direction: Direction to move (LEFT or RIGHT). direction: Direction to move (LEFT or RIGHT).
Returns:
True if the movement was successful, False otherwise.
""" """
if not self._check_vertical_collision(self.blocks, direction): if not self._check_vertical_collision(self.blocks, direction):
for block in self.blocks: for block in self.blocks:
block.pos.x += direction.value block.pos.x += direction.value
return True
return False
def rotate(self, rotation: Rotation = Rotation.CLOCKWISE) -> None: def rotate(self, rotation: Rotation = Rotation.CLOCKWISE) -> bool:
""" """
Rotates the Tetromino. Rotates the Tetromino.
@ -75,9 +87,12 @@ class Tetromino:
Args: Args:
rotation: Rotation to perform (CLOCKWISE or COUNTER_CLOCKWISE). rotation: Rotation to perform (CLOCKWISE or COUNTER_CLOCKWISE).
Returns:
True if the rotation was successful, False otherwise.
""" """
if self.figure == Figure.O: if self.figure == Figure.O:
return return False
pivot: pygame.Vector2 = self.blocks[0].pos pivot: pygame.Vector2 = self.blocks[0].pos
@ -88,19 +103,48 @@ class Tetromino:
if self._are_new_positions_valid(new_positions): if self._are_new_positions_valid(new_positions):
self._update_block_positions(new_positions) self._update_block_positions(new_positions)
return return True
if any(pos.x < 0 for pos in new_positions): if any(pos.x < 0 for pos in new_positions):
self.move_horizontal(Direction.RIGHT) self.move_horizontal(Direction.RIGHT)
else: else:
self.move_horizontal(Direction.LEFT) self.move_horizontal(Direction.LEFT)
def drop(self) -> None: return False
"""Drops the Tetromino to the bottom of the game field."""
def next_rotation(self) -> "Tetromino":
self.rotate()
return self
def drop(self) -> bool:
"""
Drops the Tetromino to the bottom of the game field.
Returns:
True if the drop was successful, False otherwise.
"""
while not self._check_horizontal_collision(self.blocks, Direction.DOWN): while not self._check_horizontal_collision(self.blocks, Direction.DOWN):
for block in self.blocks: for block in self.blocks:
block.pos.y += 1 block.pos.y += 1
return True
def check_collision(self, direction: Direction) -> bool:
"""
Checks if there is a collision in the given direction.
Args:
direction: Direction to check (UP, DOWN, LEFT, or RIGHT).
Returns:
True if there is a collision, False otherwise.
"""
return self._check_horizontal_collision(
self.blocks, direction
) or self._check_vertical_collision(self.blocks, direction)
def _check_vertical_collision( def _check_vertical_collision(
self, blocks: list[Block], direction: Direction self, blocks: list[Block], direction: Direction
) -> bool: ) -> bool:

View File

@ -3,7 +3,8 @@ from .enum import Direction, GameMode, Rotation
from .figure import Figure, FigureConfig from .figure import Figure, FigureConfig
from .log import log from .log import log
from .path import BASE_PATH from .path import BASE_PATH
from .size import Size from .tuples import BestMove, Size
from .weights import Weights
__all__ = [ __all__ = [
"BASE_PATH", "BASE_PATH",
@ -15,4 +16,5 @@ __all__ = [
"Direction", "Direction",
"Rotation", "Rotation",
"GameMode", "GameMode",
"Weights",
] ]

View File

@ -5,7 +5,7 @@ from pygame import Vector2 as Vec2
from .colors import TokyoNightNight from .colors import TokyoNightNight
from .path import BASE_PATH from .path import BASE_PATH
from .size import Size from .tuples import Size
PADDING = 20 PADDING = 20

View File

@ -99,4 +99,4 @@ class Figure(Enum):
@classmethod @classmethod
def random(cls) -> "Figure": def random(cls) -> "Figure":
return random.choice(list(Figure)) return random.choice(list(cls))

View File

@ -1,5 +1,7 @@
from typing import NamedTuple, Union from typing import NamedTuple, Union
from .enum import Direction
class Size(NamedTuple): class Size(NamedTuple):
width: int | float width: int | float
@ -9,3 +11,8 @@ class Size(NamedTuple):
if isinstance(other, Size): if isinstance(other, Size):
return Size(self.width - other.width, self.height - other.height) return Size(self.width - other.width, self.height - other.height)
return Size(self.width - other, self.height - other) return Size(self.width - other, self.height - other)
class BestMove(NamedTuple):
rotation: int
direction: Direction

9
src/utils/weights.py Normal file
View File

@ -0,0 +1,9 @@
from attrs import define
@define
class Weights:
height: float
lines: float
holes: float
bumpiness: float

View File

@ -2,7 +2,7 @@ import unittest
import numpy as np import numpy as np
from ai.fitness.bumpiness import get_bumpiness from ai.fitness.bumpiness import get_bumpiness
from ai.fitness.holes import get_holes from ai.fitness.holes import holes
from ai.fitness.peaks import get_peaks_sum from ai.fitness.peaks import get_peaks_sum
from ai.fitness.transitions import ( from ai.fitness.transitions import (
get_col_transition, get_col_transition,
@ -58,7 +58,7 @@ class TestFitness(unittest.TestCase):
np.array([0, 1, 0, 0, 0]), np.array([0, 1, 0, 0, 0]),
) )
for field, answer in zip(self.fields, answers): for field, answer in zip(self.fields, answers):
self.assertTrue(np.array_equal(get_holes(field), answer)) self.assertTrue(np.array_equal(holes(field), answer))
def test_get_wells(self) -> None: def test_get_wells(self) -> None:
answers = ( answers = (