Skip to content

Commit

Permalink
Merge pull request #34 from corail-research/main
Browse files Browse the repository at this point in the history
Reprise sur la branche de la V2
  • Loading branch information
arc-hugo authored Jun 9, 2024
2 parents 83c1d2a + 374453b commit b187652
Show file tree
Hide file tree
Showing 15 changed files with 541 additions and 466 deletions.
18 changes: 9 additions & 9 deletions gen_ref_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@
import mkdocs_gen_files
import re

for path in sorted(Path("src").rglob("*.py")): #
for path in sorted(Path("src").rglob("*.py")): #
if len(re.findall('(^|/)__',str(path))):
continue

module_path = path.relative_to("src").with_suffix("") #
doc_path = path.relative_to("src").with_suffix(".md") #
full_doc_path = Path("reference", doc_path) #
module_path = path.relative_to("src").with_suffix("") #
doc_path = path.relative_to("src").with_suffix(".md") #
full_doc_path = Path("reference", doc_path) #

parts = list(module_path.parts)

if parts[-1] == "__init__": #
if parts[-1] == "__init__": #
parts = parts[:-1]
elif parts[-1] == "__main__":
continue

with mkdocs_gen_files.open(full_doc_path, "w") as fd: #
identifier = ".".join(parts) #
print("::: " + identifier, file=fd) #
with mkdocs_gen_files.open(full_doc_path, "w") as fd: #
identifier = ".".join(parts) #
print("::: " + identifier, file=fd) #

mkdocs_gen_files.set_edit_path(full_doc_path, path) #
mkdocs_gen_files.set_edit_path(full_doc_path, path) #
49 changes: 9 additions & 40 deletions src/seahorse/game/action.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,28 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from abc import abstractmethod
from seahorse.utils.serializer import Serializable

if TYPE_CHECKING:
from seahorse.game.game_state import GameState


class Action(Serializable):
"""
A class representing an action in the game.
A generic class representing an action in the game.
Attributes:
past_gs (GameState): The past game state.
new_gs (GameState): The new game state.
"""

def __init__(self, current_game_state: GameState, next_game_state: GameState) -> None:
def __init__(self) -> None:
"""
Initializes a new instance of the Action class.
Args:
past_gs (GameState): The past game state.
new_gs (GameState): The new game state.
"""
self.current_game_state = current_game_state
self.next_game_state = next_game_state
pass

def get_current_game_state(self) -> GameState:
@abstractmethod
def get_heavy_action(self, *_) -> Action:
"""
Returns the past game state.
Returns the heavy action.
Returns:
GameState: The past game state.
Action: The heavy action.
"""
return self.current_game_state

def get_next_game_state(self) -> GameState:
"""
Returns the new game state.
Returns:
GameState: The new game state.
"""
return self.next_game_state

def __hash__(self) -> int:
return hash((hash(self.get_next_game_state()), hash(self.get_current_game_state())))

def __eq__(self, value: object) -> bool:
return hash(self) == hash(value)

def __str__(self) -> str:
return "From:\n" + self.get_current_game_state().get_rep().__str__() + "\nto:\n" + self.get_next_game_state().get_rep().__str__()
raise NotImplementedError

def to_json(self) -> dict:
return self.__dict__
81 changes: 69 additions & 12 deletions src/seahorse/game/game_state.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from abc import abstractmethod
from itertools import cycle
from typing import Any

