diff --git a/overcooked_simulator/counter_factory.py b/overcooked_simulator/counter_factory.py index 02abcf47a6e4f1ce408bd673528934a96f127585..405751e3889441a4eefa382201f17e359304dfc9 100644 --- a/overcooked_simulator/counter_factory.py +++ b/overcooked_simulator/counter_factory.py @@ -33,6 +33,7 @@ layout_chars: """ import inspect import sys +from random import Random from typing import Any, Type, TypeVar import numpy as np @@ -113,6 +114,7 @@ class CounterFactory: plate_config: PlateConfig, order_and_score: OrderAndScoreManager, hook: Hooks, + random: Random, ) -> None: """Constructor for the `CounterFactory` class. Set up the attributes necessary to instantiate the counters. @@ -171,6 +173,9 @@ class CounterFactory: self.hook = hook """Reference to the hook manager.""" + self.random = random + """Random instance.""" + def get_counter_object(self, c: str, pos: npt.NDArray[float]) -> Counter: """Create and returns a counter object based on the provided character and position.""" @@ -216,6 +221,7 @@ class CounterFactory: ), "plate_config": self.plate_config, "dispensing": self.item_info[Plate.__name__], + "random": self.random, } ) elif issubclass(counter_class, ServingWindow): diff --git a/overcooked_simulator/counters.py b/overcooked_simulator/counters.py index d4b72eec6029dd96ca6c8d5604deb64b12b0c970..3265414e66e810ba58c5ee7c021c7ed205c10918 100644 --- a/overcooked_simulator/counters.py +++ b/overcooked_simulator/counters.py @@ -40,6 +40,7 @@ import uuid from collections import deque from collections.abc import Iterable from datetime import datetime, timedelta +from random import Random from typing import TYPE_CHECKING, Optional, Callable, Set from overcooked_simulator.hooks import ( @@ -70,7 +71,6 @@ if TYPE_CHECKING: OrderAndScoreManager, ) -import numpy as np import numpy.typing as npt from overcooked_simulator.game_items import ( @@ -456,6 +456,7 @@ class PlateDispenser(Counter): dispensing: ItemInfo, plate_config: PlateConfig, plate_transitions: dict[str, ItemInfo], + random: Random, **kwargs, ) -> None: super().__init__(**kwargs) @@ -473,6 +474,8 @@ class PlateDispenser(Counter): `out_of_kitchen_timer` list every frame.""" self.plate_transitions: dict[str, ItemInfo] = plate_transitions """Transitions for the plates. Relevant for the sink, because a plate can become a clean one there.""" + self.random = random + """Random instance.""" self.setup_plates() def pick_up(self, on_hands: bool = True) -> Item | None: @@ -497,9 +500,9 @@ class PlateDispenser(Counter): """Is called from the serving window to add a plate out of kitchen.""" # not perfect identical to datetime.now but based on framerate enough. time_plate_to_add = env_time + timedelta( - seconds=np.random.uniform( - low=self.plate_config.plate_delay[0], - high=self.plate_config.plate_delay[1], + seconds=self.random.uniform( + a=self.plate_config.plate_delay[0], + b=self.plate_config.plate_delay[1], ) ) log.debug(f"New plate out of kitchen until {time_plate_to_add}") diff --git a/overcooked_simulator/order.py b/overcooked_simulator/order.py index 299bcdd6e3c7afbfcd9b2a57c816a911606106f2..e5cc4fae636a713c02840681f92779e15faa7bb9 100644 --- a/overcooked_simulator/order.py +++ b/overcooked_simulator/order.py @@ -46,11 +46,11 @@ from __future__ import annotations import dataclasses import logging -import random import uuid from abc import abstractmethod from collections import deque from datetime import datetime, timedelta +from random import Random from typing import Callable, Tuple, Any, Deque, Protocol, TypedDict, Type from overcooked_simulator.game_items import Item, Plate, ItemInfo @@ -137,9 +137,11 @@ class OrderGeneration: ``` """ - def __init__(self, available_meals: dict[str, ItemInfo], **kwargs): + def __init__(self, available_meals: dict[str, ItemInfo], random: Random, **kwargs): self.available_meals: list[ItemInfo] = list(available_meals.values()) """Available meals restricted through the `environment_config.yml`.""" + self.random = random + """Random instance.""" @abstractmethod def init_orders(self, now) -> list[Order]: @@ -161,11 +163,21 @@ class OrderGeneration: class OrderAndScoreManager: """The Order and Score Manager that is called from the serving window.""" - def __init__(self, order_config, available_meals: dict[str, ItemInfo], hook: Hooks): + def __init__( + self, + order_config, + available_meals: dict[str, ItemInfo], + hook: Hooks, + random: Random, + ): + self.random = random + """Random instance.""" self.score: float = 0.0 """The current score of the environment.""" self.order_gen: OrderGeneration = order_config["order_gen_class"]( - available_meals=available_meals, kwargs=order_config["order_gen_kwargs"] + available_meals=available_meals, + random=random, + kwargs=order_config["order_gen_kwargs"], ) """The order generation.""" self.serving_not_ordered_meals: Callable[ @@ -511,8 +523,8 @@ class RandomOrderGeneration(OrderGeneration): ``` """ - def __init__(self, available_meals: dict[str, ItemInfo], **kwargs): - super().__init__(available_meals, **kwargs) + def __init__(self, available_meals: dict[str, ItemInfo], random: Random, **kwargs): + super().__init__(available_meals, random, **kwargs) self.kwargs: RandomOrderKwarg = RandomOrderKwarg(**kwargs["kwargs"]) self.next_order_time: datetime | None = datetime.max self.number_cur_orders: int = 0 @@ -524,7 +536,7 @@ class RandomOrderGeneration(OrderGeneration): if not self.kwargs.sample_on_serving: self.create_random_next_time_delta(now) return self.create_orders_for_meals( - random.choices(self.available_meals, k=self.kwargs.num_start_meals), + self.random.choices(self.available_meals, k=self.kwargs.num_start_meals), now, self.kwargs.sample_on_serving, ) @@ -547,7 +559,7 @@ class RandomOrderGeneration(OrderGeneration): self.needed_orders = max(self.needed_orders, 0) self.number_cur_orders += len(new_finished_orders) return self.create_orders_for_meals( - random.choices(self.available_meals, k=len(new_finished_orders)), + self.random.choices(self.available_meals, k=len(new_finished_orders)), now, ) if self.next_order_time <= now: @@ -560,7 +572,7 @@ class RandomOrderGeneration(OrderGeneration): self.next_order_time = datetime.max self.number_cur_orders += 1 return self.create_orders_for_meals( - [random.choice(self.available_meals)], + [self.random.choice(self.available_meals)], now, ) return [] @@ -575,7 +587,7 @@ class RandomOrderGeneration(OrderGeneration): else: duration = timedelta( seconds=getattr( - random, self.kwargs.order_duration_random_func["func"] + self.random, self.kwargs.order_duration_random_func["func"] )(**self.kwargs.order_duration_random_func["kwargs"]) ) log.info(f"Create order for meal {meal} with duration {duration}") @@ -601,7 +613,7 @@ class RandomOrderGeneration(OrderGeneration): def create_random_next_time_delta(self, now: datetime): self.next_order_time = now + timedelta( - seconds=getattr(random, self.kwargs.sample_on_dur_random_func["func"])( + seconds=getattr(self.random, self.kwargs.sample_on_dur_random_func["func"])( **self.kwargs.sample_on_dur_random_func["kwargs"] ) ) diff --git a/overcooked_simulator/overcooked_environment.py b/overcooked_simulator/overcooked_environment.py index 53d8559f0be0642f3fa376789bea66e495eafb14..317e37f6cec4090deb83aaf6b623045675d4020e 100644 --- a/overcooked_simulator/overcooked_environment.py +++ b/overcooked_simulator/overcooked_environment.py @@ -4,11 +4,11 @@ import dataclasses import inspect import json import logging -import random import sys from datetime import timedelta, datetime from enum import Enum from pathlib import Path +from random import Random from typing import Literal, TypedDict, Callable import numpy as np @@ -123,7 +123,10 @@ class Environment: layout_config: Path | str, item_info: Path | str, as_files: bool = True, + seed: int = 56789223842348, ): + self.random: Random = Random(seed) + """Random instance.""" self.hook: Hooks = Hooks(self) """Hook manager. Register callbacks and create hook points with additional kwargs.""" @@ -174,6 +177,7 @@ class Environment: if info.type == ItemType.Meal and item in self.allowed_meal_names }, hook=self.hook, + random=self.random, ) """The manager for the orders and score update.""" @@ -198,6 +202,7 @@ class Environment: ), order_and_score=self.order_and_score, hook=self.hook, + random=self.random, ) ( @@ -600,11 +605,13 @@ class Environment: self.players[player.name] = player if player.pos is None: if len(self.designated_player_positions) > 0: - free_idx = random.randint(0, len(self.designated_player_positions) - 1) + free_idx = self.random.randint( + 0, len(self.designated_player_positions) - 1 + ) player.move_abs(self.designated_player_positions[free_idx]) del self.designated_player_positions[free_idx] elif len(self.free_positions) > 0: - free_idx = random.randint(0, len(self.free_positions) - 1) + free_idx = self.random.randint(0, len(self.free_positions) - 1) player.move_abs(self.free_positions[free_idx]) del self.free_positions[free_idx] else: