diff --git a/src/py2048/block.py b/src/py2048/block.py index b0f4cde..ea175f1 100644 --- a/src/py2048/block.py +++ b/src/py2048/block.py @@ -9,14 +9,22 @@ from .utils import Direction, grid_pos class Block(pygame.sprite.Sprite): - def __init__(self, x: int, y: int, group: pygame.sprite.Group, value: int | None = 2): + def __init__( + self, x: int, y: int, group: pygame.sprite.Group, value: int | None = 2 + ): """Initialize a block""" super().__init__() self.image = pygame.Surface((Config.BLOCK_SIZE, Config.BLOCK_SIZE)) self.rect = self.image.get_rect() self.rect.topleft = x, y - self.value: int = value if value is not None else 2 if random.random() <= Config.BLOCK_VALUE_PROBABILITY else 4 + self.value: int = ( + value + if value is not None + else 2 + if random.random() <= Config.BLOCK_VALUE_PROBABILITY + else 4 + ) self.font = pygame.font.SysFont(Config.FONT_FAMILY, Config.FONT_SIZE) self.group = group self.update() @@ -55,16 +63,28 @@ 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.WIDTH - Config.BLOCK_SIZE and 0 <= y <= Config.HEIGHT - Config.BLOCK_SIZE) + return not ( + 0 <= x <= Config.WIDTH - Config.BLOCK_SIZE + and 0 <= y <= Config.HEIGHT - Config.BLOCK_SIZE + ) def _has_collision(self, x: int, y: int) -> bool: """Checks whether the block has a collision with any other block.""" - return any(block.rect.collidepoint(x, y) for block in self.group if block != self) + return any( + block.rect.collidepoint(x, y) for block in self.group if block != self + ) def _get_collided_block(self, x: int, y: int) -> Union["Block", None]: """Get the block that collides with the given block.""" - return next((block for block in self.group if block != self and block.rect.collidepoint(x, y)), None) + return next( + ( + block + for block in self.group + if block != self and block.rect.collidepoint(x, y) + ), + None, + ) def _merge(self, other: "Block") -> None: """Merge the block with another block.""" @@ -98,7 +118,7 @@ class Block(pygame.sprite.Sprite): def __repr__(self) -> str: """Return a string representation of the block""" - return f"Block({id(self)}): {self.pos()} num={self.value}" + return f"Block({id(self)}): {self.pos} num={self.value}" def __str__(self) -> str: """Return a string representation of the block""" @@ -108,6 +128,7 @@ class Block(pygame.sprite.Sprite): """Return a hash of the block""" return hash((self.rect.x, self.rect.y, self.value)) + @property def pos(self) -> tuple[int, int]: """Return the position of the block""" return grid_pos(self.rect.x), grid_pos(self.rect.y) diff --git a/src/py2048/board.py b/src/py2048/board.py index cb07153..b0b4b91 100644 --- a/src/py2048/board.py +++ b/src/py2048/board.py @@ -12,9 +12,8 @@ from .utils import Direction class Board(pygame.sprite.Group): def __init__(self, screen: pygame.Surface): super().__init__() - self.generate_block(Config.INITIAL_BLOCK_COUNT) self.screen = screen - self._draw_grid() + self.generate_initial_blocks() def move(self, direction: Direction): blocks = self.sprites() @@ -33,19 +32,14 @@ class Board(pygame.sprite.Group): for block in blocks: block.move(direction) - self.generate_block() + self.generate_random_block() - def _draw_grid(self) -> None: - """Draw the grid.""" - for x in range(0, Config.WIDTH + 20, Config.BLOCK_SIZE): - pygame.draw.line( - self.screen, Color.BG_HIGHLIGHT, (x, 0), (x, Config.HEIGHT) - ) - for y in range(0, Config.HEIGHT + 20, Config.BLOCK_SIZE): - pygame.draw.line(self.screen, Color.BG_HIGHLIGHT, (0, y), (Config.WIDTH, y)) + def generate_initial_blocks(self) -> None: + """Generate the initial blocks.""" + self.generate_block(Config.INITIAL_BLOCK_COUNT) def generate_block(self, amount: int = 1, *pos: tuple[int, int]) -> None: - """Generate `amount` number of blocks.""" + """Generate `amount` number of blocks or at the specified positions.""" if pos: for coords in pos: x, y = coords[0] * Config.BLOCK_SIZE, coords[1] * Config.BLOCK_SIZE @@ -53,17 +47,19 @@ class Board(pygame.sprite.Group): return for _ in range(amount): - 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 - block = Block(x, y, self) + self.generate_random_block() - colliding_blocks = pygame.sprite.spritecollide( - block, self, False - ) # check for collisions + def generate_random_block(self) -> None: + """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 + block = Block(x, y, self) - if not colliding_blocks: - self.add(block) - logger.debug(f"Created block at {block.pos()}") - break + colliding_blocks = pygame.sprite.spritecollide(block, self, False) # check for collisions + + if not colliding_blocks: + self.add(block) + logger.debug(f"Created block at {block.pos}") + break