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:
state = self.get_state(player_id, additional_key_values)
json_data = json.dumps(state)
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
def reset_env_time(self):
......
......@@ -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`.
Additionally, the `Effect` class is defined for the `Fire` effect.
## Code Documentation
"""
......@@ -26,7 +28,13 @@ import datetime
import logging
import uuid
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:
from cooperative_cuisine.effects import EffectManager
......@@ -136,7 +144,7 @@ class ActiveTransitionTypedDict(TypedDict):
class Item:
"""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). """
def __init__(
......@@ -157,6 +165,11 @@ class Item:
self.active_effects: list[Effect] = []
"""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):
if self.progress_equipment is None:
return f"{self.name}({self.extra_repr})"
......@@ -166,11 +179,6 @@ class Item:
def __eq__(self, other):
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:
"""Check if the item can be combined with the other. After it returned True the `combine` method is called."""
return False
......@@ -198,7 +206,7 @@ class Item:
self.progress_percentage = 0.0
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."""
return {
"id": self.uuid,
......@@ -236,6 +244,10 @@ class CookingEquipment(Item):
for transition in self.transitions.values():
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:
# already cooking or nothing to combine
if other is None or (
......@@ -299,6 +311,11 @@ class CookingEquipment(Item):
# todo set active transition for fire/burnt?
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)
for result, transition in self.transitions.items():
if transition.type == ItemType.Effect:
......@@ -337,17 +354,13 @@ class CookingEquipment(Item):
self.content_list = []
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"""
content = self.content_list
self.reset_content()
self.reset()
return content
@property
def extra_repr(self):
return f"{self.content_list}, {self.content_ready}"
def reset(self):
super().reset()
self.active_transition = None
......@@ -360,18 +373,15 @@ class CookingEquipment(Item):
return self.content_list[0]
return None
def to_dict(self) -> dict:
def to_dict(self) -> CookingEquipmentState:
d = super().to_dict()
d.update(
(
("content_list", [c.to_dict() for c in self.content_list]),
(
"content_ready",
self.content_ready.to_dict()
if self.content_ready is not None
else None,
),
)
{
"content_list": [c.to_dict() for c in self.content_list],
"content_ready": self.content_ready.to_dict()
if self.content_ready is not None
else None,
}
)
return d
......@@ -382,8 +392,8 @@ class Plate(CookingEquipment):
def __init__(self, transitions: dict[str, ItemInfo], clean: bool, *args, **kwargs):
self.clean: bool = clean
"""If the plate is clean or dirty."""
self.meals = set(transitions.keys())
"""All meals can be hold by a clean plate"""
self.meals: Set[str] = set(transitions.keys())
"""All meals (meal names) can be hold by a clean plate,"""
super().__init__(
name=self.create_name(),
transitions={k: v for k, v in transitions.items() if not v.equipment},
......@@ -394,12 +404,12 @@ class Plate(CookingEquipment):
def progress(self, equipment: str, percent: float):
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
`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 (
isinstance(other, CookingEquipment)
......@@ -418,13 +428,19 @@ class Plate(CookingEquipment):
# this is here, due to a circular import if it would be in the effects.py
class Effect:
"""Effects on counters and items. Like Fire."""
def __init__(self, name: str, item_info: ItemInfo, uid: str = None):
self.uuid: str = uuid.uuid4().hex if uid is None else uid
self.name = name
self.item_info = item_info
self.progres_percentage = 0.0
"""ID of the effect instance."""
self.name: str = name
"""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 {
"id": self.uuid,
"type": self.name,
......
......@@ -21,7 +21,7 @@ class referenced.
This file defines the following classes:
- `Order`
- `OrderGeneration`
- `OrderAndScoreManager`
- `OrderManager`
Further, it defines same implementations for the basic order generation based on random sampling:
- `RandomOrderGeneration`
......@@ -60,9 +60,6 @@ from cooperative_cuisine.state_representation import OrderState
log = logging.getLogger(__name__)
"""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):
"""The configuration of the order in the `environment_config`under the `order` key."""
......@@ -283,13 +280,13 @@ class OrderManager:
def order_state(self) -> list[OrderState]:
"""Similar to the `to_dict` in `Item` and `Counter`. Relevant for the state of the environment"""
return [
OrderState(
id=order.uuid,
category=ORDER_CATEGORY,
meal=order.meal.name,
start_time=order.start_time.isoformat(),
max_duration=order.max_duration.total_seconds(),
)
{
"id": order.uuid,
"category": "Order",
"meal": order.meal.name,
"start_time": order.start_time.isoformat(),
"max_duration": order.max_duration.total_seconds(),
}
for order in self.open_orders
]
......
......@@ -13,9 +13,9 @@ class PlayerInfo(TypedDict):
"""Information about a player in an environment."""
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
"""Hash of the player."""
"""Hash of the player, for validation."""
player_id: str
"""ID of the player."""
......
......@@ -14,68 +14,70 @@ class OrderState(TypedDict):
"""Format of the state representation of an order."""
id: str
"""ID of the order."""
"""UUID of the order."""
category: Literal["Order"]
"""Category of the order."""
"""Object category of env output, should be "Order"."""
meal: str
"""Name of the ordered meal."""
start_time: datetime # isoformat str
start_time: datetime | str # isoformat str
"""Time of the creation of the order."""
max_duration: float
"""Maximum duration of the order."""
"""Maximum duration of the order until it should be served."""
class EffectState(TypedDict):
"""Format of the state representation of an effect."""
"""Format of the state representation of an effect (fire)."""
id: str
"""ID of th effect."""
"""UUID of th effect."""
type: str
"""Type of the effect."""
progress_percentage: float | int
"""Progressed percentage of the effect."""
inverse_progress: bool
"""Progress in reverse."""
"""Display the "inverse" of the percentage: 1 - progress_percentage."""
class ItemState(TypedDict):
"""Format of the state representation of an item."""
id: str
"""ID of the item."""
"""UUID of the item."""
category: Literal["Item"] | Literal["ItemCookingEquipment"]
"""Category of the item."""
"""Object category of env output, should be "Item" or "ItemCookingEquipment"."""
type: str
"""Type of the item."""
"""Type of the item, "Tomato", ..."""
progress_percentage: float | int
"""Progressed percentage of the item."""
inverse_progress: bool
"""Progress in reverse."""
"""Display the "inverse" of the percentage: 1 - progress_percentage."""
active_effects: list[EffectState]
"""Active effects of the item."""
"""Active effects of the item, e.g., fire."""
# add ItemType Meal ?
class CookingEquipmentState(TypedDict):
class CookingEquipmentState(ItemState):
"""Format of the state representation of cooking equipment."""
content_list: list[ItemState]
"""List of contents of the cooking equipment."""
"""Items in/on the the cooking equipment."""
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):
"""Format of the state representation of a counter."""
id: str
"""ID of the counter."""
"""UUID of the counter."""
category: Literal["Counter"]
"""Category of the counter."""
"""Object category of env output, should be "Counter"."""
type: str
"""Type of the counter."""
pos: list[float]
"""Position of the counter."""
"""2D Position of the counter."""
orientation: list[float]
"""Orientation / facing direction of the counter, currently only visual."""
occupied_by: None | list[
......@@ -93,13 +95,13 @@ class PlayerState(TypedDict):
id: str
"""ID of the player."""
pos: list[float]
"""Position of the player."""
"""2D Position of the player."""
facing_direction: list[float]
"""The direction the player is facing."""
holding: ItemState | CookingEquipmentState | None
"""What the player is currently holding."""
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
"""ID of the currently nearest counter to the player."""
......@@ -115,12 +117,12 @@ class KitchenInfo(BaseModel):
class ViewRestriction(BaseModel):
"""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 of what the player can see."""
"""2D Direction of what the player can see."""
position: list[float]
"""Starting position of the view cone."""
"""2D Starting position of the view cone."""
angle: int # degrees
"""Angle of the view cone."""
counter_mask: None | list[bool]
......@@ -133,8 +135,11 @@ class InfoMsgLevel(Enum):
"""Level of importance of the messages displayed to the players."""
Normal = "Normal"
"""Standard text, maybe black font color."""
Warning = "Warning"
"""Warning text, should display emergency. Maybe red font color."""
Success = "Success"
"""Something was good. Maybe green font color."""
class InfoMsg(TypedDict):
......@@ -158,17 +163,18 @@ class StateRepresentation(BaseModel):
counters: list[CounterState]
"""Information about the counters in the environment."""
kitchen: KitchenInfo
"""General information about the kitchen."""
"""General information about the kitchen: Width, Height."""
score: float | int
"""The score achieved by the players."""
"""The total score achieved by the players."""
orders: list[OrderState]
"""Current orders in the environment."""
ended: bool
"""If the environment has ended."""
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 for the players to act in the environment."""
"""Remaining seconds for the players to act in the environment."""
view_restrictions: None | list[ViewRestriction]
"""Restriction of the view for the players."""
served_meals: list[tuple[str, str]]
......@@ -176,7 +182,8 @@ class StateRepresentation(BaseModel):
info_msg: list[tuple[str, str]]
"""Info messages for the players to be displayed."""
# 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]:
......
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