diff --git a/cooperative_cuisine/configs/environment_config.yaml b/cooperative_cuisine/configs/environment_config.yaml index 1567ebd4078e97b4a6e9dc30388b65c5c5885305..608eb9728bb07f5e49e8d9812864dd5a06c00cd1 100644 --- a/cooperative_cuisine/configs/environment_config.yaml +++ b/cooperative_cuisine/configs/environment_config.yaml @@ -164,6 +164,45 @@ extra_setup_functions: callback_class_kwargs: log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl add_hook_ref: true + + # Game event recording + game_events: + func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class '' + kwargs: + hooks: + - post_counter_pick_up + - post_counter_drop_off + - dispenser_pick_up + - cutting_board_100 + - cutting_board_start_interaction + - cutting_board_end_interact + - pre_serving + - post_serving + - no_serving + - dirty_plate_arrives + - trashcan_usage + - plate_cleaned + - sink_start_interact + - sink_end_interact + - added_plate_to_sink + - drop_on_sink_addon + - pick_up_from_sink_addon + - serve_not_ordered_meal + - serve_without_plate + - completed_order + - new_orders + - order_expired + - action_on_not_reachable_counter + - new_fire + - fire_spreading + - drop_off_on_cooking_equipment + - players_collide + callback_class: !!python/name:cooperative_cuisine.recording.FileRecorder '' + callback_class_kwargs: + log_path: USER_LOG_DIR/ENV_NAME/LOG_RECORD_NAME.jsonl + add_hook_ref: true + + # info_msg: # func: !!python/name:cooperative_cuisine.hooks.hooks_via_callback_class '' # kwargs: diff --git a/cooperative_cuisine/counters.py b/cooperative_cuisine/counters.py index 2853e59eafc010736fa7ed2938d5a6bbe4d7cec9..f0c7c7fc2db4431cfdbb24464b882baf815a8f78 100644 --- a/cooperative_cuisine/counters.py +++ b/cooperative_cuisine/counters.py @@ -62,6 +62,8 @@ from cooperative_cuisine.hooks import ( PICK_UP_FROM_SINK_ADDON, PLATE_OUT_OF_KITCHEN_TIME, DROP_OFF_ON_COOKING_EQUIPMENT, + POST_COUNTER_DROP_OFF, + PRE_COUNTER_DROP_OFF, ) if TYPE_CHECKING: @@ -153,6 +155,7 @@ class Counter: counter=self, on_hands=on_hands, return_this=occupied_by, + player=player, ) return occupied_by return None @@ -163,6 +166,7 @@ class Counter: counter=self, on_hands=on_hands, return_this=return_this, + player=player, ) return return_this occupied_by = self.occupied_by @@ -172,6 +176,7 @@ class Counter: counter=self, on_hands=on_hands, return_this=occupied_by, + player=player, ) return occupied_by @@ -198,8 +203,22 @@ class Counter: Returns: Item or None what should be put back on the players hand, e.g., the cooking equipment. """ + self.hook( + PRE_COUNTER_DROP_OFF, + item=item, + equipment=self.occupied_by, + counter=self, + player=player, + ) if self.occupied_by is None: self.occupied_by = item + self.hook( + POST_COUNTER_DROP_OFF, + item=item, + equipment=self.occupied_by, + counter=self, + player=player, + ) elif self.occupied_by.can_combine(item): self.hook( DROP_OFF_ON_COOKING_EQUIPMENT, @@ -209,6 +228,7 @@ class Counter: player=player, ) return self.occupied_by.combine(item) + return None def __repr__(self): @@ -248,7 +268,9 @@ class Counter: return True return False - def do_hand_free_interaction(self, passed_time: timedelta, now: datetime): + def do_hand_free_interaction( + self, passed_time: timedelta, now: datetime, player_name: str + ): ... def to_dict(self) -> dict: @@ -293,7 +315,9 @@ class CuttingBoard(Counter): board.""" super().__init__(**kwargs) - def do_hand_free_interaction(self, passed_time: timedelta, now: datetime): + def do_hand_free_interaction( + self, passed_time: timedelta, now: datetime, player_name: str + ): """Called by environment step function for time progression. Args: @@ -334,7 +358,7 @@ class CuttingBoard(Counter): self.occupied_by.name = self.inverted_transition_dict[ self.occupied_by.name ].name - self.hook(CUTTING_BOARD_100, counter=self) + self.hook(CUTTING_BOARD_100, counter=self, player_name=player_name) class ServingWindow(Counter): @@ -740,7 +764,9 @@ class Sink(Counter): """If there is a plate in the sink.""" return len(self.occupied_by) != 0 - def do_hand_free_interaction(self, passed_time: timedelta, now: datetime): + def do_hand_free_interaction( + self, passed_time: timedelta, now: datetime, player_name: str + ): """Called by environment step function for time progression""" if ( self.occupied @@ -772,7 +798,7 @@ class Sink(Counter): equipment=self.__class__.__name__, percent=percent ) if self.occupied_by[-1].progress_percentage == 1.0: - self.hook(PLATE_CLEANED, counter=self) + self.hook(PLATE_CLEANED, counter=self, player_name=player_name) self.occupied_by[-1].reset() self.occupied_by[-1].name = name plate = self.occupied_by.pop() diff --git a/cooperative_cuisine/environment.py b/cooperative_cuisine/environment.py index 144d96c044903dc492d26f2969d12f2f09711192..795f3afcc2d16c08cb420401893c55afcec84338 100644 --- a/cooperative_cuisine/environment.py +++ b/cooperative_cuisine/environment.py @@ -229,6 +229,7 @@ class Environment: [[-0.5, self.kitchen_width - 0.5], [-0.5, self.kitchen_height - 0.5]], dtype=float, ), + hook=self.hook, ) """Does the movement of players in each step.""" diff --git a/cooperative_cuisine/game_server.py b/cooperative_cuisine/game_server.py index e45065f9203843072f2fb924f779d806555e3956..6cf18d8f62676f27962f98747633914a24851064 100644 --- a/cooperative_cuisine/game_server.py +++ b/cooperative_cuisine/game_server.py @@ -40,6 +40,7 @@ from cooperative_cuisine.utils import ( url_and_port_arguments, add_list_of_manager_ids_arguments, disable_websocket_logging_arguments, + setup_logging, ) log = logging.getLogger(__name__) @@ -800,7 +801,7 @@ async def websocket_player_endpoint(websocket: WebSocket, client_id: str): def main( host: str, port: int, manager_ids: list[str], enable_websocket_logging: bool = False ): - # setup_logging(enable_websocket_logging) + setup_logging(enable_websocket_logging) loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) environment_handler.extend_allowed_manager(manager_ids) diff --git a/cooperative_cuisine/hooks.py b/cooperative_cuisine/hooks.py index 9435c432e484dc917bfb2f40201a3ca55df82c19..1e44e08e1abee3e21d38229ab78afdfd9d19efea 100644 --- a/cooperative_cuisine/hooks.py +++ b/cooperative_cuisine/hooks.py @@ -60,6 +60,8 @@ PRE_RESET_ENV_TIME = "pre_reset_env_time" POST_RESET_ENV_TIME = "post_reset_env_time" +# ----- + PRE_COUNTER_PICK_UP = "pre_counter_pick_up" POST_COUNTER_PICK_UP = "post_counter_pick_up" @@ -125,6 +127,7 @@ FIRE_SPREADING = "fire_spreading" DROP_OFF_ON_COOKING_EQUIPMENT = "drop_off_on_cooking_equipment" +PLAYERS_COLLIDE = "players_collide" class Hooks: """ diff --git a/cooperative_cuisine/movement.py b/cooperative_cuisine/movement.py index fa554ad52fab568cf6f741878fb01128caae4975..56956686c6c09bb3491cd9660b9412027a4b37a6 100644 --- a/cooperative_cuisine/movement.py +++ b/cooperative_cuisine/movement.py @@ -11,6 +11,7 @@ import numpy.typing as npt from scipy.spatial import distance_matrix from cooperative_cuisine.counters import Counter +from cooperative_cuisine.hooks import Hooks, PLAYERS_COLLIDE from cooperative_cuisine.player import Player @@ -22,7 +23,7 @@ class Movement: world_borders_upper = None """World borders upper bounds.""" - def __init__(self, counter_positions, player_config, world_borders): + def __init__(self, counter_positions, player_config, world_borders, hook: Hooks): self.counter_positions = counter_positions """Positions of all counters in an environment. Needs to be updated if the counters position changes.""" self.player_radius = player_config["radius"] @@ -36,6 +37,9 @@ class Movement: equal to the number of player.""" self.set_collision_arrays(1) + self.hook = hook + """Hook manager. Register callbacks and create hook points with additional kwargs.""" + def set_collision_arrays(self, number_players: int): """Sets collision arrays for the given number of players. @@ -110,6 +114,11 @@ class Movement: collisions = distances_players_after_scipy < (2 * self.player_radius) eye_idxs = np.eye(len(player_positions), len(player_positions), dtype=bool) collisions[eye_idxs] = False + if np.any(collisions): + self.hook( + PLAYERS_COLLIDE, + collisions=collisions, + ) player_diff_vecs[collisions == False] = 0 push_vectors = np.sum(player_diff_vecs, axis=0) collisions = np.any(collisions, axis=1) diff --git a/cooperative_cuisine/player.py b/cooperative_cuisine/player.py index a92f12f38886cafac974aa796aff167bc454fc74..6dc25caa108147e7da436485eafe0f4b3e5f3b3d 100644 --- a/cooperative_cuisine/player.py +++ b/cooperative_cuisine/player.py @@ -200,7 +200,9 @@ class Player: passed_time, self.holding ) else: - self.last_interacted_counter.do_hand_free_interaction(passed_time, now) + self.last_interacted_counter.do_hand_free_interaction( + passed_time, now, self.name + ) def __repr__(self): return f"Player(name:{self.name},pos:{str(self.pos)},holds:{self.holding})" diff --git a/cooperative_cuisine/recording.py b/cooperative_cuisine/recording.py index 7f540b055f933a5b6410afc12ec785a337b00d4f..490377ba89fbc303ae68bdef2d4cb5bc97a16bf6 100644 --- a/cooperative_cuisine/recording.py +++ b/cooperative_cuisine/recording.py @@ -46,8 +46,11 @@ import os import traceback from pathlib import Path +from cooperative_cuisine.counters import Counter from cooperative_cuisine.environment import Environment from cooperative_cuisine.hooks import HookCallbackClass +from cooperative_cuisine.items import Item, Effect +from cooperative_cuisine.orders import Order from cooperative_cuisine.utils import NumpyAndDataclassEncoder, expand_path log = logging.getLogger(__name__) @@ -76,6 +79,14 @@ class FileRecorder(HookCallbackClass): os.makedirs(log_path.parent, exist_ok=True) def __call__(self, hook_ref: str, env: Environment, **kwargs): + for key, item in kwargs.items(): + if isinstance(item, (Counter, Item, Effect, Order)): + kwargs[key] = str(item) + elif isinstance(item, list): + kwargs[key] = [str(i) for i in item] + if hook_ref == "new_orders": + new_orders = kwargs["new_orders"] + kwargs["new_orders"] = [str(order) for order in new_orders] try: record = ( json.dumps( @@ -92,4 +103,6 @@ class FileRecorder(HookCallbackClass): log_file.write(record) except TypeError as e: traceback.print_exception(e) - log.info(f"Not JSON serializable Record {kwargs}") + log.info( + f"Not JSON serializable Record, hook: {hook_ref}, kwargs: {kwargs}" + )