mirror of
https://github.com/kristoferssolo/2048.git
synced 2025-10-21 15:20:35 +00:00
145 lines
4.5 KiB
Python
145 lines
4.5 KiB
Python
import random
|
|
from typing import Optional
|
|
|
|
import pygame
|
|
from loguru import logger
|
|
|
|
from py2048 import Config
|
|
from py2048.utils import Direction, Position
|
|
|
|
from .tile import Tile
|
|
|
|
|
|
class Board(pygame.sprite.Group):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.rect = pygame.Rect(0, 0, *Config.BOARD.size)
|
|
self.score: int = 0
|
|
self.rect.x, self.rect.y = Config.BOARD.pos
|
|
self._initiate_game()
|
|
|
|
def _initiate_game(self) -> None:
|
|
"""Initiate the game."""
|
|
self.generate_initial_tiles()
|
|
|
|
def draw(self, surface: pygame.Surface) -> None:
|
|
"""Draw the board."""
|
|
self._draw_background(surface)
|
|
super().draw(surface)
|
|
|
|
def _draw_background(self, surface: pygame.Surface) -> None:
|
|
"""Draw the board background."""
|
|
pygame.draw.rect(
|
|
surface,
|
|
Config.COLORSCHEME.BOARD_BG,
|
|
self.rect,
|
|
border_radius=Config.TILE.border.radius,
|
|
) # background
|
|
pygame.draw.rect(
|
|
surface,
|
|
Config.COLORSCHEME.BOARD_BG,
|
|
self.rect,
|
|
width=Config.TILE.border.width,
|
|
border_radius=Config.TILE.border.radius,
|
|
) # border
|
|
|
|
def move(self, direction: Direction) -> None:
|
|
"""Move the tiles in the specified direction."""
|
|
tiles = self.sprites()
|
|
tile: Tile
|
|
|
|
match direction:
|
|
case Direction.UP:
|
|
tiles.sort(key=lambda tile: tile.rect.y)
|
|
case Direction.DOWN:
|
|
tiles.sort(key=lambda tile: tile.rect.y, reverse=True)
|
|
case Direction.LEFT:
|
|
tiles.sort(key=lambda tile: tile.rect.x)
|
|
case Direction.RIGHT:
|
|
tiles.sort(key=lambda tile: tile.rect.x, reverse=True)
|
|
|
|
for tile in tiles:
|
|
self.score += tile.move(direction)
|
|
|
|
if not self._is_full():
|
|
self.generate_random_tile()
|
|
|
|
def generate_initial_tiles(self) -> None:
|
|
"""Generate the initial tiles."""
|
|
self.generate_tile(Config.TILE.initial_count)
|
|
|
|
def generate_tile(self, amount: int = 1, *pos: Position) -> None:
|
|
"""Generate `amount` number of tiles or at the specified positions."""
|
|
if pos:
|
|
for coords in pos:
|
|
x, y = coords.x * Config.TILE.size, coords.y * Config.TILE.size
|
|
self.add(Tile(Position(x, y), self))
|
|
return
|
|
|
|
for _ in range(amount):
|
|
self.generate_random_tile()
|
|
|
|
def generate_random_tile(self) -> None:
|
|
"""Generate a tile with random coordinates aligned with the grid."""
|
|
while True:
|
|
# Generate random coordinates aligned with the grid
|
|
x = random.randint(0, 3) * Config.TILE.size + Config.BOARD.pos.x
|
|
y = random.randint(0, 3) * Config.TILE.size + Config.BOARD.pos.y
|
|
tile = Tile(Position(x, y), self)
|
|
|
|
colliding_tiles = pygame.sprite.spritecollide(
|
|
tile, self, False
|
|
) # check for collisions
|
|
|
|
if not colliding_tiles:
|
|
self.add(tile)
|
|
return
|
|
|
|
def _is_full(self) -> bool:
|
|
"""Check if the board is full."""
|
|
return len(self.sprites()) == Config.BOARD.len**2
|
|
|
|
def _can_move(self) -> bool:
|
|
"""Check if any movement is possible on the board."""
|
|
tile: Tile
|
|
for tile in self.sprites():
|
|
if tile.can_move():
|
|
return True
|
|
return False
|
|
|
|
def is_game_over(self) -> bool:
|
|
"""Check if the game is over."""
|
|
return self._is_full() and not self._can_move()
|
|
|
|
def reset(self) -> None:
|
|
"""Reset the board."""
|
|
self.empty()
|
|
self._initiate_game()
|
|
|
|
def max_val(self) -> int:
|
|
"""Return the maximum value of the tiles."""
|
|
tile: Tile
|
|
return int(max(tile.value for tile in self.sprites()))
|
|
|
|
def get_tile(self, position: Position) -> Optional[Tile]:
|
|
"""Return the tile at the specified position."""
|
|
tile: Tile
|
|
for tile in self.sprites():
|
|
if tile.pos == position:
|
|
return tile
|
|
return None
|
|
|
|
def matrix(self) -> list[int]:
|
|
"""Return a 1d matrix of values of the tiles."""
|
|
matrix: list[int] = []
|
|
|
|
for i in range(1, Config.BOARD.len + 1):
|
|
for j in range(1, Config.BOARD.len + 1):
|
|
tile = self.get_tile(Position(j, i))
|
|
if tile:
|
|
matrix.append(tile.value)
|
|
else:
|
|
matrix.append(0)
|
|
|
|
return matrix
|