Skip to content

Commit

Permalink
Merge pull request #33 from corail-research/remote_timer_fix
Browse files Browse the repository at this point in the history
Remote timer fix
  • Loading branch information
YoannSab authored Jun 6, 2024
2 parents e54d8ad + 22ab830 commit bb11959
Show file tree
Hide file tree
Showing 13 changed files with 42 additions and 137 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) #
2 changes: 0 additions & 2 deletions src/seahorse/game/action.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from __future__ import annotations

from abc import abstractmethod
from typing import TYPE_CHECKING

from seahorse.utils.serializer import Serializable

class Action(Serializable):
Expand Down
6 changes: 2 additions & 4 deletions src/seahorse/game/game_state.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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
Expand Down Expand Up @@ -112,7 +111,7 @@ def get_possible_light_actions(self) -> frozenset[LightAction]:
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 heavy actions from this state.
Expand Down Expand Up @@ -172,7 +171,6 @@ def apply_action(self, action: LightAction) -> "GameState":
MethodNotImplementedError: If the method is not implemented.
"""
raise MethodNotImplementedError()


@abstractmethod
def generate_possible_light_actions(self) -> set[LightAction]:
Expand All @@ -186,7 +184,7 @@ def generate_possible_light_actions(self) -> set[LightAction]:
MethodNotImplementedError: If the method is not implemented.
"""
raise MethodNotImplementedError()

@abstractmethod
def generate_possible_heavy_actions(self) -> set[HeavyAction]:
"""
Expand Down
3 changes: 1 addition & 2 deletions src/seahorse/game/heavy_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from typing import TYPE_CHECKING

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

if TYPE_CHECKING:
from seahorse.game.game_state import GameState
Expand Down Expand Up @@ -47,7 +46,7 @@ def get_next_game_state(self) -> GameState:
"""
return self.next_game_state

def get_heavy_action(self, game_state: GameState = None) -> HeavyAction:
def get_heavy_action(self, **_) -> HeavyAction:
"""
Returns the heavy action.
Expand Down
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
12 changes: 6 additions & 6 deletions src/seahorse/game/light_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

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

Expand All @@ -22,11 +22,11 @@ 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.
Expand All @@ -35,10 +35,10 @@ def get_heavy_action(self, game_state: GameState = None) -> HeavyAction:
HeavyAction: The heavy action.
"""
if game_state is None:
raise ValueError("Cannot apply a light action without current game state.")
raise NoGameStateProvidedError()

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


def __hash__(self) -> int:
return hash(tuple(self.data.items()))
Expand Down
24 changes: 2 additions & 22 deletions src/seahorse/game/master.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
from loguru import logger

