mirror of
https://github.com/kristoferssolo/Tetris.git
synced 2025-10-21 20:00:35 +00:00
feat(game): add main menu
This commit is contained in:
parent
0afe1ed3cb
commit
7d5bf8e658
11
main.py
11
main.py
@ -61,16 +61,17 @@ def main(args: argparse.ArgumentParser) -> None:
|
|||||||
elif args.verbose:
|
elif args.verbose:
|
||||||
CONFIG.log_level = "info"
|
CONFIG.log_level = "info"
|
||||||
|
|
||||||
import ai
|
# import ai
|
||||||
import game
|
import game
|
||||||
|
|
||||||
if args.train is not None:
|
if args.train is not None:
|
||||||
ai.log.debug("Training the AI")
|
# ai.log.debug("Training the AI")
|
||||||
# ai.train(*args.train)
|
# # ai.train(*args.train)
|
||||||
game.Game(GameMode.AI_TRAINING).run()
|
# game.Menu(GameMode.AI_TRAINING).run()
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
game.log.debug("Running the game")
|
game.log.debug("Running the game")
|
||||||
game.Game(GameMode.PLAYER).run()
|
game.Main(GameMode.PLAYER).run()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
from .log import log
|
from .log import log
|
||||||
from .screens import Game, Menu, Tetris
|
from .screens import Game, Main, Tetris
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"log",
|
"log",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from .game import Game
|
from .game import Game
|
||||||
from .menu import Menu
|
from .main import Main
|
||||||
from .preview import Preview
|
from .preview import Preview
|
||||||
from .score import Score
|
from .score import Score
|
||||||
from .tetris import Tetris
|
from .tetris import Tetris
|
||||||
|
|
||||||
__all__ = ["Tetris", "Game", "Preview", "Score", "Menu"]
|
__all__ = ["Tetris", "Game", "Preview", "Score", "Main"]
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
from abc import ABC, ABCMeta, abstractmethod
|
from abc import ABC, ABCMeta, abstractmethod
|
||||||
|
|
||||||
import pygame
|
|
||||||
|
|
||||||
|
|
||||||
class BaseScreen(ABC, metaclass=ABCMeta):
|
class BaseScreen(ABC, metaclass=ABCMeta):
|
||||||
"""Base screen class."""
|
"""Base screen class."""
|
||||||
@ -37,7 +35,7 @@ class SceenElement(ABC, metaclass=ABCMeta):
|
|||||||
"""Initialize the rectangle."""
|
"""Initialize the rectangle."""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def _update_diplaysurface(self) -> None:
|
def _update_display_surface(self) -> None:
|
||||||
"""Update the display surface."""
|
"""Update the display surface."""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
20
src/game/screens/button.py
Normal file
20
src/game/screens/button.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from abc import ABC, ABCMeta, abstractmethod
|
||||||
|
from typing import Callable, Optional
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
|
||||||
|
class Button(ABC, metaclass=ABCMeta):
|
||||||
|
"""Base button class."""
|
||||||
|
|
||||||
|
def __init__(self, text: str, action: Optional[Callable[[], None]]) -> None:
|
||||||
|
self.action = action
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def on_click(self) -> None:
|
||||||
|
"""Handle click event."""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def on_hover(self) -> None:
|
||||||
|
"""Handle hover event."""
|
||||||
@ -4,12 +4,12 @@ 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 .base import BaseScreen
|
from .base import BaseScreen
|
||||||
from .preview import Preview
|
from .preview import Preview
|
||||||
from .score import Score
|
from .score import Score
|
||||||
from .tetris import Tetris
|
from .tetris import Tetris
|
||||||
from .tetromino import Tetromino
|
|
||||||
|
|
||||||
|
|
||||||
class Game(BaseScreen):
|
class Game(BaseScreen):
|
||||||
@ -27,26 +27,20 @@ class Game(BaseScreen):
|
|||||||
music: Pygame music that plays in the background.
|
music: Pygame music that plays in the background.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, mode: GameMode) -> None:
|
def __init__(self) -> None:
|
||||||
log.info("Initializing the game")
|
|
||||||
self.game_mode = mode
|
|
||||||
self._initialize_pygeme()
|
|
||||||
self._initialize_game_components()
|
self._initialize_game_components()
|
||||||
self._start_background_music()
|
self._start_background_music()
|
||||||
|
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
"""Update the display."""
|
"""Update the display."""
|
||||||
pygame.display.update()
|
|
||||||
|
def update(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
"""Run the main game loop."""
|
|
||||||
while True:
|
|
||||||
self.run_game_loop()
|
|
||||||
|
|
||||||
def run_game_loop(self) -> None:
|
|
||||||
"""Run a single iteration of the game loop."""
|
"""Run a single iteration of the game loop."""
|
||||||
self.draw()
|
self.draw()
|
||||||
self.handle_events()
|
self.handle_events() # FIX:
|
||||||
|
|
||||||
self.tetris.run()
|
self.tetris.run()
|
||||||
self.score.run()
|
self.score.run()
|
||||||
@ -56,33 +50,20 @@ class Game(BaseScreen):
|
|||||||
self.draw()
|
self.draw()
|
||||||
self.clock.tick(CONFIG.fps)
|
self.clock.tick(CONFIG.fps)
|
||||||
|
|
||||||
def handle_events(self) -> None:
|
def mute(self) -> None:
|
||||||
"""Handle Pygame events."""
|
"""Mute the game."""
|
||||||
for event in pygame.event.get():
|
self.music.set_volume(0)
|
||||||
if event.type == pygame.QUIT:
|
self.tetris.mute()
|
||||||
self.exit()
|
|
||||||
elif event.type == pygame.KEYDOWN:
|
|
||||||
if event.key == pygame.K_q:
|
|
||||||
self.exit()
|
|
||||||
|
|
||||||
def exit(self) -> None:
|
|
||||||
"""Exit the game."""
|
|
||||||
pygame.quit()
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
def _initialize_game_components(self) -> None:
|
def _initialize_game_components(self) -> None:
|
||||||
"""Initialize game-related components."""
|
"""Initialize game-related components."""
|
||||||
|
self.clock = pygame.time.Clock()
|
||||||
self.next_figure: Figure = self._generate_next_figure()
|
self.next_figure: Figure = self._generate_next_figure()
|
||||||
|
|
||||||
self.tetris = Tetris(self._get_next_figure, self._update_score)
|
self.tetris = Tetris(self._get_next_figure, self._update_score)
|
||||||
self.score = Score()
|
self.score = Score()
|
||||||
self.preview = Preview()
|
self.preview = Preview()
|
||||||
|
|
||||||
def mute(self) -> None:
|
|
||||||
"""Mute the game."""
|
|
||||||
self.music.set_volume(0)
|
|
||||||
self.tetris.mute()
|
|
||||||
|
|
||||||
def _update_score(self, lines: int, score: int, level: int) -> None:
|
def _update_score(self, lines: int, score: int, level: int) -> None:
|
||||||
"""
|
"""
|
||||||
Update the game score.
|
Update the game score.
|
||||||
@ -114,14 +95,6 @@ class Game(BaseScreen):
|
|||||||
self.next_figure = self._generate_next_figure()
|
self.next_figure = self._generate_next_figure()
|
||||||
return next_figure
|
return next_figure
|
||||||
|
|
||||||
def _initialize_pygeme(self) -> None:
|
|
||||||
"""Initialize Pygame and set up the display."""
|
|
||||||
pygame.init()
|
|
||||||
pygame.display.set_caption(CONFIG.window.title)
|
|
||||||
self.display_surface = pygame.display.set_mode(CONFIG.window.size)
|
|
||||||
self.display_surface.fill(CONFIG.colors.bg)
|
|
||||||
self.clock = pygame.time.Clock()
|
|
||||||
|
|
||||||
def _start_background_music(self) -> None:
|
def _start_background_music(self) -> None:
|
||||||
"""Start playing background music."""
|
"""Start playing background music."""
|
||||||
self.music = pygame.mixer.Sound(CONFIG.music.background)
|
self.music = pygame.mixer.Sound(CONFIG.music.background)
|
||||||
|
|||||||
116
src/game/screens/main.py
Normal file
116
src/game/screens/main.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
from utils import CONFIG, GameMode
|
||||||
|
|
||||||
|
from game.log import log
|
||||||
|
|
||||||
|
from .base import BaseScreen, SceenElement, TextScreen
|
||||||
|
from .game import Game
|
||||||
|
from .menu_button import MenuButton
|
||||||
|
|
||||||
|
|
||||||
|
class Main(BaseScreen, SceenElement, TextScreen):
|
||||||
|
def __init__(self, mode: GameMode) -> None:
|
||||||
|
log.info("Initializing the game")
|
||||||
|
self._initialize_pygame()
|
||||||
|
self._initialize_surface()
|
||||||
|
self._initialize_rect()
|
||||||
|
self._initialize_font()
|
||||||
|
self._set_buttons()
|
||||||
|
self._initialize_increment_height()
|
||||||
|
self.game_mode = mode # TODO: use this
|
||||||
|
|
||||||
|
def draw(self) -> None:
|
||||||
|
"""Update the display."""
|
||||||
|
self._draw_background()
|
||||||
|
self._draw_text()
|
||||||
|
self._draw_border()
|
||||||
|
pygame.display.update()
|
||||||
|
|
||||||
|
def update(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def handle_events(self) -> None:
|
||||||
|
for event in pygame.event.get():
|
||||||
|
if event.type == pygame.QUIT:
|
||||||
|
self.exit()
|
||||||
|
elif event.type == pygame.KEYDOWN:
|
||||||
|
if event.key == pygame.K_q:
|
||||||
|
self.exit()
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
while True:
|
||||||
|
self.draw()
|
||||||
|
self.handle_events()
|
||||||
|
|
||||||
|
def exit(self) -> None:
|
||||||
|
"""Exit the game."""
|
||||||
|
pygame.quit()
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
def _set_buttons(self) -> None:
|
||||||
|
self.buttons: list[MenuButton] = [
|
||||||
|
MenuButton("Play", None),
|
||||||
|
MenuButton("AI", None),
|
||||||
|
MenuButton("Settings", None),
|
||||||
|
MenuButton("Quit", self.exit),
|
||||||
|
]
|
||||||
|
|
||||||
|
def _initialize_pygame(self) -> None:
|
||||||
|
"""Initialize Pygame and set up the display."""
|
||||||
|
pygame.init()
|
||||||
|
pygame.display.set_caption(CONFIG.window.title)
|
||||||
|
|
||||||
|
def _draw_background(self) -> None:
|
||||||
|
self.display_surface.fill(CONFIG.colors.bg)
|
||||||
|
|
||||||
|
def _draw_border(self) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _initialize_surface(self) -> None:
|
||||||
|
self.display_surface = pygame.display.set_mode(CONFIG.window.size)
|
||||||
|
|
||||||
|
def _initialize_rect(self) -> None:
|
||||||
|
"""Initialize the rectangle."""
|
||||||
|
self.rect = self.display_surface.get_rect(topright=(0, 0))
|
||||||
|
|
||||||
|
def _update_display_surface(self) -> None:
|
||||||
|
"""Do nothing. Not needed in this class."""
|
||||||
|
|
||||||
|
def _initialize_increment_height(self) -> None:
|
||||||
|
"""Initialize the increment height for positioning text elements/buttons."""
|
||||||
|
self.increment_height = (
|
||||||
|
self.display_surface.get_height() - CONFIG.window.size.height / 2
|
||||||
|
) / len(self.buttons)
|
||||||
|
|
||||||
|
def _display_text(self, text: str, pos: tuple[float, float]) -> None:
|
||||||
|
"""
|
||||||
|
Display a single text element on the score surface.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: A tuple containing the label and value of the text element.
|
||||||
|
pos: The position (x, y) where the text should be displayed.
|
||||||
|
"""
|
||||||
|
text_surface = self.font.render(text, True, CONFIG.colors.fg)
|
||||||
|
text_rect = text_surface.get_rect(center=pos)
|
||||||
|
self.display_surface.blit(text_surface, text_rect)
|
||||||
|
|
||||||
|
def _initialize_font(self) -> None:
|
||||||
|
"""Initialize the font used to display the score."""
|
||||||
|
self.font = pygame.font.Font(CONFIG.font.family, CONFIG.font.size)
|
||||||
|
|
||||||
|
def _draw_text(self) -> None:
|
||||||
|
"""Draw the text and buttons on the surface."""
|
||||||
|
|
||||||
|
x, y = self.display_surface.get_width() / 2, 100
|
||||||
|
self._display_text("Tetris", (x, y))
|
||||||
|
|
||||||
|
for idx, button in enumerate(self.buttons):
|
||||||
|
x = self.display_surface.get_width() / 2
|
||||||
|
y = (
|
||||||
|
self.increment_height / 4
|
||||||
|
+ idx * self.increment_height
|
||||||
|
+ CONFIG.window.size.height / 4
|
||||||
|
) # TODO: tweak a bit more
|
||||||
|
button.draw(self.display_surface, (x, y))
|
||||||
@ -1,7 +0,0 @@
|
|||||||
from game.log import log
|
|
||||||
|
|
||||||
from .base import BaseScreen
|
|
||||||
|
|
||||||
|
|
||||||
class Menu(BaseScreen):
|
|
||||||
pass
|
|
||||||
88
src/game/screens/menu_button.py
Normal file
88
src/game/screens/menu_button.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
from typing import Callable, Optional
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
from utils import CONFIG
|
||||||
|
|
||||||
|
from .base import BaseScreen, SceenElement, TextScreen
|
||||||
|
from .button import Button
|
||||||
|
|
||||||
|
|
||||||
|
class MenuButton(Button, BaseScreen, SceenElement, TextScreen):
|
||||||
|
def __init__(self, text: str, action: Optional[Callable[[], None]]) -> None:
|
||||||
|
super().__init__(text, action)
|
||||||
|
self._initialize_surface()
|
||||||
|
self._initialize_font()
|
||||||
|
|
||||||
|
def on_click(self) -> None:
|
||||||
|
"""Handle click event."""
|
||||||
|
if self.action:
|
||||||
|
self.action()
|
||||||
|
|
||||||
|
def on_hover(self) -> None:
|
||||||
|
"""Handle hover event."""
|
||||||
|
self._draw_border()
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
"""Display the button on the game surface."""
|
||||||
|
self.draw()
|
||||||
|
|
||||||
|
def update(self) -> None:
|
||||||
|
"""Update the button."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def draw(self, surface: pygame.Surface, pos: tuple[float, float]) -> None:
|
||||||
|
"""Draw the button on the button surface."""
|
||||||
|
self._initialize_rect(pos)
|
||||||
|
self._update_display_surface()
|
||||||
|
self._draw_background()
|
||||||
|
self._draw_text()
|
||||||
|
self._draw_border()
|
||||||
|
|
||||||
|
def _initialize_surface(self) -> None:
|
||||||
|
"""Initialize the button surface."""
|
||||||
|
self.surface = pygame.Surface(CONFIG.window.button.size)
|
||||||
|
self.display_surface = pygame.display.get_surface()
|
||||||
|
|
||||||
|
def _initialize_rect(self, pos: tuple[float, float]) -> None:
|
||||||
|
"""Initialize the button rectangle."""
|
||||||
|
self.rect = self.surface.get_rect(center=pos)
|
||||||
|
|
||||||
|
def _draw_text(self) -> None:
|
||||||
|
"""Draw the text on the text surface."""
|
||||||
|
x = self.surface.get_width() / 2
|
||||||
|
y = self.surface.get_height() / 2
|
||||||
|
self._display_text(self.text, (x, y))
|
||||||
|
|
||||||
|
def _display_text(self, text: str, pos: tuple[float, float]) -> None:
|
||||||
|
"""
|
||||||
|
Display a single text element on the button surface.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: The text to be displayed.
|
||||||
|
pos: The position (x, y) where the text should be displayed.
|
||||||
|
"""
|
||||||
|
text_surface = self.font.render(text, True, CONFIG.colors.fg_sidebar)
|
||||||
|
text_rect = text_surface.get_rect(center=pos)
|
||||||
|
self.surface.blit(text_surface, text_rect)
|
||||||
|
|
||||||
|
def _initialize_font(self) -> None:
|
||||||
|
"""Initialize the font used to display the score."""
|
||||||
|
self.font = pygame.font.Font(CONFIG.font.family, CONFIG.font.size)
|
||||||
|
|
||||||
|
def _draw_background(self) -> None:
|
||||||
|
"""Fill the background of the button."""
|
||||||
|
self.surface.fill(CONFIG.colors.bg_sidebar)
|
||||||
|
|
||||||
|
def _draw_border(self) -> None:
|
||||||
|
"""Draw the border of the button."""
|
||||||
|
pygame.draw.rect(
|
||||||
|
self.display_surface,
|
||||||
|
CONFIG.colors.border_highlight,
|
||||||
|
self.rect,
|
||||||
|
CONFIG.game.line_width * 2,
|
||||||
|
CONFIG.game.border_radius,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _update_display_surface(self) -> None:
|
||||||
|
"""Update the display surface."""
|
||||||
|
self.display_surface.blit(self.surface, self.rect)
|
||||||
@ -11,7 +11,7 @@ class Preview(BaseScreen, SceenElement):
|
|||||||
Attributes:
|
Attributes:
|
||||||
surface: Pygame surface representing the preview.
|
surface: Pygame surface representing the preview.
|
||||||
rect: Pygame rectangle representing the preview.
|
rect: Pygame rectangle representing the preview.
|
||||||
dispaly_surface: Pygame display surface.
|
display_surface: Pygame display surface.
|
||||||
increment_height: Height of each figure in the preview.
|
increment_height: Height of each figure in the preview.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ class Preview(BaseScreen, SceenElement):
|
|||||||
|
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
"""Draw the preview on the preview surface."""
|
"""Draw the preview on the preview surface."""
|
||||||
self._update_diplaysurface()
|
self._update_display_surface()
|
||||||
self._draw_background()
|
self._draw_background()
|
||||||
self._draw_border()
|
self._draw_border()
|
||||||
self._draw_figure()
|
self._draw_figure()
|
||||||
@ -42,7 +42,7 @@ class Preview(BaseScreen, SceenElement):
|
|||||||
def _draw_border(self) -> None:
|
def _draw_border(self) -> None:
|
||||||
"""Draw the border around the preview surface."""
|
"""Draw the border around the preview surface."""
|
||||||
pygame.draw.rect(
|
pygame.draw.rect(
|
||||||
self.dispaly_surface,
|
self.display_surface,
|
||||||
CONFIG.colors.border_highlight,
|
CONFIG.colors.border_highlight,
|
||||||
self.rect,
|
self.rect,
|
||||||
CONFIG.game.line_width * 2,
|
CONFIG.game.line_width * 2,
|
||||||
@ -70,7 +70,7 @@ class Preview(BaseScreen, SceenElement):
|
|||||||
def _initialize_surface(self) -> None:
|
def _initialize_surface(self) -> None:
|
||||||
"""Initialize the preview surface."""
|
"""Initialize the preview surface."""
|
||||||
self.surface = pygame.Surface(CONFIG.sidebar.preview)
|
self.surface = pygame.Surface(CONFIG.sidebar.preview)
|
||||||
self.dispaly_surface = pygame.display.get_surface()
|
self.display_surface = pygame.display.get_surface()
|
||||||
|
|
||||||
def _initialize_rect(self) -> None:
|
def _initialize_rect(self) -> None:
|
||||||
"""Initialize the preview rectangle."""
|
"""Initialize the preview rectangle."""
|
||||||
@ -81,6 +81,6 @@ class Preview(BaseScreen, SceenElement):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def _update_diplaysurface(self) -> None:
|
def _update_display_surface(self) -> None:
|
||||||
"""Update the display surface."""
|
"""Update the display surface."""
|
||||||
self.dispaly_surface.blit(self.surface, self.rect)
|
self.display_surface.blit(self.surface, self.rect)
|
||||||
|
|||||||
@ -10,7 +10,7 @@ class Score(BaseScreen, SceenElement, TextScreen):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
surface: Pygame surface representing the score.
|
surface: Pygame surface representing the score.
|
||||||
dispaly_surface: Pygame display surface.
|
display_surface: Pygame display surface.
|
||||||
rect: Pygame rectangle representing the score.
|
rect: Pygame rectangle representing the score.
|
||||||
font: Pygame font used to display the score.
|
font: Pygame font used to display the score.
|
||||||
text: Text to be displayed on the score.
|
text: Text to be displayed on the score.
|
||||||
@ -26,7 +26,6 @@ class Score(BaseScreen, SceenElement, TextScreen):
|
|||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
"""Display the score on the game surface."""
|
"""Display the score on the game surface."""
|
||||||
self._update_diplaysurface()
|
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
def update(self, lines: int, score: int, level: int) -> None:
|
def update(self, lines: int, score: int, level: int) -> None:
|
||||||
@ -46,6 +45,7 @@ class Score(BaseScreen, SceenElement, TextScreen):
|
|||||||
|
|
||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
"""Draw the score on the score surface."""
|
"""Draw the score on the score surface."""
|
||||||
|
self._update_display_surface()
|
||||||
self._draw_background()
|
self._draw_background()
|
||||||
self._draw_text()
|
self._draw_text()
|
||||||
self._draw_border()
|
self._draw_border()
|
||||||
@ -62,8 +62,8 @@ class Score(BaseScreen, SceenElement, TextScreen):
|
|||||||
Display a single text element on the score surface.
|
Display a single text element on the score surface.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
text (tuple[str, int]): A tuple containing the label and value of the text element.
|
text: A tuple containing the label and value of the text element.
|
||||||
pos (tuple[int, int]): The position (x, y) where the text should be displayed.
|
pos: The position (x, y) where the text should be displayed.
|
||||||
"""
|
"""
|
||||||
text_surface = self.font.render(
|
text_surface = self.font.render(
|
||||||
f"{text[0]}: {text[1]}", True, CONFIG.colors.fg_sidebar
|
f"{text[0]}: {text[1]}", True, CONFIG.colors.fg_sidebar
|
||||||
@ -74,7 +74,7 @@ class Score(BaseScreen, SceenElement, TextScreen):
|
|||||||
def _draw_border(self) -> None:
|
def _draw_border(self) -> None:
|
||||||
"""Draw the border of the score surface."""
|
"""Draw the border of the score surface."""
|
||||||
pygame.draw.rect(
|
pygame.draw.rect(
|
||||||
self.dispaly_surface,
|
self.display_surface,
|
||||||
CONFIG.colors.border_highlight,
|
CONFIG.colors.border_highlight,
|
||||||
self.rect,
|
self.rect,
|
||||||
CONFIG.game.line_width * 2,
|
CONFIG.game.line_width * 2,
|
||||||
@ -88,12 +88,12 @@ class Score(BaseScreen, SceenElement, TextScreen):
|
|||||||
def _initialize_surface(self) -> None:
|
def _initialize_surface(self) -> None:
|
||||||
"""Initialize the score surface."""
|
"""Initialize the score surface."""
|
||||||
self.surface = pygame.Surface(CONFIG.sidebar.score)
|
self.surface = pygame.Surface(CONFIG.sidebar.score)
|
||||||
self.dispaly_surface = pygame.display.get_surface()
|
self.display_surface = pygame.display.get_surface()
|
||||||
|
|
||||||
def _initialize_rect(self) -> None:
|
def _initialize_rect(self) -> None:
|
||||||
"""Initialize the score rectangle."""
|
"""Initialize the score rectangle."""
|
||||||
self.rect = self.surface.get_rect(
|
self.rect = self.surface.get_rect(
|
||||||
bottomright=CONFIG.window.size.sub(CONFIG.window.padding)
|
bottomright=CONFIG.window.size - CONFIG.window.padding
|
||||||
)
|
)
|
||||||
|
|
||||||
def _initialize_font(self) -> None:
|
def _initialize_font(self) -> None:
|
||||||
@ -104,6 +104,6 @@ class Score(BaseScreen, SceenElement, TextScreen):
|
|||||||
"""Initialize the increment height for positioning text elements."""
|
"""Initialize the increment height for positioning text elements."""
|
||||||
self.increment_height = self.surface.get_height() / 3
|
self.increment_height = self.surface.get_height() / 3
|
||||||
|
|
||||||
def _update_diplaysurface(self) -> None:
|
def _update_display_surface(self) -> None:
|
||||||
"""Update the display surface."""
|
"""Update the display surface."""
|
||||||
self.dispaly_surface.blit(self.surface, self.rect)
|
self.display_surface.blit(self.surface, self.rect)
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from game.sprites.block import Block
|
|||||||
from game.sprites.tetromino import Tetromino
|
from game.sprites.tetromino import Tetromino
|
||||||
from game.timer import Timer, Timers
|
from game.timer import Timer, Timers
|
||||||
|
|
||||||
from .base import BaseScreen
|
from .base import BaseScreen, SceenElement
|
||||||
|
|
||||||
|
|
||||||
class Tetris(BaseScreen):
|
class Tetris(BaseScreen):
|
||||||
@ -46,7 +46,7 @@ class Tetris(BaseScreen):
|
|||||||
get_next_figure: Callable[[], Figure],
|
get_next_figure: Callable[[], Figure],
|
||||||
update_score: Callable[[int, int, int], None],
|
update_score: Callable[[int, int, int], None],
|
||||||
) -> None:
|
) -> None:
|
||||||
self._initialize_game_surface()
|
self._initialize_surface()
|
||||||
self._initialize_sprites()
|
self._initialize_sprites()
|
||||||
|
|
||||||
self.get_next_figure = get_next_figure
|
self.get_next_figure = get_next_figure
|
||||||
@ -70,7 +70,7 @@ class Tetris(BaseScreen):
|
|||||||
def draw(self) -> None:
|
def draw(self) -> None:
|
||||||
"""Draw the game surface and its components."""
|
"""Draw the game surface and its components."""
|
||||||
self.update()
|
self.update()
|
||||||
self._fill_game_surface()
|
self._draw_background()
|
||||||
self.sprites.draw(self.surface)
|
self.sprites.draw(self.surface)
|
||||||
self._draw_border()
|
self._draw_border()
|
||||||
self._draw_grid()
|
self._draw_grid()
|
||||||
@ -249,10 +249,13 @@ class Tetris(BaseScreen):
|
|||||||
self._draw_border()
|
self._draw_border()
|
||||||
self._draw_grid()
|
self._draw_grid()
|
||||||
|
|
||||||
def _initialize_game_surface(self) -> None:
|
def _initialize_surface(self) -> None:
|
||||||
"""Initialize the game surface."""
|
"""Initialize the game surface."""
|
||||||
self.surface = pygame.Surface(CONFIG.game.size)
|
self.surface = pygame.Surface(CONFIG.game.size)
|
||||||
self.dispaly_surface = pygame.display.get_surface()
|
self.dispaly_surface = pygame.display.get_surface()
|
||||||
|
|
||||||
|
def _initialize_rect(self) -> None:
|
||||||
|
"""Initialize the rectangle."""
|
||||||
self.rect = self.surface.get_rect(topleft=CONFIG.game.pos)
|
self.rect = self.surface.get_rect(topleft=CONFIG.game.pos)
|
||||||
|
|
||||||
def _initialize_sprites(self) -> None:
|
def _initialize_sprites(self) -> None:
|
||||||
@ -308,7 +311,7 @@ class Tetris(BaseScreen):
|
|||||||
"""Update the display surface."""
|
"""Update the display surface."""
|
||||||
self.dispaly_surface.blit(self.surface, CONFIG.game.pos)
|
self.dispaly_surface.blit(self.surface, CONFIG.game.pos)
|
||||||
|
|
||||||
def _fill_game_surface(self) -> None:
|
def _draw_background(self) -> None:
|
||||||
"""Fill the game surface with background color."""
|
"""Fill the game surface with background color."""
|
||||||
self.surface.fill(CONFIG.colors.bg_float)
|
self.surface.fill(CONFIG.colors.bg_float)
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from typing import Any
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pygame
|
import pygame
|
||||||
from utils import CONFIG, Rotation, Size
|
from utils import CONFIG, Field, Rotation, Size
|
||||||
|
|
||||||
|
|
||||||
class Block(pygame.sprite.Sprite):
|
class Block(pygame.sprite.Sprite):
|
||||||
@ -24,7 +24,7 @@ class Block(pygame.sprite.Sprite):
|
|||||||
self,
|
self,
|
||||||
/,
|
/,
|
||||||
*,
|
*,
|
||||||
group: pygame.sprite.Group[Any],
|
group: pygame.sprite.Group,
|
||||||
pos: pygame.Vector2,
|
pos: pygame.Vector2,
|
||||||
color: str,
|
color: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -40,7 +40,7 @@ class Block(pygame.sprite.Sprite):
|
|||||||
self.pos.y * CONFIG.game.cell.width,
|
self.pos.y * CONFIG.game.cell.width,
|
||||||
)
|
)
|
||||||
|
|
||||||
def vertical_collision(self, x: int, field: np.ndarray[int, Any]) -> bool:
|
def vertical_collision(self, x: int, field: np.ndarray[Field, Any]) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks for vertical collision with the game field.
|
Checks for vertical collision with the game field.
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ class Block(pygame.sprite.Sprite):
|
|||||||
"""
|
"""
|
||||||
return not 0 <= x < CONFIG.game.columns or field[int(self.pos.y), x]
|
return not 0 <= x < CONFIG.game.columns or field[int(self.pos.y), x]
|
||||||
|
|
||||||
def horizontal_collision(self, y: int, field: np.ndarray[int, Any]) -> bool:
|
def horizontal_collision(self, y: int, field: np.ndarray[Field, Any]) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks for horizontal collision with the game field.
|
Checks for horizontal collision with the game field.
|
||||||
|
|
||||||
|
|||||||
@ -2,10 +2,11 @@ from typing import Any, Callable, Optional
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pygame
|
import pygame
|
||||||
from utils import CONFIG, Direction, Figure, Rotation, Size
|
from utils import CONFIG, Direction, Field, Figure, Rotation, Size
|
||||||
|
|
||||||
|
from game.log import log
|
||||||
|
|
||||||
from .block import Block
|
from .block import Block
|
||||||
from .log import log
|
|
||||||
|
|
||||||
|
|
||||||
class Tetromino:
|
class Tetromino:
|
||||||
@ -29,9 +30,9 @@ class Tetromino:
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
group: pygame.sprite.Group[Any],
|
group: pygame.sprite.Group,
|
||||||
create_new: Callable[[], None],
|
create_new: Callable[[], None],
|
||||||
field: np.ndarray[int, Any],
|
field: np.ndarray[Field, Any],
|
||||||
shape: Optional[Figure] = None,
|
shape: Optional[Figure] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.figure: Figure = self._generate_figure(shape)
|
self.figure: Figure = self._generate_figure(shape)
|
||||||
@ -156,7 +157,7 @@ class Tetromino:
|
|||||||
for pos in new_positions
|
for pos in new_positions
|
||||||
)
|
)
|
||||||
|
|
||||||
def _initialize_blocks(self, group: pygame.sprite.Group[Any]) -> list[Block]:
|
def _initialize_blocks(self, group: pygame.sprite.Group) -> list[Block]:
|
||||||
"""
|
"""
|
||||||
Initializes Tetromino blocks.
|
Initializes Tetromino blocks.
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,11 @@ class Font:
|
|||||||
size: int = 32
|
size: int = 32
|
||||||
|
|
||||||
|
|
||||||
|
@define
|
||||||
|
class Button:
|
||||||
|
size: Size = Size(200, 50)
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
class Window:
|
class Window:
|
||||||
title: str = "Tetris"
|
title: str = "Tetris"
|
||||||
@ -49,6 +54,7 @@ class Window:
|
|||||||
Game().size.width + SideBar().size.width + padding * 3,
|
Game().size.width + SideBar().size.width + padding * 3,
|
||||||
Game().size.height + padding * 2,
|
Game().size.height + padding * 2,
|
||||||
)
|
)
|
||||||
|
button: Button = Button()
|
||||||
|
|
||||||
|
|
||||||
@define
|
@define
|
||||||
|
|||||||
@ -5,12 +5,7 @@ class Size(NamedTuple):
|
|||||||
width: int | float
|
width: int | float
|
||||||
height: int | float
|
height: int | float
|
||||||
|
|
||||||
def add(self, other: Union["Size", int, float]) -> "Size":
|
def __sub__(self, other: Union["Size", int, float]) -> "Size":
|
||||||
if isinstance(other, Size):
|
|
||||||
return Size(self.width + other.width, self.height + other.height)
|
|
||||||
return Size(self.width + other, self.height + other)
|
|
||||||
|
|
||||||
def sub(self, other: Union["Size", int, float]) -> "Size":
|
|
||||||
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)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user