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)