from seahorse.game.game_state import GameState
from seahorse.game.heavy_action import HeavyAction
from seahorse.game.io_stream import EventMaster, EventSlave
from seahorse.game.light_action import LightAction
from seahorse.player.player import Player
from seahorse.utils.custom_exceptions import (
ActionNotPermittedError,
Expand Down Expand Up @@ -105,7 +103,7 @@ async def step(self) -> GameState:
self.remaining_time[next_player.get_id()] -= (tstp-start)
if self.remaining_time[next_player.get_id()] < 0:
raise SeahorseTimeoutError()

# if abs((tstp-start)-(tstp-next_player.get_last_timestamp()))>self.timetol:
# next_player.stop_timer()
# raise StopAndStartError()
Expand All @@ -116,6 +114,7 @@ async def step(self) -> GameState:
if action not in possible_actions:
raise ActionNotPermittedError()

# TODO
action.current_game_state._possible_actions=None
action.current_game_state=None
action.next_game_state._possible_actions=None
Expand All @@ -128,48 +127,32 @@ async def play_game(self) -> list[Player]:
Returns:
Iterable[Player]: The winner(s) of the game.
"""
time_start = time.time()
await self.emitter.sio.emit(
"play",
json.dumps(self.current_game_state.to_json(),default=lambda x:x.to_json()),
)
# self.recorded_plays.append(self.current_game_state.__class__.from_json(json.dumps(self.current_game_state.to_json(),default=lambda x:x.to_json())))
id2player={}
# verdict_scores=[-1e9,-1e9]
for player in self.get_game_state().get_players() :
id2player[player.get_id()]=player.get_name()
logger.info(f"Player : {player.get_name()} - {player.get_id()}")
while not self.current_game_state.is_done():
try:
logger.info(f"Player now playing : {self.get_game_state().get_next_player().get_name()} - {self.get_game_state().get_next_player().get_id()}")
self.current_game_state = await self.step()

# self.recorded_plays.append(self.current_game_state.__class__.from_json(json.dumps(self.current_game_state.to_json(),default=lambda x:x.to_json())))

except (ActionNotPermittedError,SeahorseTimeoutError,StopAndStartError) as e:
if isinstance(e,SeahorseTimeoutError):
logger.error(f"Time credit expired for player {self.current_game_state.get_next_player()}")
elif isinstance(e,ActionNotPermittedError) :
logger.error(f"Action not permitted for player {self.current_game_state.get_next_player()}")
# else:
# logger.error(f"Player {self.current_game_state.get_next_player()} might have tried tampering with the timer.\n The timedelta difference exceeded the allowed tolerancy in GameMaster.timetol ")

temp_score = copy.copy(self.current_game_state.get_scores())
id_player_error = self.current_game_state.get_next_player().get_id()
other_player = next(iter([player.get_id() for player in self.current_game_state.get_players() if player.get_id()!=id_player_error]))
temp_score[id_player_error] = -1e9
temp_score[other_player] = 1e9

# temp_score.pop(id_player_error)
self.winner = self.compute_winner(temp_score)

# self.current_game_state.get_scores()[id_player_error] = -3
# other_player = next(iter([player.get_id() for player in self.current_game_state.get_players() if player.get_id()!=id_player_error]))
# self.current_game_state.get_scores()[other_player] = 0

# scores = self.get_scores()
for key in temp_score.keys():
# verdict_scores[int(id2player[key].split("_")[-1])-1]=-scores[key]
logger.info(f"{id2player[key]}:{temp_score[key]}")

for player in self.get_winner() :
Expand Down Expand Up @@ -199,9 +182,6 @@ async def play_game(self) -> list[Player]:

await self.emitter.sio.emit("done",json.dumps(self.get_scores()))
logger.verdict(f"{','.join(w.get_name() for w in self.get_winner())} has won the game")

print(f"Time taken for the whole game : {time.time()-time_start}")

return self.winner

def record_game(self, listeners:Optional[List[EventSlave]]=None) -> None:
Expand Down
11 changes: 6 additions & 5 deletions src/seahorse/player/proxies.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,9 @@ def __init__(self, wrapped_player: Player,gs:type=GameState) -> None:
async def handle_turn(*data):
logger.info(f"{self.wrapped_player.name} is playing")
logger.debug(f"Data received : {data}")
logger.debug(f"Deserialized data : \n{gs.from_json(data[0],next_player=self)}")
action = await self.play(gs.from_json(data[0],next_player=self))
deserialized = json.loads(data[0])
logger.debug(f"Deserialized data : \n{deserialized}")
action = await self.play(gs.from_json(data[0],next_player=self),remaining_time = deserialized["remaining_time"])
logger.info(f"{self.wrapped_player} played the following action : \n{action}")

@self.sio.on("update_id")
Expand All @@ -118,7 +119,7 @@ def play(self, current_state: GameState, remaining_time: int) -> Action:
Returns:
Action: The action resulting from the move.
"""
return self.compute_action(current_state=current_state, remaining_time=remaining_time)
return self.compute_action(current_state=current_state, remaining_time=remaining_time).get_heavy_action(current_state)

def __getattr__(self, attr):
return getattr(self.wrapped_player, attr)
Expand All @@ -136,7 +137,7 @@ def to_json(self) -> dict:
return self.wrapped_player.to_json()

class InteractivePlayerProxy(LocalPlayerProxy):
"""Proxy for interactive players,
"""Proxy for interactive players,
inherits from `LocalPlayerProxy`
"""
def __init__(self, mimics: Player, gui_path:Optional[str]=None, *args, **kwargs) -> None:
Expand All @@ -152,7 +153,7 @@ def __init__(self, mimics: Player, gui_path:Optional[str]=None, *args, **kwargs)
self.shared_sid = None
self.sid = None

async def play(self, current_state: GameState, **args) -> Action:
async def play(self, current_state: GameState, **_) -> Action:
if self.shared_sid and not self.sid:
self.sid=self.shared_sid.sid
while True:
Expand Down
6 changes: 6 additions & 0 deletions src/seahorse/utils/custom_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ def __init__(self, message: str = "Trying to stop something twice !"):
self.message = message
super().__init__(message)

class NoGameStateProvidedError(Exception):
"""Thrown when trying to get a heavy action from a light action without providing a game state
"""
def __init__(self, message: str = "Cannot apply a light action without current game state."):
self.message = message
super().__init__(message)
class PlayerDuplicateError(Exception):
"""Thrown when trying to stop somethin twice
"""
Expand Down
2 changes: 1 addition & 1 deletion src/seahorse/utils/gui_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from typing import Any, Coroutine, Optional

from loguru import logger
from seahorse.game.io_stream import EventMaster, EventSlave
from seahorse.game.io_stream import EventMaster, EventSlave

class GUIClient(EventSlave):
def __init__(self, path:Optional[str]=None) -> None:
Expand Down
4 changes: 2 additions & 2 deletions src/seahorse/utils/recorders.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class StateRecorder(EventSlave):
"""

def __init__(self) -> None:
super().__init__()
super().__init__()
self.identifier = "__REC__"+str(int(time.time()*1000000-random.randint(1,1000000)))
self.id = builtins.id(self)
self.wrapped_id = self.id
Expand All @@ -22,7 +22,7 @@ def __init__(self) -> None:

@self.sio.on("play")
def record_play(data):
self.recorded_content.append(json.loads(data))
self.recorded_content.append(json.loads(data))

@self.sio.event()
def disconnect():
Expand Down
4 changes: 2 additions & 2 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from seahorse.game.representation import Representation
from seahorse.player.player import Player
from seahorse.game.game_state import GameState
from seahorse.game.action import Action
from seahorse.game.heavy_action import HeavyAction


class Dummy_GameState(GameState):
Expand All @@ -26,7 +26,7 @@ def generate_possible_actions(self):
copy_rep.get_env()[(i, j)] = Piece(piece_type="Added", owner=next_player)
list_rep.append(copy.deepcopy(copy_rep))
poss_actions = {
Action(
HeavyAction(
self,
Dummy_GameState(
self.get_scores(),
Expand Down
Loading

0 comments on commit bb11959

Please sign in to comment.