Skip to content
Snippets Groups Projects
Commit b0201cd6 authored by Florian Schröder's avatar Florian Schröder
Browse files

Refactor and modify classes with improved explanations

The classes across several files have been modified to improve clarity, provide better comments, and refine attribute representations. This commit involves refactoring the item, effect, and player classes to include more detailed descriptions and user-friendly outputs. The change also simplifies some methods and eliminates redundancies for cleaner and more efficient code.
parent 3c53c7e4
No related branches found
No related tags found
No related merge requests found
Pipeline #48029 failed
...@@ -517,7 +517,7 @@ class Environment: ...@@ -517,7 +517,7 @@ class Environment:
state = self.get_state(player_id, additional_key_values) state = self.get_state(player_id, additional_key_values)
json_data = json.dumps(state) json_data = json.dumps(state)
self.hook(JSON_STATE, json_data=json_data, player_id=player_id) self.hook(JSON_STATE, json_data=json_data, player_id=player_id)
# assert additional_key_values is None or StateRepresentation.model_validate_json(json_data=json_data) # assert StateRepresentation.model_validate_json(json_data=json_data)
return json_data return json_data
def reset_env_time(self): def reset_env_time(self):
......
...@@ -15,6 +15,8 @@ The following classes are used for the base for all game items: ...@@ -15,6 +15,8 @@ The following classes are used for the base for all game items:
The `ItemInfo` is the dataclass for the items in the `item_info.yml`. The `ItemInfo` is the dataclass for the items in the `item_info.yml`.
Additionally, the `Effect` class is defined for the `Fire` effect.
## Code Documentation ## Code Documentation
""" """
...@@ -26,7 +28,13 @@ import datetime ...@@ -26,7 +28,13 @@ import datetime
import logging import logging
import uuid import uuid
from enum import Enum from enum import Enum
from typing import Optional, TypedDict, TYPE_CHECKING from typing import Optional, TypedDict, TYPE_CHECKING, Literal, Set
from cooperative_cuisine.state_representation import (
ItemState,
CookingEquipmentState,
EffectState,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from cooperative_cuisine.effects import EffectManager from cooperative_cuisine.effects import EffectManager
...@@ -136,7 +144,7 @@ class ActiveTransitionTypedDict(TypedDict): ...@@ -136,7 +144,7 @@ class ActiveTransitionTypedDict(TypedDict):
class Item: class Item:
"""Base class for game items which can be held by a player.""" """Base class for game items which can be held by a player."""
item_category = ITEM_CATEGORY item_category: Literal["Item"] | Literal["ItemCookingEquipment"] = ITEM_CATEGORY
"""Class dependent category (is changed for the `CookingEquipment` class). """ """Class dependent category (is changed for the `CookingEquipment` class). """
def __init__( def __init__(
...@@ -157,6 +165,11 @@ class Item: ...@@ -157,6 +165,11 @@ class Item:
self.active_effects: list[Effect] = [] self.active_effects: list[Effect] = []
"""The effects that affect the item.""" """The effects that affect the item."""
@property
def extra_repr(self) -> str | Literal[""]:
"""Additional string to add to the representation of the item (in __repr__)."""
return ""
def __repr__(self): def __repr__(self):
if self.progress_equipment is None: if self.progress_equipment is None:
return f"{self.name}({self.extra_repr})" return f"{self.name}({self.extra_repr})"
...@@ -166,11 +179,6 @@ class Item: ...@@ -166,11 +179,6 @@ class Item:
def __eq__(self, other): def __eq__(self, other):
return other and self.name == other.name return other and self.name == other.name
@property
def extra_repr(self):
"""Additional string to add to the representation of the item (in __repr__)."""
return ""
def can_combine(self, other) -> bool: def can_combine(self, other) -> bool:
"""Check if the item can be combined with the other. After it returned True the `combine` method is called.""" """Check if the item can be combined with the other. After it returned True the `combine` method is called."""
return False return False
...@@ -198,7 +206,7 @@ class Item: ...@@ -198,7 +206,7 @@ class Item:
self.progress_percentage = 0.0 self.progress_percentage = 0.0
self.inverse_progress = False self.inverse_progress = False
def to_dict(self) -> dict: def to_dict(self) -> CookingEquipmentState | ItemState:
"""For the state representation. Only the relevant attributes are put into the dict.""" """For the state representation. Only the relevant attributes are put into the dict."""
return { return {
"id": self.uuid, "id": self.uuid,
...@@ -236,6 +244,10 @@ class CookingEquipment(Item): ...@@ -236,6 +244,10 @@ class CookingEquipment(Item):
for transition in self.transitions.values(): for transition in self.transitions.values():
transition.recipe = collections.Counter(transition.needs) transition.recipe = collections.Counter(transition.needs)
@property
def extra_repr(self) -> str:
return f"{self.content_list}, {self.content_ready}"
def can_combine(self, other) -> bool: def can_combine(self, other) -> bool:
# already cooking or nothing to combine # already cooking or nothing to combine
if other is None or ( if other is None or (
...@@ -299,6 +311,11 @@ class CookingEquipment(Item): ...@@ -299,6 +311,11 @@ class CookingEquipment(Item):
# todo set active transition for fire/burnt? # todo set active transition for fire/burnt?
def check_active_transition(self): def check_active_transition(self):
"""Check if a new active transition is possible, if so, set a new active transition.
Checks if there is an active transition based on the current state of the content list and transitions dictionary.
If an active transition is found, it updates the necessary attributes accordingly.
"""
ingredients = collections.Counter(item.name for item in self.content_list) ingredients = collections.Counter(item.name for item in self.content_list)
for result, transition in self.transitions.items(): for result, transition in self.transitions.items():
if transition.type == ItemType.Effect: if transition.type == ItemType.Effect:
...@@ -337,17 +354,13 @@ class CookingEquipment(Item): ...@@ -337,17 +354,13 @@ class CookingEquipment(Item):
self.content_list = [] self.content_list = []
self.content_ready = None self.content_ready = None
def release(self): def release(self) -> list[Item]:
"""Release the content when the player "picks up" the equipment with a plate in the hands""" """Release the content when the player "picks up" the equipment with a plate in the hands"""
content = self.content_list content = self.content_list
self.reset_content() self.reset_content()
self.reset() self.reset()
return content return content
@property
def extra_repr(self):
return f"{self.content_list}, {self.content_ready}"
def reset(self): def reset(self):
super().reset() super().reset()
self.active_transition = None self.active_transition = None
...@@ -360,18 +373,15 @@ class CookingEquipment(Item): ...@@ -360,18 +373,15 @@ class CookingEquipment(Item):
return self.content_list[0] return self.content_list[0]
return None return None
def to_dict(self) -> dict: def to_dict(self) -> CookingEquipmentState:
d = super().to_dict() d = super().to_dict()
d.update( d.update(
( {
("content_list", [c.to_dict() for c in self.content_list]), "content_list": [c.to_dict() for c in self.content_list],
( "content_ready": self.content_ready.to_dict()
"content_ready", if self.content_ready is not None
self.content_ready.to_dict() else None,
if self.content_ready is not None }
else None,
),
)
) )
return d return d
...@@ -382,8 +392,8 @@ class Plate(CookingEquipment): ...@@ -382,8 +392,8 @@ class Plate(CookingEquipment):
def __init__(self, transitions: dict[str, ItemInfo], clean: bool, *args, **kwargs): def __init__(self, transitions: dict[str, ItemInfo], clean: bool, *args, **kwargs):
self.clean: bool = clean self.clean: bool = clean
"""If the plate is clean or dirty.""" """If the plate is clean or dirty."""
self.meals = set(transitions.keys()) self.meals: Set[str] = set(transitions.keys())
"""All meals can be hold by a clean plate""" """All meals (meal names) can be hold by a clean plate,"""
super().__init__( super().__init__(
name=self.create_name(), name=self.create_name(),
transitions={k: v for k, v in transitions.items() if not v.equipment}, transitions={k: v for k, v in transitions.items() if not v.equipment},
...@@ -394,12 +404,12 @@ class Plate(CookingEquipment): ...@@ -394,12 +404,12 @@ class Plate(CookingEquipment):
def progress(self, equipment: str, percent: float): def progress(self, equipment: str, percent: float):
Item.progress(self, equipment, percent) Item.progress(self, equipment, percent)
def create_name(self): def create_name(self) -> str:
"""The name depends on the clean or dirty state of the plate. Clean plates are `Plate`, otherwise """The name depends on the clean or dirty state of the plate. Clean plates are `Plate`, otherwise
`DirtyPlate`.""" `DirtyPlate`."""
return "Plate" if self.clean else "DirtyPlate" return "Plate" if self.clean else "DirtyPlate"
def can_combine(self, other): def can_combine(self, other) -> bool:
if not super().can_combine(other): if not super().can_combine(other):
if ( if (
isinstance(other, CookingEquipment) isinstance(other, CookingEquipment)
...@@ -418,13 +428,19 @@ class Plate(CookingEquipment): ...@@ -418,13 +428,19 @@ class Plate(CookingEquipment):
# this is here, due to a circular import if it would be in the effects.py # this is here, due to a circular import if it would be in the effects.py
class Effect: class Effect:
"""Effects on counters and items. Like Fire."""
def __init__(self, name: str, item_info: ItemInfo, uid: str = None): def __init__(self, name: str, item_info: ItemInfo, uid: str = None):
self.uuid: str = uuid.uuid4().hex if uid is None else uid self.uuid: str = uuid.uuid4().hex if uid is None else uid
self.name = name """ID of the effect instance."""
self.item_info = item_info self.name: str = name
self.progres_percentage = 0.0 """Name of the effect"""
self.item_info: ItemInfo = item_info
"""Item info of the effect, effect manager, effect type, etc."""
self.progres_percentage: float = 0.0
"""For fire: how much the player still has to extinguish."""
def to_dict(self) -> dict: def to_dict(self) -> EffectState:
return { return {
"id": self.uuid, "id": self.uuid,
"type": self.name, "type": self.name,
......
...@@ -21,7 +21,7 @@ class referenced. ...@@ -21,7 +21,7 @@ class referenced.
This file defines the following classes: This file defines the following classes:
- `Order` - `Order`
- `OrderGeneration` - `OrderGeneration`
- `OrderAndScoreManager` - `OrderManager`
Further, it defines same implementations for the basic order generation based on random sampling: Further, it defines same implementations for the basic order generation based on random sampling:
- `RandomOrderGeneration` - `RandomOrderGeneration`
...@@ -60,9 +60,6 @@ from cooperative_cuisine.state_representation import OrderState ...@@ -60,9 +60,6 @@ from cooperative_cuisine.state_representation import OrderState
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
"""The logger for this module.""" """The logger for this module."""
ORDER_CATEGORY = "Order"
"""The string for the `category` value in the json state representation for all orders."""
class OrderConfig(TypedDict): class OrderConfig(TypedDict):
"""The configuration of the order in the `environment_config`under the `order` key.""" """The configuration of the order in the `environment_config`under the `order` key."""
...@@ -283,13 +280,13 @@ class OrderManager: ...@@ -283,13 +280,13 @@ class OrderManager:
def order_state(self) -> list[OrderState]: def order_state(self) -> list[OrderState]:
"""Similar to the `to_dict` in `Item` and `Counter`. Relevant for the state of the environment""" """Similar to the `to_dict` in `Item` and `Counter`. Relevant for the state of the environment"""
return [ return [
OrderState( {
id=order.uuid, "id": order.uuid,
category=ORDER_CATEGORY, "category": "Order",
meal=order.meal.name, "meal": order.meal.name,
start_time=order.start_time.isoformat(), "start_time": order.start_time.isoformat(),
max_duration=order.max_duration.total_seconds(), "max_duration": order.max_duration.total_seconds(),
) }
for order in self.open_orders for order in self.open_orders
] ]
......
...@@ -13,9 +13,9 @@ class PlayerInfo(TypedDict): ...@@ -13,9 +13,9 @@ class PlayerInfo(TypedDict):
"""Information about a player in an environment.""" """Information about a player in an environment."""
client_id: str client_id: str
"""ID of the client that controls the player.""" """ID of the client that controls the player. Used for the websocket url."""
player_hash: str player_hash: str
"""Hash of the player.""" """Hash of the player, for validation."""
player_id: str player_id: str
"""ID of the player.""" """ID of the player."""
......
...@@ -14,68 +14,70 @@ class OrderState(TypedDict): ...@@ -14,68 +14,70 @@ class OrderState(TypedDict):
"""Format of the state representation of an order.""" """Format of the state representation of an order."""
id: str id: str
"""ID of the order.""" """UUID of the order."""
category: Literal["Order"] category: Literal["Order"]
"""Category of the order.""" """Object category of env output, should be "Order"."""
meal: str meal: str
"""Name of the ordered meal.""" """Name of the ordered meal."""
start_time: datetime # isoformat str start_time: datetime | str # isoformat str
"""Time of the creation of the order.""" """Time of the creation of the order."""
max_duration: float max_duration: float
"""Maximum duration of the order.""" """Maximum duration of the order until it should be served."""
class EffectState(TypedDict): class EffectState(TypedDict):
"""Format of the state representation of an effect.""" """Format of the state representation of an effect (fire)."""
id: str id: str
"""ID of th effect.""" """UUID of th effect."""
type: str type: str
"""Type of the effect.""" """Type of the effect."""
progress_percentage: float | int progress_percentage: float | int
"""Progressed percentage of the effect.""" """Progressed percentage of the effect."""
inverse_progress: bool inverse_progress: bool
"""Progress in reverse.""" """Display the "inverse" of the percentage: 1 - progress_percentage."""
class ItemState(TypedDict): class ItemState(TypedDict):
"""Format of the state representation of an item.""" """Format of the state representation of an item."""
id: str id: str
"""ID of the item.""" """UUID of the item."""
category: Literal["Item"] | Literal["ItemCookingEquipment"] category: Literal["Item"] | Literal["ItemCookingEquipment"]
"""Category of the item.""" """Object category of env output, should be "Item" or "ItemCookingEquipment"."""
type: str type: str
"""Type of the item.""" """Type of the item, "Tomato", ..."""
progress_percentage: float | int progress_percentage: float | int
"""Progressed percentage of the item.""" """Progressed percentage of the item."""
inverse_progress: bool inverse_progress: bool
"""Progress in reverse.""" """Display the "inverse" of the percentage: 1 - progress_percentage."""
active_effects: list[EffectState] active_effects: list[EffectState]
"""Active effects of the item.""" """Active effects of the item, e.g., fire."""
# add ItemType Meal ? # add ItemType Meal ?
class CookingEquipmentState(TypedDict): class CookingEquipmentState(ItemState):
"""Format of the state representation of cooking equipment.""" """Format of the state representation of cooking equipment."""
content_list: list[ItemState] content_list: list[ItemState]
"""List of contents of the cooking equipment.""" """Items in/on the the cooking equipment."""
content_ready: None | ItemState content_ready: None | ItemState
"""Is the content ready to be released?""" """Utility attribute, to show meals that would be ready to be served. But could also be transformed to other
meals. (Therefore, keeping the `content_list`.) If not None, you can display the `content_ready` meal instead of
the `content_list`"""
class CounterState(TypedDict): class CounterState(TypedDict):
"""Format of the state representation of a counter.""" """Format of the state representation of a counter."""
id: str id: str
"""ID of the counter.""" """UUID of the counter."""
category: Literal["Counter"] category: Literal["Counter"]
"""Category of the counter.""" """Object category of env output, should be "Counter"."""
type: str type: str
"""Type of the counter.""" """Type of the counter."""
pos: list[float] pos: list[float]
"""Position of the counter.""" """2D Position of the counter."""
orientation: list[float] orientation: list[float]
"""Orientation / facing direction of the counter, currently only visual.""" """Orientation / facing direction of the counter, currently only visual."""
occupied_by: None | list[ occupied_by: None | list[
...@@ -93,13 +95,13 @@ class PlayerState(TypedDict): ...@@ -93,13 +95,13 @@ class PlayerState(TypedDict):
id: str id: str
"""ID of the player.""" """ID of the player."""
pos: list[float] pos: list[float]
"""Position of the player.""" """2D Position of the player."""
facing_direction: list[float] facing_direction: list[float]
"""The direction the player is facing.""" """The direction the player is facing."""
holding: ItemState | CookingEquipmentState | None holding: ItemState | CookingEquipmentState | None
"""What the player is currently holding.""" """What the player is currently holding."""
current_nearest_counter_pos: list[float] | None current_nearest_counter_pos: list[float] | None
"""Position of the currently nearest counter to the player.""" """2D Position of the currently nearest counter to the player."""
current_nearest_counter_id: str | None current_nearest_counter_id: str | None
"""ID of the currently nearest counter to the player.""" """ID of the currently nearest counter to the player."""
...@@ -115,12 +117,12 @@ class KitchenInfo(BaseModel): ...@@ -115,12 +117,12 @@ class KitchenInfo(BaseModel):
class ViewRestriction(BaseModel): class ViewRestriction(BaseModel):
"""Format of the state representation of a view restriction from the players perspectives. """Format of the state representation of a view restriction from the players perspectives.
Currently as a view cone, like a flashlight in the dark.""" Currently, as a view cone, like a flashlight in the dark."""
direction: list[float] direction: list[float]
"""Direction of what the player can see.""" """2D Direction of what the player can see."""
position: list[float] position: list[float]
"""Starting position of the view cone.""" """2D Starting position of the view cone."""
angle: int # degrees angle: int # degrees
"""Angle of the view cone.""" """Angle of the view cone."""
counter_mask: None | list[bool] counter_mask: None | list[bool]
...@@ -133,8 +135,11 @@ class InfoMsgLevel(Enum): ...@@ -133,8 +135,11 @@ class InfoMsgLevel(Enum):
"""Level of importance of the messages displayed to the players.""" """Level of importance of the messages displayed to the players."""
Normal = "Normal" Normal = "Normal"
"""Standard text, maybe black font color."""
Warning = "Warning" Warning = "Warning"
"""Warning text, should display emergency. Maybe red font color."""
Success = "Success" Success = "Success"
"""Something was good. Maybe green font color."""
class InfoMsg(TypedDict): class InfoMsg(TypedDict):
...@@ -158,17 +163,18 @@ class StateRepresentation(BaseModel): ...@@ -158,17 +163,18 @@ class StateRepresentation(BaseModel):
counters: list[CounterState] counters: list[CounterState]
"""Information about the counters in the environment.""" """Information about the counters in the environment."""
kitchen: KitchenInfo kitchen: KitchenInfo
"""General information about the kitchen.""" """General information about the kitchen: Width, Height."""
score: float | int score: float | int
"""The score achieved by the players.""" """The total score achieved by the players."""
orders: list[OrderState] orders: list[OrderState]
"""Current orders in the environment.""" """Current orders in the environment."""
ended: bool ended: bool
"""If the environment has ended.""" """If the environment has ended."""
env_time: datetime # isoformat str env_time: datetime # isoformat str
"""Current time of the environment, relative to the start time of the env.""" """Current time of the environment. Each environment start time is 2000-01-01T00:00:00. See
`cooperative_cuisine.utils.create_init_env_time`."""
remaining_time: float remaining_time: float
"""Remaining time for the players to act in the environment.""" """Remaining seconds for the players to act in the environment."""
view_restrictions: None | list[ViewRestriction] view_restrictions: None | list[ViewRestriction]
"""Restriction of the view for the players.""" """Restriction of the view for the players."""
served_meals: list[tuple[str, str]] served_meals: list[tuple[str, str]]
...@@ -176,7 +182,8 @@ class StateRepresentation(BaseModel): ...@@ -176,7 +182,8 @@ class StateRepresentation(BaseModel):
info_msg: list[tuple[str, str]] info_msg: list[tuple[str, str]]
"""Info messages for the players to be displayed.""" """Info messages for the players to be displayed."""
# is added: # is added:
# all_players_ready: bool all_players_ready: bool
"""Added by the game server, indicate if all players are ready and actions are passed to the environment."""
def create_json_schema() -> dict[str, Any]: def create_json_schema() -> dict[str, Any]:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment