diff --git a/.idea/Uno-Game.iml b/.idea/Uno-Game.iml index 3e4e698..b8519e9 100644 --- a/.idea/Uno-Game.iml +++ b/.idea/Uno-Game.iml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 2c03a59..30807bf 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,7 @@ - + + + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 0fe9d89..c14447f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ keywords = [ "game", ] classifiers = [ - "Development Status :: 1 - Planning", + "Development Status :: 2 - Pre-Alpha", "Programming Language :: Python :: 3", "Operating System :: OS Independent", "License :: OSI Approved :: MIT License", diff --git a/src/uno/engine/__init__.py b/src/uno/engine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/uno/engine/context.py b/src/uno/engine/context.py new file mode 100644 index 0000000..8ada188 --- /dev/null +++ b/src/uno/engine/context.py @@ -0,0 +1,17 @@ +from dataclasses import dataclass + +from .player import Player + + +@dataclass(slots=True) +class Round: + deck: list[str] + discard: list[str] + hands: list[list[str]] + + +@dataclass(slots=True) +class Context: + players: list[Player] + rounds: int + current_round: Round | None = None diff --git a/src/uno/engine/game.py b/src/uno/engine/game.py new file mode 100644 index 0000000..6adfc05 --- /dev/null +++ b/src/uno/engine/game.py @@ -0,0 +1,19 @@ +from .player import Player +from .rule import Rule +from .context import Context, Round + + +class Game: + context: Context + rule: Rule + history: list + + def __init__(self, rule: Rule, n_players: int = 4): + self.context = Context([Player()] * n_players, 0) + self.history = [] + + self.rule = rule + + def start(self): + while not self.rule.is_over(self.context): + self.history.append(self.rule.step(self.context)) diff --git a/src/uno/engine/player.py b/src/uno/engine/player.py new file mode 100644 index 0000000..7156b15 --- /dev/null +++ b/src/uno/engine/player.py @@ -0,0 +1,26 @@ +from dataclasses import dataclass + + +@dataclass +class Request: + hand: list[str] + hand_sizes: list[int] + scores: list[int] + latest_moves: list[tuple[int, str]] + order: str + + +class Player: + name: str + + def __init__(self, name: str): + self.name = name + + def act(self, request: Request) -> str: + """ + Player should return a string of the card to play + :param request: + :return: + """ + raise NotImplementedError + diff --git a/src/uno/engine/rule.py b/src/uno/engine/rule.py new file mode 100644 index 0000000..4ba8d81 --- /dev/null +++ b/src/uno/engine/rule.py @@ -0,0 +1,38 @@ +import random + +from .context import Context + + +class Rule: + def init_deck(self, ctx: Context): + raise NotImplementedError + + def step(self, ctx: Context): + raise NotImplementedError + + def is_over(self, ctx: Context): + raise NotImplementedError + + +class ExampleRule(Rule): + cards: tuple[str] = ('red', 'blue', 'green', 'yellow', 'wild', 'wild_draw_four') + + def init_deck(self, ctx: Context): + return [(color, number) for color in self.cards for number in range(10)] + + def step(self, ctx: Context): + assert ctx.current_round is not None + + round_ = ctx.current_round + if len(round_.deck) == 0: + round_.deck.extend(round_.discard) + random.shuffle(round_.deck) + round_.discard.clear() + return round_.deck.pop() + + def is_over(self, scoreboard): + return any(score >= 500 for score in scoreboard) + + @staticmethod + def is_round_over(ctx: Context): + return any(len(hand) == 0 for hand in ctx.current_round.hands)