from seahorse.game.action import Action
from seahorse.game.heavy_action import HeavyAction
from seahorse.game.light_action import LightAction
from seahorse.game.representation import Representation
from seahorse.player.player import Player
from seahorse.utils.custom_exceptions import MethodNotImplementedError
Expand Down Expand Up @@ -34,7 +35,8 @@ def __init__(self, scores: dict[int, Any], next_player: Player, players: list[Pl
self.next_player = next_player
self.players = players
self.rep = rep
self._possible_actions = None
self._possible_light_actions = None
self._possible_heavy_actions = None

def get_player_score(self, player: Player) -> float:
"""
Expand Down Expand Up @@ -95,20 +97,35 @@ def get_rep(self) -> Representation:
"""
return self.rep

def get_possible_actions(self) -> frozenset[Action]:
def get_possible_light_actions(self) -> frozenset[LightAction]:
"""
Returns a copy of the possible light actions from this state.
The first call triggers the `generate_possible_light_actions` method.
Returns:
FrozenSet[LightAction]: The possible actions.
"""
# Lazy loading
if self.is_done():
return frozenset()
if self._possible_light_actions is None:
self._possible_light_actions = frozenset(self.generate_possible_light_actions())
return self._possible_light_actions

def get_possible_heavy_actions(self) -> frozenset[HeavyAction]:
"""
Returns a copy of the possible actions from this state.
The first call triggers the `generate_possible_actions` method.
Returns a copy of the possible heavy actions from this state.
The first call triggers the `generate_possible_heavy_actions` method.
Returns:
FrozenSet[Action]: The possible actions.
"""
# Lazy loading
if self.is_done():
return frozenset()
if self._possible_actions is None:
self._possible_actions = frozenset(self.generate_possible_actions())
return self._possible_actions
if self._possible_heavy_actions is None:
self._possible_heavy_actions = frozenset(self.generate_possible_heavy_actions())
return self._possible_heavy_actions

def check_action(self, action: Action) -> bool:
"""
Expand All @@ -120,16 +137,56 @@ def check_action(self, action: Action) -> bool:
Returns:
bool: True if the action is feasible, False otherwise.
"""
if action in self.get_possible_actions():
return True
if isinstance(action, LightAction):
return action in self.get_possible_light_actions()
if isinstance(action, HeavyAction):
return action in self.get_possible_heavy_actions()
return False

def convert_gui_data_to_action_data(self, data: dict[str, Any]) -> dict[str, Any]:
"""
Converts GUI data to light action data.
This method can and should be overridden by the user.
Args:
data (Dict[str, Any]): The GUI data.
Returns:
Dict[str, Any]: The action data.
"""
return data

@abstractmethod
def convert_light_action_to_action(self,data) -> Action :
def apply_action(self, action: LightAction) -> "GameState":
"""
Applies an action to the game state.
Args:
action (LightAction): The action to apply.
Returns:
GameState: The new game state.
Raises:
MethodNotImplementedError: If the method is not implemented.
"""
raise MethodNotImplementedError()

@abstractmethod
def generate_possible_light_actions(self) -> set[LightAction]:
"""
Generates a set of all possible actions from this game state.
Returns:
Set[Action]: A set of possible actions.
Raises:
MethodNotImplementedError: If the method is not implemented.
"""
raise MethodNotImplementedError()

@abstractmethod
def generate_possible_actions(self) -> set[Action]:
def generate_possible_heavy_actions(self) -> set[HeavyAction]:
"""
Generates a set of all possible actions from this game state.
Expand Down
68 changes: 68 additions & 0 deletions src/seahorse/game/heavy_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from seahorse.game.action import Action

if TYPE_CHECKING:
from seahorse.game.game_state import GameState


class HeavyAction(Action):
"""
A class representing an action in the game.
Attributes:
past_gs (GameState): The past game state.
new_gs (GameState): The new game state.
"""

def __init__(self, current_game_state: GameState, next_game_state: GameState) -> None:
"""
Initializes a new instance of the Action class.
Args:
past_gs (GameState): The past game state.
new_gs (GameState): The new game state.
"""
self.current_game_state = current_game_state
self.next_game_state = next_game_state

def get_current_game_state(self) -> GameState:
"""
Returns the past game state.
Returns:
GameState: The past game state.
"""
return self.current_game_state

def get_next_game_state(self) -> GameState:
"""
Returns the new game state.
Returns:
GameState: The new game state.
"""
return self.next_game_state

def get_heavy_action(self, *_) -> HeavyAction:
"""
Returns the heavy action.
Returns:
HeavyAction: The heavy action.
"""
return self

def __hash__(self) -> int:
return hash((hash(self.get_next_game_state()), hash(self.get_current_game_state())))

def __eq__(self, value: object) -> bool:
return hash(self) == hash(value)

def __str__(self) -> str:
return "From:\n" + self.get_current_game_state().get_rep().__str__() + "\nto:\n" + self.get_next_game_state().get_rep().__str__()

def to_json(self) -> dict:
return self.__dict__
9 changes: 5 additions & 4 deletions src/seahorse/game/io_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from loguru import logger

from seahorse.game.action import Action
from seahorse.game.heavy_action import HeavyAction
from seahorse.utils.serializer import Serializable

if TYPE_CHECKING:
Expand Down Expand Up @@ -90,8 +91,8 @@ def remote_action(label: str):
"""
def meta_wrapper(fun: Callable):
@functools.wraps(fun)
async def wrapper(self:EventSlave,current_state:GameState,*_,**__):
await EventMaster.get_instance().sio.emit(label,json.dumps(current_state.to_json(),default=lambda x:x.to_json()),to=self.sid)
async def wrapper(self:EventSlave,current_state:GameState,*_,**kwargs):
await EventMaster.get_instance().sio.emit(label,json.dumps({**current_state.to_json(),**kwargs},default=lambda x:x.to_json()),to=self.sid)
out = await EventMaster.get_instance().wait_for_next_play(self.sid,current_state.players)
return out

Expand Down Expand Up @@ -248,7 +249,7 @@ async def wait_for_next_play(self,sid:int,players:list) -> Action:
new_gs.players = players


return Action(past_gs,new_gs)
return HeavyAction(past_gs,new_gs)

async def wait_for_event(self,sid:int,label:str,*,flush_until:float | None=None) -> Coroutine:
"""Waits for an aribtrary event emitted by the connection identified by `sid`
Expand All @@ -261,7 +262,7 @@ async def wait_for_event(self,sid:int,label:str,*,flush_until:float | None=None)
flush_until (float, optional): The timestamp treshold. Defaults to None.
Returns:
Coroutine: a promise yielding the data associated to the event
Coroutine: a promise yielding the data associated to the event
"""
while not len(self.__events.get(sid,{}).get(label,[])):
await asyncio.sleep(.1)
Expand Down
53 changes: 53 additions & 0 deletions src/seahorse/game/light_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from seahorse.game.action import Action
from seahorse.game.heavy_action import HeavyAction
from seahorse.utils.custom_exceptions import NoGameStateProvidedError
if TYPE_CHECKING:
from seahorse.game.game_state import GameState


class LightAction(Action):
"""
A class representing an action in the game.
Attributes:
data (dict): The data of the light action.
"""

def __init__(self, data: dict) -> None:
"""
Initializes a new instance of the Action class.
Args: data (dict): The data of the light action.
"""
self.data = data


def get_heavy_action(self, game_state: GameState = None) -> HeavyAction:
"""
Returns the heavy action.
Returns:
HeavyAction: The heavy action.
"""
if game_state is None:
raise NoGameStateProvidedError()

return HeavyAction(game_state, game_state.apply_action(self))


def __hash__(self) -> int:
return hash(tuple(self.data.items()))

def __eq__(self, value: object) -> bool:
return hash(self) == hash(value)

def __str__(self) -> str:
return "LightAction: " + str(self.data)

def to_json(self) -> dict:
return self.__dict__
Loading

0 comments on commit b187652

Please sign in to comment.