mirror of
https://github.com/kristoferssolo/2048.git
synced 2025-10-21 15:20:35 +00:00
feat(game): set Menu as primary screen
This commit is contained in:
parent
d22b5dbab2
commit
f95ca3aaf6
4
main.py
4
main.py
@ -2,12 +2,12 @@
|
||||
|
||||
|
||||
from loguru import logger
|
||||
from py2048 import Game
|
||||
from py2048 import Menu
|
||||
|
||||
|
||||
@logger.catch
|
||||
def main() -> None:
|
||||
Game().run()
|
||||
Menu().run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from .config import Config
|
||||
from .game import Game
|
||||
from .screens import Menu
|
||||
|
||||
__all__ = ["Config", "Game"]
|
||||
__all__ = ["Config", "Menu"]
|
||||
|
||||
@ -1,76 +0,0 @@
|
||||
import sys
|
||||
|
||||
import pygame
|
||||
from loguru import logger
|
||||
|
||||
from .config import Config
|
||||
from .objects import Board
|
||||
from .screens import Header, Menu
|
||||
from .utils import Direction, setup_logger
|
||||
|
||||
|
||||
class Game:
|
||||
def __init__(self) -> None:
|
||||
setup_logger()
|
||||
logger.info("Initializing game")
|
||||
|
||||
pygame.init()
|
||||
self.screen: pygame.Surface = pygame.display.set_mode(Config.SCREEN.size)
|
||||
pygame.display.set_caption("2048")
|
||||
self.board = Board()
|
||||
self.header = Header()
|
||||
self.menu = Menu()
|
||||
|
||||
def run(self) -> None:
|
||||
"""Run the game loop."""
|
||||
while True:
|
||||
self._hande_events()
|
||||
self._update()
|
||||
self._render()
|
||||
|
||||
def _update(self) -> None:
|
||||
"""Update the game."""
|
||||
self.board.update()
|
||||
|
||||
def _render(self) -> None:
|
||||
"""Render the game."""
|
||||
self.screen.fill(Config.COLORSCHEME.BG)
|
||||
# self.board.draw(self.screen)
|
||||
# self.header.draw(self.screen, 2048)
|
||||
self.menu.draw(self.screen)
|
||||
pygame.display.flip()
|
||||
|
||||
def _hande_events(self) -> None:
|
||||
"""Handle pygame events."""
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
self.exit()
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key in (pygame.K_LEFT, pygame.K_a, pygame.K_h):
|
||||
self.move_left()
|
||||
elif event.key in (pygame.K_RIGHT, pygame.K_d, pygame.K_l):
|
||||
self.move_right()
|
||||
elif event.key in (pygame.K_UP, pygame.K_w, pygame.K_k):
|
||||
self.move_up()
|
||||
elif event.key in (pygame.K_DOWN, pygame.K_s, pygame.K_j):
|
||||
self.move_down()
|
||||
elif event.key == pygame.K_q:
|
||||
self.exit()
|
||||
self.menu._handle_events(event)
|
||||
|
||||
def move_up(self) -> None:
|
||||
self.board.move(Direction.UP)
|
||||
|
||||
def move_down(self) -> None:
|
||||
self.board.move(Direction.DOWN)
|
||||
|
||||
def move_left(self) -> None:
|
||||
self.board.move(Direction.LEFT)
|
||||
|
||||
def move_right(self) -> None:
|
||||
self.board.move(Direction.RIGHT)
|
||||
|
||||
def exit(self) -> None:
|
||||
"""Exit the game."""
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
@ -18,7 +18,7 @@ class UIElement(ABC, metaclass=ABCMeta):
|
||||
font_color: str,
|
||||
font_size: int = Config.FONT.size,
|
||||
font_family: str = Config.FONT.family,
|
||||
size: Size = Size(50, 50),
|
||||
size: Optional[Size] = Size(50, 50),
|
||||
text: str = "",
|
||||
border_radius: int = 0,
|
||||
border_width: int = 0,
|
||||
|
||||
@ -42,12 +42,13 @@ class Button(ClickableUIElement, pygame.sprite.Sprite):
|
||||
|
||||
def _draw_background(self, surface: pygame.Surface) -> None:
|
||||
"""Draw a rectangle on the given surface."""
|
||||
pygame.draw.rect(
|
||||
surface,
|
||||
self.bg_color,
|
||||
(0, 0, *self.size),
|
||||
border_radius=Config.TILE.border.radius,
|
||||
)
|
||||
if self.size:
|
||||
pygame.draw.rect(
|
||||
surface,
|
||||
self.bg_color,
|
||||
(0, 0, *self.size),
|
||||
border_radius=Config.TILE.border.radius,
|
||||
)
|
||||
|
||||
def _draw_hover_background(self, surface: pygame.Surface) -> None:
|
||||
"""Draw the hover rectangle."""
|
||||
|
||||
@ -7,13 +7,15 @@ from py2048.utils import Position, Size
|
||||
from .abc import UIElement
|
||||
|
||||
|
||||
class Label(UIElement):
|
||||
class Label(UIElement, pygame.sprite.Sprite):
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.image = self._create_surface()
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.topleft = self.position
|
||||
self.update()
|
||||
|
||||
def draw(self, surface: pygame.Surface) -> None:
|
||||
"""Draw the element on the given surface."""
|
||||
@ -29,23 +31,17 @@ class Label(UIElement):
|
||||
|
||||
def _draw_background(self, surface: pygame.Surface) -> None:
|
||||
"""Draw a background for the given surface."""
|
||||
rect = (0, 0, *self.size)
|
||||
pygame.draw.rect(
|
||||
surface, self.bg_color, rect, border_radius=Config.TILE.border.radius
|
||||
) # background
|
||||
pygame.draw.rect(
|
||||
surface,
|
||||
(0, 0, 0, 0),
|
||||
rect,
|
||||
border_radius=Config.TILE.border.radius,
|
||||
width=Config.TILE.border.width,
|
||||
) # border
|
||||
if self.size:
|
||||
pygame.draw.rect(
|
||||
surface,
|
||||
self.bg_color,
|
||||
(0, 0, *self.size),
|
||||
border_radius=Config.TILE.border.radius,
|
||||
)
|
||||
|
||||
def _draw_text(self) -> None:
|
||||
"""Draw the text of the element."""
|
||||
self.rendered_text = self.font.render(
|
||||
self.text, True, self.font_color, self.bg_color
|
||||
)
|
||||
self.rendered_text = self.font.render(self.text, True, self.font_color)
|
||||
self.image.blit(
|
||||
self.rendered_text,
|
||||
self.rendered_text.get_rect(center=self.image.get_rect().center),
|
||||
|
||||
@ -39,6 +39,7 @@ class Tile(MovableUIElement, pygame.sprite.Sprite):
|
||||
self.image = self._create_surface()
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.topleft = self.position
|
||||
self.update()
|
||||
|
||||
def draw(self, surface: pygame.Surface) -> None:
|
||||
"""Draw the value of the tile."""
|
||||
@ -55,23 +56,25 @@ class Tile(MovableUIElement, pygame.sprite.Sprite):
|
||||
|
||||
def _draw_background(self, surface: pygame.Surface) -> None:
|
||||
"""Draw a rounded rectangle on the given surface."""
|
||||
pygame.draw.rect(
|
||||
surface,
|
||||
self._get_color(),
|
||||
(0, 0, *self.size),
|
||||
border_radius=Config.TILE.border.radius,
|
||||
)
|
||||
self._draw_border(surface)
|
||||
if self.size:
|
||||
pygame.draw.rect(
|
||||
surface,
|
||||
self._get_color(),
|
||||
(0, 0, *self.size),
|
||||
border_radius=Config.TILE.border.radius,
|
||||
)
|
||||
self._draw_border(surface)
|
||||
|
||||
def _draw_border(self, surface: pygame.Surface) -> None:
|
||||
"""Draw a rounded border on the given surface."""
|
||||
pygame.draw.rect(
|
||||
surface,
|
||||
(0, 0, 0, 0),
|
||||
(0, 0, *self.size),
|
||||
border_radius=Config.TILE.border.radius,
|
||||
width=Config.TILE.border.width,
|
||||
)
|
||||
if self.size:
|
||||
pygame.draw.rect(
|
||||
surface,
|
||||
(0, 0, 0, 0),
|
||||
(0, 0, *self.size),
|
||||
border_radius=Config.TILE.border.radius,
|
||||
width=Config.TILE.border.width,
|
||||
)
|
||||
|
||||
def _draw_text(self) -> None:
|
||||
"""Draw the text of the sprite."""
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
from .game import Game
|
||||
from .header import Header
|
||||
from .menu import Menu
|
||||
|
||||
__all__ = ["Header", "Menu"]
|
||||
__all__ = ["Header", "Menu", "Game"]
|
||||
|
||||
43
src/py2048/screens/game.py
Normal file
43
src/py2048/screens/game.py
Normal file
@ -0,0 +1,43 @@
|
||||
import pygame
|
||||
from loguru import logger
|
||||
|
||||
from py2048 import Config
|
||||
from py2048.objects import Board
|
||||
from py2048.utils import Direction, setup_logger
|
||||
|
||||
from .header import Header
|
||||
|
||||
|
||||
class Game:
|
||||
def __init__(self) -> None:
|
||||
self.header = Header()
|
||||
self.board = Board()
|
||||
|
||||
def draw(self, surface: pygame.Surface) -> None:
|
||||
surface.fill(Config.COLORSCHEME.BG)
|
||||
self.board.draw(surface)
|
||||
self.header.draw(surface, 2048)
|
||||
pygame.display.flip()
|
||||
|
||||
def handle_events(self, event: pygame.Event) -> None:
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key in (pygame.K_LEFT, pygame.K_a, pygame.K_h):
|
||||
self.move_left()
|
||||
elif event.key in (pygame.K_RIGHT, pygame.K_d, pygame.K_l):
|
||||
self.move_right()
|
||||
elif event.key in (pygame.K_UP, pygame.K_w, pygame.K_k):
|
||||
self.move_up()
|
||||
elif event.key in (pygame.K_DOWN, pygame.K_s, pygame.K_j):
|
||||
self.move_down()
|
||||
|
||||
def move_up(self) -> None:
|
||||
self.board.move(Direction.UP)
|
||||
|
||||
def move_down(self) -> None:
|
||||
self.board.move(Direction.DOWN)
|
||||
|
||||
def move_left(self) -> None:
|
||||
self.board.move(Direction.LEFT)
|
||||
|
||||
def move_right(self) -> None:
|
||||
self.board.move(Direction.RIGHT)
|
||||
@ -8,20 +8,32 @@ from py2048.utils import Position, Size
|
||||
class Header:
|
||||
def __init__(self) -> None:
|
||||
self.rect = pygame.Rect(0, 0, *Config.HEADER.size)
|
||||
self.labels = self._create_labels()
|
||||
|
||||
def draw(self, screen: pygame.Surface, score: int) -> None:
|
||||
"""Draw the header."""
|
||||
self.label = Label(
|
||||
text=f"{score}",
|
||||
def _create_labels(self) -> pygame.sprite.Group:
|
||||
score = Label(
|
||||
text=f"SCORE\n{0}",
|
||||
size=Size(50, 50),
|
||||
position=Position(0, 0),
|
||||
bg_color=Config.COLORSCHEME.BOARD_BG,
|
||||
font_color=Config.COLORSCHEME.DARK_TEXT,
|
||||
font_color=Config.COLORSCHEME.LIGHT_TEXT,
|
||||
font_size=16,
|
||||
)
|
||||
self.label.draw(screen)
|
||||
highscore = Label(
|
||||
text=f"HIGHSCORE\n{2048}",
|
||||
size=Size(50, 50),
|
||||
position=Position(200, 0),
|
||||
bg_color=Config.COLORSCHEME.BOARD_BG,
|
||||
font_color=Config.COLORSCHEME.LIGHT_TEXT,
|
||||
font_size=16,
|
||||
)
|
||||
return pygame.sprite.Group(score, highscore)
|
||||
|
||||
def draw(self, screen: pygame.Surface, score: int) -> None:
|
||||
"""Draw the header."""
|
||||
self.labels.draw(screen)
|
||||
|
||||
def update(self, score: int) -> None:
|
||||
"""Update the header."""
|
||||
self.label.text = f"SCORE\n{score}"
|
||||
self.label.update()
|
||||
# self.labels. = f"SCORE\n{score}"
|
||||
self.labels.update()
|
||||
|
||||
@ -1,52 +1,99 @@
|
||||
import sys
|
||||
|
||||
import pygame
|
||||
from loguru import logger
|
||||
|
||||
from py2048 import Config
|
||||
from py2048.objects import Button
|
||||
from py2048.utils import Position
|
||||
from py2048.utils import Position, setup_logger
|
||||
|
||||
from .game import Game
|
||||
|
||||
|
||||
class Menu(pygame.sprite.AbstractGroup):
|
||||
class Menu:
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
setup_logger()
|
||||
pygame.init()
|
||||
pygame.display.set_caption("2048")
|
||||
self._surface: pygame.Surface = pygame.display.set_mode(Config.SCREEN.size)
|
||||
|
||||
buttons_data = {
|
||||
logger.info("Initializing game")
|
||||
|
||||
self._buttons_data = {
|
||||
"Play": self.play,
|
||||
"AI": self.ai,
|
||||
"Settings": self.settings,
|
||||
"Exit": self.exit,
|
||||
}
|
||||
button_width, button_height = 120, 50
|
||||
self._game_active = False
|
||||
self._ai_active = False
|
||||
self._setting_active = False
|
||||
self._buttons = self._create_buttons()
|
||||
|
||||
for index, (text, action) in enumerate(buttons_data.items(), start=1):
|
||||
self.add(
|
||||
def _create_buttons(self) -> pygame.sprite.Group:
|
||||
"""Create the buttons."""
|
||||
width, height = 120, 50
|
||||
buttons = pygame.sprite.Group()
|
||||
for index, (text, action) in enumerate(self._buttons_data.items(), start=1):
|
||||
buttons.add(
|
||||
Button(
|
||||
position=Position(
|
||||
Config.SCREEN.size.width / 2 - button_width / 2,
|
||||
Config.SCREEN.size.height / len(buttons_data) * index
|
||||
- button_height,
|
||||
position=(
|
||||
Config.SCREEN.size.width / 2 - width / 2,
|
||||
Config.SCREEN.size.height / len(self._buttons_data) * index
|
||||
- height,
|
||||
),
|
||||
bg_color=Config.COLORSCHEME.BOARD_BG,
|
||||
font_color=Config.COLORSCHEME.LIGHT_TEXT,
|
||||
hover_color=Config.COLORSCHEME.TILE_0,
|
||||
size=(button_width, button_height),
|
||||
size=(width, height),
|
||||
text=text,
|
||||
border_radius=Config.TILE.border.radius,
|
||||
action=action,
|
||||
)
|
||||
)
|
||||
|
||||
def _handle_events(self, event: pygame.event.Event) -> None:
|
||||
"""Handle the event."""
|
||||
if event.type == pygame.MOUSEBUTTONDOWN:
|
||||
for button in self.sprites():
|
||||
button.on_click(Position(*event.pos))
|
||||
elif event.type == pygame.MOUSEMOTION:
|
||||
for button in self.sprites():
|
||||
button.on_hover(Position(*event.pos))
|
||||
return buttons
|
||||
|
||||
def draw(self) -> None:
|
||||
self._surface.fill(Config.COLORSCHEME.BG)
|
||||
self._buttons.draw(self._surface)
|
||||
pygame.display.flip()
|
||||
|
||||
def run(self) -> None:
|
||||
"""Run the game loop."""
|
||||
while True:
|
||||
self._hande_events()
|
||||
|
||||
if self._game_active:
|
||||
self.game.draw(self._surface)
|
||||
elif self._ai_active:
|
||||
pass
|
||||
elif self._setting_active:
|
||||
pass
|
||||
else:
|
||||
self.draw()
|
||||
|
||||
def _hande_events(self) -> None:
|
||||
"""Handle pygame events."""
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
self.exit()
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
for button in self._buttons:
|
||||
button.on_click(Position(*event.pos))
|
||||
elif event.type == pygame.MOUSEMOTION:
|
||||
for button in self._buttons:
|
||||
button.on_hover(Position(*event.pos))
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_q:
|
||||
self.exit()
|
||||
if self._game_active:
|
||||
self.game.handle_events(event)
|
||||
|
||||
def play(self) -> None:
|
||||
logger.debug("Play")
|
||||
logger.debug("Launching game")
|
||||
self._game_active = True
|
||||
self.game = Game()
|
||||
|
||||
def ai(self) -> None:
|
||||
logger.debug("AI")
|
||||
@ -55,4 +102,7 @@ class Menu(pygame.sprite.AbstractGroup):
|
||||
logger.debug("Settings")
|
||||
|
||||
def exit(self) -> None:
|
||||
logger.debug("Exit")
|
||||
"""Exit the game."""
|
||||
logger.debug("Exiting")
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user