diff --git a/src/py2048/block.py b/src/py2048/block.py index 8b895a5..89195df 100644 --- a/src/py2048/block.py +++ b/src/py2048/block.py @@ -61,10 +61,11 @@ class Block(pygame.sprite.Sprite): def _is_out_if_bounds(self, x: int, y: int) -> bool: """Return whether the block is out of bounds.""" - return not ( - 0 <= x <= Config.GRID_WIDTH - Config.BLOCK_SIZE - and 0 <= y <= Config.GRID_HEIGHT - Config.BLOCK_SIZE - ) + board_left = Config.BOARD_X + board_right = Config.BOARD_X + Config.BOARD_WIDTH - Config.BLOCK_SIZE + board_top = Config.BOARD_Y + board_bottom = Config.BOARD_Y + Config.BOARD_HEIGHT - Config.BLOCK_SIZE + return not (board_left <= x <= board_right and board_top <= y <= board_bottom) def _has_collision(self, x: int, y: int) -> bool: """Checks whether the block has a collision with any other block.""" diff --git a/src/py2048/board.py b/src/py2048/board.py index 2856717..d8a58ca 100644 --- a/src/py2048/board.py +++ b/src/py2048/board.py @@ -10,12 +10,26 @@ from .utils import Direction class Board(pygame.sprite.Group): - def __init__(self, screen: pygame.Surface): + def __init__(self): super().__init__() - self.screen = screen + self.rect = pygame.Rect(0, 0, Config.BOARD_WIDTH, Config.BOARD_HEIGHT) + self.rect.x = Config.BOARD_X + self.rect.y = Config.BOARD_Y + self.initiate_game() + + def initiate_game(self) -> None: + """Initiate the game.""" self.generate_initial_blocks() + def draw(self, screen: pygame.Surface) -> None: + """Draw the board.""" + block: Block + pygame.draw.rect(screen, Color.YELLOW, self.rect, 2) + + super().draw(screen) + def move(self, direction: Direction): + """Move the blocks in the specified direction.""" blocks = self.sprites() block: Block @@ -54,8 +68,8 @@ class Board(pygame.sprite.Group): """Generate a block with random coordinates aligned with the grid.""" while True: # Generate random coordinates aligned with the grid - x = random.randint(0, 3) * Config.BLOCK_SIZE - y = random.randint(0, 3) * Config.BLOCK_SIZE + x = random.randint(0, 3) * Config.BLOCK_SIZE + Config.BOARD_X + y = random.randint(0, 3) * Config.BLOCK_SIZE + Config.BOARD_Y block = Block(x, y, self) colliding_blocks = pygame.sprite.spritecollide( @@ -64,12 +78,11 @@ class Board(pygame.sprite.Group): if not colliding_blocks: self.add(block) - logger.debug(f"Created block at {block.pos}") - break + return def _is_full(self) -> bool: """Check if the board is full.""" - return len(self.sprites()) == Config.GRID_SIZE**2 + return len(self.sprites()) == Config.BOARD_SIZE**2 def _can_move(self) -> bool: """Check if any movement is possible on the board.""" @@ -83,7 +96,7 @@ class Board(pygame.sprite.Group): """Check if the game is over.""" return self._is_full() and not self._can_move() - def restart(self) -> None: - """Restart the game.""" + def reset(self) -> None: + """Reset the board.""" self.empty() - self.generate_initial_blocks() + self.initiate_game() diff --git a/src/py2048/config.py b/src/py2048/config.py index 5b45dc4..e80e217 100644 --- a/src/py2048/config.py +++ b/src/py2048/config.py @@ -1,11 +1,25 @@ class Config: FONT_FAMILY = "Roboto" - FONT_SIZE = 40 - GRID_SIZE = 4 + FONT_SIZE = 32 + + BOARD_SIZE = 4 BLOCK_SIZE = 50 - WIDTH = GRID_SIZE * BLOCK_SIZE - HEIGHT = GRID_SIZE * BLOCK_SIZE + 100 - GRID_HEIGHT = GRID_SIZE * BLOCK_SIZE - GRID_WIDTH = GRID_SIZE * BLOCK_SIZE + + # WIDTH = BOARD_SIZE * BLOCK_SIZE + BLOCK_SIZE + # HEIGHT = BOARD_SIZE * BLOCK_SIZE + BLOCK_SIZE * 2 + + BOARD_WIDTH = BOARD_SIZE * BLOCK_SIZE + BOARD_HEIGHT = BOARD_SIZE * BLOCK_SIZE + + HEADER_WIDTH = BOARD_WIDTH + BLOCK_SIZE + HEADER_HEIGHT = BLOCK_SIZE + + BOARD_X = BLOCK_SIZE // 2 + BOARD_Y = HEADER_HEIGHT + BLOCK_SIZE // 2 + + SCREEN_WIDTH = HEADER_WIDTH + SCREEN_HEIGHT = BOARD_HEIGHT + BLOCK_SIZE + HEADER_HEIGHT + SCREEN_SIZE = SCREEN_WIDTH, SCREEN_HEIGHT + INITIAL_BLOCK_COUNT = 2 BLOCK_VALUE_PROBABILITY = 0.9 diff --git a/src/py2048/game.py b/src/py2048/game.py index a608273..0497071 100644 --- a/src/py2048/game.py +++ b/src/py2048/game.py @@ -7,6 +7,7 @@ from .board import Board from .color import Color from .config import Config from .logger import setup_logger +from .screens.header import Header from .utils import Direction @@ -16,11 +17,10 @@ class Game: logger.info("Initializing game") pygame.init() - self.screen: pygame.Surface = pygame.display.set_mode( - (Config.WIDTH, Config.HEIGHT) - ) + self.screen: pygame.Surface = pygame.display.set_mode(Config.SCREEN_SIZE) pygame.display.set_caption("2048") - self.blocks = Board(self.screen) + self.board = Board() + self.header = Header() def run(self) -> None: """Run the game loop.""" @@ -31,12 +31,13 @@ class Game: def _update(self) -> None: """Update the game.""" - self.blocks.update() + self.board.update() def _render(self) -> None: """Render the game.""" self.screen.fill(Color.BG) - self.blocks.draw(self.screen) + self.board.draw(self.screen) + self.header.draw(self.screen) pygame.display.flip() def _hande_events(self) -> None: @@ -57,16 +58,16 @@ class Game: self.exit() def move_up(self) -> None: - self.blocks.move(Direction.UP) + self.board.move(Direction.UP) def move_down(self) -> None: - self.blocks.move(Direction.DOWN) + self.board.move(Direction.DOWN) def move_left(self) -> None: - self.blocks.move(Direction.LEFT) + self.board.move(Direction.LEFT) def move_right(self) -> None: - self.blocks.move(Direction.RIGHT) + self.board.move(Direction.RIGHT) def exit(self) -> None: """Exit the game.""" diff --git a/src/py2048/screens/header.py b/src/py2048/screens/header.py new file mode 100644 index 0000000..619eb33 --- /dev/null +++ b/src/py2048/screens/header.py @@ -0,0 +1,14 @@ +import pygame + +from ..color import Color +from ..config import Config + + +class Header: + def __init__(self) -> None: + self.rect = pygame.Rect(0, 0, Config.HEADER_WIDTH, Config.HEADER_HEIGHT) + self.score = 0 # TODO: Implement score + + def draw(self, screen: pygame.Surface) -> None: + """Draw the header.""" + pygame.draw.rect(screen, Color.MAGENTA, self.rect, 2)