mirror of
https://github.com/kristoferssolo/2048.git
synced 2025-10-21 15:20:35 +00:00
fix(game): no more duplicates
This commit is contained in:
parent
276ed26aee
commit
54787e727b
@ -12,7 +12,7 @@ from .utils import Direction, grid_pos
|
|||||||
|
|
||||||
|
|
||||||
class Block(pygame.sprite.Sprite):
|
class Block(pygame.sprite.Sprite):
|
||||||
def __init__(self, x: int, y: int, value: int | None = 2):
|
def __init__(self, x: int, y: int, group: pygame.sprite.Group, value: int | None = 2):
|
||||||
"""Initialize a block"""
|
"""Initialize a block"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.image = pygame.Surface((Config.BLOCK_SIZE, Config.BLOCK_SIZE))
|
self.image = pygame.Surface((Config.BLOCK_SIZE, Config.BLOCK_SIZE))
|
||||||
@ -21,6 +21,7 @@ class Block(pygame.sprite.Sprite):
|
|||||||
|
|
||||||
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.font = pygame.font.SysFont(Config.FONT_FAMILY, Config.FONT_SIZE)
|
||||||
|
self.group = group
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def _draw_value(self) -> None:
|
def _draw_value(self) -> None:
|
||||||
@ -46,7 +47,9 @@ class Block(pygame.sprite.Sprite):
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.group.remove(self)
|
||||||
self.rect.topleft = new_x, new_y
|
self.rect.topleft = new_x, new_y
|
||||||
|
self.group.add(self)
|
||||||
|
|
||||||
def _calc_new_pos(self, direction: Direction) -> tuple[int, int]:
|
def _calc_new_pos(self, direction: Direction) -> tuple[int, int]:
|
||||||
"""Calculate the new position of the block."""
|
"""Calculate the new position of the block."""
|
||||||
@ -59,22 +62,21 @@ class Block(pygame.sprite.Sprite):
|
|||||||
|
|
||||||
def _has_collision(self, x: int, y: int) -> bool:
|
def _has_collision(self, x: int, y: int) -> bool:
|
||||||
"""Checks whether the block has a collision with any other block."""
|
"""Checks whether the block has a collision with any other block."""
|
||||||
groups = self.groups()
|
return any(block.rect.collidepoint(x, y) for block in self.group if block != self)
|
||||||
if not groups or not groups[0]:
|
|
||||||
return False
|
|
||||||
return any(block.rect.collidepoint(x, y) for block in self.groups()[0] if block != self)
|
|
||||||
|
|
||||||
def _get_collided_block(self, x: int, y: int) -> Union["Block", None]:
|
def _get_collided_block(self, x: int, y: int) -> Union["Block", None]:
|
||||||
"""Get the block that collides with the given block."""
|
"""Get the block that collides with the given block."""
|
||||||
|
|
||||||
return next((block for block in self.groups()[0] 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:
|
def _merge(self, other: "Block") -> None:
|
||||||
"""Merge the block with another block."""
|
"""Merge the block with another block."""
|
||||||
|
self.group.remove(other)
|
||||||
|
self.group.remove(self)
|
||||||
self.value += other.value
|
self.value += other.value
|
||||||
self.update()
|
self.update()
|
||||||
logger.debug(f"Merging block({id(self)}) with block({id(other)})")
|
logger.debug(f"Merging block({id(self)}) with block({id(other)})")
|
||||||
self.groups()[0].remove(other)
|
self.group.add(self)
|
||||||
|
|
||||||
def update(self) -> None:
|
def update(self) -> None:
|
||||||
"""Update the block"""
|
"""Update the block"""
|
||||||
@ -100,7 +102,7 @@ class Block(pygame.sprite.Sprite):
|
|||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""Return a string representation of the block"""
|
"""Return a string representation of the block"""
|
||||||
return f"Block({id(self)}): ({self.pos()})"
|
return f"Block({id(self)}): {self.pos()} num={self.value}"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
"""Return a string representation of the block"""
|
"""Return a string representation of the block"""
|
||||||
|
|||||||
42
src/py2048/board.py
Normal file
42
src/py2048/board.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
from .block import Block
|
||||||
|
from .config import Config
|
||||||
|
from .utils import Direction
|
||||||
|
|
||||||
|
|
||||||
|
class Board(pygame.sprite.Group):
|
||||||
|
def move(self, direction: Direction):
|
||||||
|
blocks = self.sprites()
|
||||||
|
block: Block
|
||||||
|
|
||||||
|
if direction in {Direction.DOWN, Direction.RIGHT}:
|
||||||
|
blocks.sort(key=lambda block: (block.rect.x, block.rect.y), reverse=True)
|
||||||
|
|
||||||
|
for block in blocks:
|
||||||
|
block.move(direction)
|
||||||
|
|
||||||
|
self.generate_block()
|
||||||
|
|
||||||
|
def generate_block(self, amount: int = 1, *pos: tuple[int, int]) -> None:
|
||||||
|
"""Generate `amount` number of blocks."""
|
||||||
|
if pos:
|
||||||
|
for coords in pos:
|
||||||
|
x, y = coords[0] * Config.BLOCK_SIZE, coords[1] * Config.BLOCK_SIZE
|
||||||
|
self.add(Block(x, y, self))
|
||||||
|
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)
|
||||||
|
|
||||||
|
colliding_blocks = pygame.sprite.spritecollide(block, self, False) # check for collisions
|
||||||
|
|
||||||
|
if not colliding_blocks:
|
||||||
|
self.add(block)
|
||||||
|
break
|
||||||
@ -3,9 +3,10 @@ import sys
|
|||||||
import pygame
|
import pygame
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from .board import Board
|
||||||
|
|
||||||
from .colors import COLORS
|
from .colors import COLORS
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .grid import Grid
|
|
||||||
from .logger import setup_logger
|
from .logger import setup_logger
|
||||||
from .utils import Direction
|
from .utils import Direction
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ class Game:
|
|||||||
pygame.init()
|
pygame.init()
|
||||||
self.screen: pygame.Surface = pygame.display.set_mode((Config.WIDTH, Config.HEIGHT))
|
self.screen: pygame.Surface = pygame.display.set_mode((Config.WIDTH, Config.HEIGHT))
|
||||||
pygame.display.set_caption("2048")
|
pygame.display.set_caption("2048")
|
||||||
self.blocks = Grid()
|
self.blocks = Board()
|
||||||
self.blocks.generate_block(Config.INITIAL_BLOCK_COUNT)
|
self.blocks.generate_block(Config.INITIAL_BLOCK_COUNT)
|
||||||
# self.blocks.generate_block(2, (1, 1), (1, 3))
|
# self.blocks.generate_block(2, (1, 1), (1, 3))
|
||||||
|
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
import random
|
|
||||||
|
|
||||||
import pygame
|
|
||||||
|
|
||||||
from .block import Block
|
|
||||||
from .config import Config
|
|
||||||
from .utils import Direction
|
|
||||||
|
|
||||||
|
|
||||||
class Grid(pygame.sprite.Group):
|
|
||||||
def move(self, direction: Direction):
|
|
||||||
blocks = list(self.sprites())
|
|
||||||
|
|
||||||
match direction:
|
|
||||||
case Direction.DOWN:
|
|
||||||
blocks.sort(key=lambda block: block.rect.y, reverse=True)
|
|
||||||
case Direction.RIGHT:
|
|
||||||
blocks.sort(key=lambda block: block.rect.x, reverse=True)
|
|
||||||
|
|
||||||
for block in blocks:
|
|
||||||
block: Block
|
|
||||||
block.move(direction)
|
|
||||||
|
|
||||||
self.generate_block()
|
|
||||||
|
|
||||||
def generate_block(self, amount: int = 1, *pos: tuple[int, int]) -> None:
|
|
||||||
"""Generate `amount` number of blocks."""
|
|
||||||
if pos:
|
|
||||||
for coords in pos:
|
|
||||||
self.add(Block(coords[0] * Config.BLOCK_SIZE, coords[1] * Config.BLOCK_SIZE))
|
|
||||||
return
|
|
||||||
for _ in range(amount):
|
|
||||||
while True:
|
|
||||||
x = random.randint(0, 3) * Config.BLOCK_SIZE # random column position
|
|
||||||
y = random.randint(0, 3) * Config.BLOCK_SIZE # random row position
|
|
||||||
block = Block(x, y)
|
|
||||||
|
|
||||||
colliding_blocks = pygame.sprite.spritecollide(block, self, False) # check collision
|
|
||||||
|
|
||||||
if not colliding_blocks:
|
|
||||||
self.add(block)
|
|
||||||
break
|
|
||||||
Loading…
Reference in New Issue
Block a user