Project rename and restructure
This commit is contained in:
0
hopper/models/__init__.py
Normal file
0
hopper/models/__init__.py
Normal file
111
hopper/models/board.py
Normal file
111
hopper/models/board.py
Normal file
@ -0,0 +1,111 @@
|
||||
import random
|
||||
from copy import copy
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
from hopper.enums import ObjectType
|
||||
from hopper.models.player import Player, Position
|
||||
|
||||
BOARD_DUMP_CHARS: dict[ObjectType, str] = {
|
||||
ObjectType.NONE: "·",
|
||||
ObjectType.OBSTACLE: "✘",
|
||||
ObjectType.PLAYER: "⯧",
|
||||
ObjectType.DESTINATION: "⬤",
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class LayerObject:
|
||||
type_: ObjectType
|
||||
position: Position
|
||||
|
||||
|
||||
@dataclass
|
||||
class Layer:
|
||||
name: Optional[str] = None
|
||||
objects: list[LayerObject] = field(default_factory=list)
|
||||
|
||||
def get_object_at_position(self, position: Position) -> Optional[LayerObject]:
|
||||
for obj in self.objects:
|
||||
if obj.position == position:
|
||||
return obj
|
||||
return None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Destination:
|
||||
position: Position
|
||||
|
||||
|
||||
@dataclass
|
||||
class GameBoard:
|
||||
width: int
|
||||
height: int
|
||||
destination: Destination
|
||||
layers: list[Layer] = field(default_factory=list)
|
||||
|
||||
def dump(self) -> list[list[str]]:
|
||||
board = [
|
||||
[BOARD_DUMP_CHARS[ObjectType.NONE] for _ in range(self.width)]
|
||||
for _ in range(self.height)
|
||||
]
|
||||
|
||||
for layer in self.layers:
|
||||
for obj in layer.objects:
|
||||
board[obj.position.y][obj.position.x] = BOARD_DUMP_CHARS[
|
||||
ObjectType.OBSTACLE
|
||||
]
|
||||
|
||||
board[self.destination.position.y][
|
||||
self.destination.position.x
|
||||
] = BOARD_DUMP_CHARS[ObjectType.DESTINATION]
|
||||
|
||||
return board
|
||||
|
||||
def get_object_at_position(self, position: Position) -> Optional[LayerObject]:
|
||||
for layer in self.layers:
|
||||
obj = layer.get_object_at_position(position)
|
||||
if obj is not None:
|
||||
return obj
|
||||
return None
|
||||
|
||||
|
||||
class BoardLayout:
|
||||
def __init__(self, board: GameBoard, players: list[Player]) -> None:
|
||||
self.board = board
|
||||
self.players = players
|
||||
self.layers = self.__create_layers()
|
||||
|
||||
def __create_layers(self) -> list[Layer]:
|
||||
layers = copy(self.board.layers)
|
||||
layers.append(
|
||||
Layer(
|
||||
name="destination",
|
||||
objects=[
|
||||
LayerObject(
|
||||
type_=ObjectType.DESTINATION,
|
||||
position=self.board.destination.position,
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
layers.append(
|
||||
Layer(
|
||||
name="players",
|
||||
objects=[
|
||||
LayerObject(
|
||||
type_=ObjectType.PLAYER,
|
||||
position=player.position,
|
||||
)
|
||||
for player in self.players
|
||||
],
|
||||
)
|
||||
)
|
||||
return layers
|
||||
|
||||
|
||||
def create_random_position(board_width: int, board_height: int) -> Position:
|
||||
return Position(
|
||||
x=random.randint(0, board_width - 1),
|
||||
y=random.randint(0, board_height - 1),
|
||||
)
|
||||
29
hopper/models/config.py
Normal file
29
hopper/models/config.py
Normal file
@ -0,0 +1,29 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class BoardSettings:
|
||||
WIDTH: int = 21
|
||||
HEIGHT: int = 21
|
||||
OBSTACLE_COUNT: int = 10
|
||||
|
||||
|
||||
@dataclass
|
||||
class InactivityWatchdogSettings:
|
||||
INACIVITY_TIMEOUT: int = 10 # seconds
|
||||
KICK_TIMEOUT: int = 60 * 10 # seconds
|
||||
TICK_INTERVAL: int = 1 # seconds
|
||||
|
||||
|
||||
@dataclass
|
||||
class DebugSettings:
|
||||
PRINT_BOARD: bool = False
|
||||
CREATE_TEST_PLAYER: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class Settings:
|
||||
board: BoardSettings
|
||||
inacivity_watchdog: InactivityWatchdogSettings
|
||||
debug: Optional[DebugSettings] = None
|
||||
34
hopper/models/player.py
Normal file
34
hopper/models/player.py
Normal file
@ -0,0 +1,34 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class Position:
|
||||
x: int
|
||||
y: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class Player:
|
||||
name: str
|
||||
uuid: str = field(default_factory=lambda: str(uuid.uuid4()))
|
||||
position: Position = field(default_factory=lambda: Position(0, 0))
|
||||
move_count: int = 0
|
||||
move_attempt_count: int = 0
|
||||
last_seen: datetime.datetime = field(
|
||||
default_factory=lambda: datetime.datetime.now()
|
||||
)
|
||||
active: bool = True
|
||||
|
||||
def reset_timeout(self) -> None:
|
||||
self.last_seen = datetime.datetime.now()
|
||||
|
||||
|
||||
class PlayerList(list[Player]):
|
||||
def find(self, uuid: str) -> Optional[Player]:
|
||||
for player in self:
|
||||
if player.uuid == uuid:
|
||||
return player
|
||||
return None
|
||||
Reference in New Issue
Block a user