From 0ab642d3d086ce25fef5f9621a62738e7ab51c0a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Florian=20Schr=C3=B6der?=
 <fschroeder@techfak.uni-bielefeld.de>
Date: Sat, 20 Jan 2024 02:20:05 +0100
Subject: [PATCH] Convert Stove to abstract "CookingCounter". Added item info
 and visualization for pizza and fish and chips meals including their
 ingredients and intermediate steps and cooking equipments and counters.
 Implemented accepting_not_ordered_meals func

---
 overcooked_simulator/__init__.py              |   3 +-
 overcooked_simulator/counters.py              |  36 +-
 .../game_content/environment_config.yaml      |   4 +-
 .../game_content/item_info.yaml               |  97 ++++-
 .../game_content/layouts/basic.layout         |  14 +-
 overcooked_simulator/game_items.py            |   2 -
 .../gui_2d_vis/overcooked_gui.py              |   8 +-
 .../gui_2d_vis/visualization.yaml             | 380 +++++++++++++++++-
 overcooked_simulator/order.py                 |  13 +-
 .../overcooked_environment.py                 |  70 +++-
 10 files changed, 592 insertions(+), 35 deletions(-)

diff --git a/overcooked_simulator/__init__.py b/overcooked_simulator/__init__.py
index 2c7bb07c..7ba05997 100644
--- a/overcooked_simulator/__init__.py
+++ b/overcooked_simulator/__init__.py
@@ -43,7 +43,8 @@ Initialize an environment....
 # Structure of the Documentation
 The API documentation follows the file and content structure in the repo.
 On the left you can find the navigation panel that brings you to the implementation of
-- the **counters**, including the kitchen utility objects like dispenser, stove, sink, etc.,
+- the **counters**, including the kitchen utility objects like dispenser, cooking counter (stove, deep fryer, oven),
+  sink, etc.,
 - the **game items**, the holdable ingredients, cooking equipment, composed ingredients, and meals,
 - in **main**, you find an example how to start a simulation,
 - the **orders**, how to sample incoming orders and their attributes,
diff --git a/overcooked_simulator/counters.py b/overcooked_simulator/counters.py
index 50d76412..75c64c31 100644
--- a/overcooked_simulator/counters.py
+++ b/overcooked_simulator/counters.py
@@ -2,11 +2,12 @@
 what should happen when the agent wants to pick something up from the counter. On the other side,
 the `Counter.drop_off` method receives the item what should be put on the counter. Before that the
 `Counter.can_drop_off` method checked if the item can be put on the counter. The progress on Counters or on objects
-on the counters are handled via the Counters. They have the task to delegate the progress call via the
-`progress` method, e.g., the `CuttingBoard.progress`. On which type of counter the progress method is called is currently defined in the
-environment class.
+on the counters are handled via the Counters. They have the task to delegate the progress call via the `progress`
+method, e.g., the `CuttingBoard.progress`. On which type of counter the progress method is called is currently
+defined in the environment class.
 
-Inside the item_info.yaml, equipment needs to be defined. It includes counters that are part of the interaction/requirements for the interaction.
+Inside the item_info.yaml, equipment needs to be defined. It includes counters that are part of the
+interaction/requirements for the interaction.
 
     CuttingBoard:
       type: Equipment
@@ -24,7 +25,7 @@ The defined counter classes are:
 - `Dispenser`
 - `PlateDispenser`
 - `Trashcan`
-- `Stove` (maybe abstracted in a class for all cooking machine counters (stove, deep fryer, oven))
+- `CookingCounter`
 - `Sink`
 - `SinkAddon`
 
@@ -226,7 +227,8 @@ class CuttingBoard(Counter):
 
 
 class ServingWindow(Counter):
-    """The orders and scores are updated based on completed and dropped off meals. The plate dispenser is pinged for the info about a plate outside of the kitchen.
+    """The orders and scores are updated based on completed and dropped off meals. The plate dispenser is pinged for
+    the info about a plate outside of the kitchen.
 
     All items in the `item_info.yml` with the type meal are considered to be servable, if they are ordered. Not
     ordered meals can also be served, if a `serving_not_ordered_meals` function is set in the `environment_config.yml`.
@@ -462,7 +464,7 @@ class Trashcan(Counter):
         return True
 
 
-class Stove(Counter):
+class CookingCounter(Counter):
     """Cooking machine. Currently, the stove which can have a pot and pan on top. In the future one class for stove,
     deep fryer, and oven.
 
@@ -473,9 +475,22 @@ class Stove(Counter):
     ```
     """
 
+    def __init__(
+        self,
+        name: str,
+        cooking_counter_equipments: dict[str, list[str]],
+        **kwargs,
+    ):
+        self.name = name
+        self.cooking_counter_equipments = cooking_counter_equipments
+        super().__init__(**kwargs)
+
     def can_drop_off(self, item: Item) -> bool:
         if self.occupied_by is None:
-            return isinstance(item, CookingEquipment) and item.name in ["Pot", "Pan"]
+            return (
+                isinstance(item, CookingEquipment)
+                and item.name in self.cooking_counter_equipments[self.name]
+            )
         else:
             return self.occupied_by.can_combine(item)
 
@@ -484,10 +499,14 @@ class Stove(Counter):
         if (
             self.occupied_by
             and isinstance(self.occupied_by, CookingEquipment)
+            and self.occupied_by.name in self.cooking_counter_equipments[self.name]
             and self.occupied_by.can_progress()
         ):
             self.occupied_by.progress(passed_time, now)
 
+    def __repr__(self):
+        return f"{self.name}(pos={self.pos},occupied_by={self.occupied_by})"
+
 
 class Sink(Counter):
     """The counter in which the dirty plates can be washed to clean plates.
@@ -537,7 +556,6 @@ class Sink(Counter):
             )
             if self.occupied_by[-1].progress_percentage == 1.0:
                 self.occupied_by[-1].reset()
-                print(self.transitions[self.occupied_by[-1].name]["result"])
                 self.occupied_by[-1].name = self.transitions[self.occupied_by[-1].name][
                     "result"
                 ]
diff --git a/overcooked_simulator/game_content/environment_config.yaml b/overcooked_simulator/game_content/environment_config.yaml
index 2698e1ff..f358a12e 100644
--- a/overcooked_simulator/game_content/environment_config.yaml
+++ b/overcooked_simulator/game_content/environment_config.yaml
@@ -8,7 +8,7 @@ game:
   time_limit_seconds: 180
 
 meals:
-  all: false
+  all: true
   # if all: false -> only orders for these meals are generated
   # TODO: what if this list is empty?
   list:
@@ -51,7 +51,7 @@ orders:
     expired_penalty_func: !!python/name:overcooked_simulator.order.simple_expired_penalty ''
     expired_penalty_kwargs:
       default: -5
-  serving_not_ordered_meals: null
+  serving_not_ordered_meals: !!python/name:overcooked_simulator.order.serving_not_ordered_meals_with_zero_score ''
   # a func that calcs a store for not ordered but served meals. Input: meal
 
 player_config:
diff --git a/overcooked_simulator/game_content/item_info.yaml b/overcooked_simulator/game_content/item_info.yaml
index 777e5f28..09003d8d 100644
--- a/overcooked_simulator/game_content/item_info.yaml
+++ b/overcooked_simulator/game_content/item_info.yaml
@@ -7,6 +7,12 @@ Sink:
 Stove:
   type: Equipment
 
+DeepFryer:
+  type: Equipment
+
+Oven:
+  type: Equipment
+
 Pot:
   type: Equipment
   equipment: Stove
@@ -15,6 +21,14 @@ Pan:
   type: Equipment
   equipment: Stove
 
+Basket:
+  type: Equipment
+  equipment: DeepFryer
+
+Peel:
+  type: Equipment
+  equipment: Oven
+
 DirtyPlate:
   type: Equipment
 
@@ -41,6 +55,21 @@ Meat:
 Bun:
   type: Ingredient
 
+Potato:
+  type: Ingredient
+
+Fish:
+  type: Ingredient
+
+Dough:
+  type: Ingredient
+
+Cheese:
+  type: Ingredient
+
+Sausage:
+  type: Ingredient
+
 ChoppedTomato:
   type: Ingredient
   needs: [ Tomato ]
@@ -59,18 +88,61 @@ ChoppedOnion:
   seconds: 5.0
   equipment: CuttingBoard
 
-ChoppedMeat:
+Patty:
   type: Ingredient
   needs: [ Meat ]
   seconds: 4.0
   equipment: CuttingBoard
 
+ChipsSticks:
+  type: Ingredient
+  needs: [ Potato ]
+  seconds: 4.0
+  equipment: CuttingBoard
+
+ChoppedFish:
+  type: Ingredient
+  needs: [ Fish ]
+  seconds: 4.0
+  equipment: CuttingBoard
+
+PizzaBase:
+  type: Ingredient
+  needs: [ Dough ]
+  seconds: 4.0
+  equipment: CuttingBoard
+
+GratedCheese:
+  type: Ingredient
+  needs: [ Cheese ]
+  seconds: 4.0
+  equipment: CuttingBoard
+
+ChoppedSausage:
+  type: Ingredient
+  needs: [ Sausage ]
+  seconds: 4.0
+  equipment: CuttingBoard
+
 CookedPatty:
   type: Ingredient
   seconds: 5.0
-  needs: [ ChoppedMeat ]
+  needs: [ Patty ]
   equipment: Pan
 
+FriedChips:
+  type: Ingredient
+  seconds: 5.0
+  needs: [ ChipsSticks ]
+  equipment: Basket
+
+FriedFish:
+  type: Ingredient
+  seconds: 5.0
+  needs: [ ChoppedFish ]
+  equipment: Basket
+
+
 # --------------------------------------------------------------------------------
 
 Burger:
@@ -94,3 +166,24 @@ OnionSoup:
   needs: [ ChoppedOnion, ChoppedOnion, ChoppedOnion ]
   seconds: 6.0
   equipment: Pot
+
+Chips:
+  type: Meal
+  needs: [ FriedChips ]
+  equipment: ~
+
+FishMeal:
+  type: Meal
+  needs: [ FriedFish ]
+  equipment: ~
+
+FishAndChips:
+  type: Meal
+  needs: [ FriedFish, FriedChips ]
+  equipment: ~
+
+Pizza:
+  type: Meal
+  needs: [ PizzaBase, ChoppedTomato, GratedCheese, ChoppedSausage ]
+  seconds: 7.0
+  equipment: Peel
diff --git a/overcooked_simulator/game_content/layouts/basic.layout b/overcooked_simulator/game_content/layouts/basic.layout
index 2e7395aa..ccc40763 100644
--- a/overcooked_simulator/game_content/layouts/basic.layout
+++ b/overcooked_simulator/game_content/layouts/basic.layout
@@ -1,9 +1,9 @@
-#QU#T###NLB#
+#QU#FO#TNLB#
 #__________M
+#__________K
+W__________I
+#__A_____A_D
+C__________E
+C__________G
 #__________#
-W___________
-#__A_____A__
-C___________
-C__________#
-#__________X
-#P#S+####S+#
\ No newline at end of file
+#P#S+#X##S+#
\ No newline at end of file
diff --git a/overcooked_simulator/game_items.py b/overcooked_simulator/game_items.py
index 08e255e3..b1b44641 100644
--- a/overcooked_simulator/game_items.py
+++ b/overcooked_simulator/game_items.py
@@ -167,7 +167,6 @@ class CookingEquipment(Item):
         ingredients = collections.Counter(
             item.name for item in self.content_list + other
         )
-        print(ingredients)
         return any(
             ingredients <= recipe["recipe"] for recipe in self.transitions.values()
         )
@@ -194,7 +193,6 @@ class CookingEquipment(Item):
                         "seconds": transition["seconds"],
                         "result": Item(name=result, item_info=transition["info"]),
                     }
-                    print(f"{self.name} {self.active_transition}, {self.content_list}")
                 break
         else:
             self.content_ready = None
diff --git a/overcooked_simulator/gui_2d_vis/overcooked_gui.py b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
index ed86bfad..e5f6a374 100644
--- a/overcooked_simulator/gui_2d_vis/overcooked_gui.py
+++ b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
@@ -14,7 +14,7 @@ import yaml
 from scipy.spatial import KDTree
 
 from overcooked_simulator import ROOT_DIR
-from overcooked_simulator.counters import Counter
+from overcooked_simulator.counters import Counter, CookingCounter
 from overcooked_simulator.game_items import (
     Item,
     CookingEquipment,
@@ -538,7 +538,11 @@ class PyGameGUI:
         else:
             self.draw_thing(
                 pos,
-                self.visualization_config[counter.__class__.__name__]["parts"],
+                self.visualization_config[
+                    counter.name
+                    if isinstance(counter, CookingCounter)
+                    else counter.__class__.__name__
+                ]["parts"],
             )
 
         if counter.occupied_by is not None:
diff --git a/overcooked_simulator/gui_2d_vis/visualization.yaml b/overcooked_simulator/gui_2d_vis/visualization.yaml
index a4ba4f7e..d1f17516 100644
--- a/overcooked_simulator/gui_2d_vis/visualization.yaml
+++ b/overcooked_simulator/gui_2d_vis/visualization.yaml
@@ -81,6 +81,12 @@ BunDispenser:
       height: 0.8
       width: 0.8
 
+Dispenser:
+  parts:
+    - color: gray83
+      type: rect
+      height: 0.8
+      width: 0.8
 
 ServingWindow:
   parts:
@@ -254,4 +260,376 @@ Pan:
   parts:
     - type: image
       path: images/pan.png
-      size: 1.1
\ No newline at end of file
+      size: 1.1
+
+DeepFryer:
+  parts:
+    - color: black
+      type: rect
+      height: 0.875
+      width: 0.875
+    - color: lightyellow2
+      type: rect
+      height: 0.675
+      width: 0.675
+Oven:
+  parts:
+    - color: gray83
+      type: rect
+      height: 0.875
+      width: 0.625
+    - type: rect
+      color: black
+      height: 0.8
+      width: 0.3
+      center_offset: [ -0.4, -0.1 ]
+
+Basket:
+  parts:
+    - type: rect
+      color: black
+      height: 0.8
+      width: 0.05
+      center_offset: [ -0.4, 0.2 ]
+    - type: rect
+      color: black
+      height: 0.8
+      width: 0.05
+      center_offset: [ -0.4, -0.2 ]
+    - type: rect
+      color: black
+      height: 0.05
+      width: 0.8
+      center_offset: [ 0.2, -0.4 ]
+    - type: rect
+      color: black
+      height: 0.05
+      width: 0.8
+      center_offset: [ -0.3, -0.4 ]
+    - type: rect
+      color: black
+      height: 0.8
+      width: 0.05
+      center_offset: [ -0.4, 0.0 ]
+
+Peel:
+  parts:
+    - type: rect
+      color: brown
+      height: 0.475
+      width: 0.675
+      center_offset: [ -0.23, -0.4 ]
+    - type: rect
+      color: brown
+      height: 0.1
+      width: 0.4
+      center_offset: [ -0.05, 0.2 ]
+
+Potato:
+  parts:
+    - type: circle
+      color: black
+      radius: 0.3
+    - type: circle
+      color: lightyellow2
+      radius: 0.25
+    - type: circle
+      color: gray83
+      radius: 0.1
+
+ChipsSticks:
+  parts:
+    - type: rect
+      color: ORANGE2
+      height: 0.8
+      width: 0.1
+      center_offset: [ -0.3, 0.2 ]
+    - type: rect
+      color: ORANGE2
+      height: 0.8
+      width: 0.1
+      center_offset: [ -0.3, -0.0 ]
+    - type: rect
+      color: ORANGE2
+      height: 0.8
+      width: 0.1
+      center_offset: [ -0.3, -0.2 ]
+    - type: rect
+      color: ORANGE2
+      height: 0.1
+      width: 0.8
+      center_offset: [ 0.0, -0.1 ]
+
+FriedChips:
+  parts:
+    - type: rect
+      color: ORANGE3
+      height: 0.1
+      width: 0.8
+      center_offset: [ -0.2, -0.3 ]
+    - type: rect
+      color: ORANGE3
+      height: 0.1
+      width: 0.8
+      center_offset: [ 0.0, -0.3 ]
+    - type: rect
+      color: ORANGE3
+      height: 0.1
+      width: 0.8
+      center_offset: [ 0.2, -0.3 ]
+    - type: rect
+      color: ORANGE3
+      height: 0.8
+      width: 0.1
+      center_offset: [ -0.3, -0.0 ]
+
+Chips:
+  parts:
+    - type: rect
+      color: ORANGE3
+      height: 0.1
+      width: 0.8
+      center_offset: [ -0.2, -0.3 ]
+    - type: rect
+      color: ORANGE3
+      height: 0.1
+      width: 0.8
+      center_offset: [ 0.0, -0.3 ]
+    - type: rect
+      color: ORANGE3
+      height: 0.1
+      width: 0.8
+      center_offset: [ 0.2, -0.3 ]
+    - type: rect
+      color: ORANGE3
+      height: 0.8
+      width: 0.1
+      center_offset: [ -0.3, -0.0 ]
+
+Fish:
+  parts:
+    - type: rect
+      color: PALEVIOLETRED
+      height: 0.3
+      width: 0.6
+      center_offset: [ 0.0, -0.15 ]
+
+ChoppedFish:
+  parts:
+    - type: rect
+      color: PALEVIOLETRED
+      height: 0.3
+      width: 0.3
+      center_offset: [ -0.2, -0.15 ]
+    - type: rect
+      color: PALEVIOLETRED
+      height: 0.3
+      width: 0.3
+      center_offset: [ 0.2, -0.15 ]
+
+FriedFish:
+  parts:
+    - type: rect
+      color: PALEVIOLETRED4
+      height: 0.3
+      width: 0.3
+      center_offset: [ -0.2, -0.15 ]
+    - type: rect
+      color: PALEVIOLETRED4
+      height: 0.3
+      width: 0.3
+      center_offset: [ 0.2, -0.15 ]
+
+FishMeal:
+  parts:
+    - type: rect
+      color: PALEVIOLETRED4
+      height: 0.3
+      width: 0.3
+      center_offset: [ -0.2, -0.15 ]
+    - type: rect
+      color: PALEVIOLETRED4
+      height: 0.3
+      width: 0.3
+      center_offset: [ 0.2, -0.15 ]
+
+FishAndChips:
+  parts:
+    - type: rect
+      color: ORANGE3
+      height: 0.1
+      width: 0.8
+      center_offset: [ -0.2, -0.3 ]
+    - type: rect
+      color: ORANGE3
+      height: 0.1
+      width: 0.8
+      center_offset: [ 0.0, -0.3 ]
+    - type: rect
+      color: ORANGE3
+      height: 0.1
+      width: 0.8
+      center_offset: [ 0.2, -0.3 ]
+    - type: rect
+      color: ORANGE3
+      height: 0.8
+      width: 0.1
+      center_offset: [ -0.3, -0.0 ]
+    - type: rect
+      color: PALEVIOLETRED4
+      height: 0.3
+      width: 0.3
+      center_offset: [ -0.2, -0.15 ]
+    - type: rect
+      color: PALEVIOLETRED4
+      height: 0.3
+      width: 0.3
+      center_offset: [ 0.2, -0.15 ]
+
+Dough:
+  parts:
+    - type: circle
+      color: lightyellow1
+      radius: 0.3
+    - type: circle
+      color: lightyellow2
+      radius: 0.25
+
+PizzaBase:
+  parts:
+    - type: circle
+      color: lightyellow2
+      radius: 0.5
+    - type: circle
+      color: lightyellow1
+      radius: 0.4
+
+Sausage:
+  parts:
+    - type: rect
+      color: RED2
+      height: 0.2
+      width: 0.6
+      center_offset: [ 0.0, 0.15 ]
+
+ChoppedSausage:
+  parts:
+    - type: rect
+      color: RED2
+      height: 0.2
+      width: 0.3
+      center_offset: [ -0.2, 0.15 ]
+    - type: rect
+      color: RED2
+      height: 0.2
+      width: 0.3
+      center_offset: [ 0.2, 0.15 ]
+
+Cheese:
+  parts:
+    - type: rect
+      color: YELLOW1
+      height: 0.3
+      width: 0.3
+
+GratedCheese:
+  parts:
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.6
+      width: 0.05
+      center_offset: [ -0.3, 0.2 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.6
+      width: 0.05
+      center_offset: [ -0.3, -0.0 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.6
+      width: 0.05
+      center_offset: [ -0.3, -0.2 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.05
+      width: 0.6
+      center_offset: [ 0.0, -0.1 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.05
+      width: 0.6
+      center_offset: [ -0.2, -0.3 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.05
+      width: 0.6
+      center_offset: [ 0.0, -0.3 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.05
+      width: 0.6
+      center_offset: [ 0.2, -0.3 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.6
+      width: 0.05
+      center_offset: [ -0.3, -0.0 ]
+
+Pizza:
+  parts:
+    - type: circle
+      color: ORANGE3
+      radius: 0.5
+    - type: circle
+      color: orangered1
+      radius: 0.4
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.6
+      width: 0.1
+      center_offset: [ -0.3, 0.2 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.6
+      width: 0.1
+      center_offset: [ -0.3, -0.0 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.6
+      width: 0.1
+      center_offset: [ -0.3, -0.2 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.1
+      width: 0.6
+      center_offset: [ 0.0, -0.1 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.1
+      width: 0.6
+      center_offset: [ -0.2, -0.3 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.1
+      width: 0.6
+      center_offset: [ 0.0, -0.3 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.1
+      width: 0.6
+      center_offset: [ 0.2, -0.3 ]
+    - type: rect
+      color: lightgoldenrodyellow
+      height: 0.6
+      width: 0.1
+      center_offset: [ -0.3, -0.0 ]
+    - type: rect
+      color: RED4
+      height: 0.2
+      width: 0.3
+      center_offset: [ -0.2, 0.15 ]
+    - type: rect
+      color: RED4
+      height: 0.2
+      width: 0.3
+      center_offset: [ 0.2, 0.15 ]
\ No newline at end of file
diff --git a/overcooked_simulator/order.py b/overcooked_simulator/order.py
index 5f396940..06ff63d9 100644
--- a/overcooked_simulator/order.py
+++ b/overcooked_simulator/order.py
@@ -178,13 +178,13 @@ class OrderAndScoreManager:
                             accept, score = self.serving_not_ordered_meals(meal)
                             if accept:
                                 log.info(
-                                    f"Serving meal without order {meal.name} with score {score}"
+                                    f"Serving meal without order {meal.name!r} with score {score}"
                                 )
                                 self.increment_score(score)
                                 self.served_meals.append((meal, env_time))
                             return accept
                         log.info(
-                            f"Do not serve meal {meal.name} because it is not ordered"
+                            f"Do not serve meal {meal.name!r} because it is not ordered"
                         )
                         return False
                     order, index = order
@@ -197,7 +197,9 @@ class OrderAndScoreManager:
                         "end_time": env_time,
                         "score": score,
                     }
-                    log.info(f"Serving meal {meal.name} with order with score {score}")
+                    log.info(
+                        f"Serving meal {meal.name!r} with order with score {score}"
+                    )
                     self.last_finished.append(order)
                     del self.open_orders[index]
                     self.served_meals.append((meal, env_time))
@@ -569,3 +571,8 @@ def simple_expired_penalty(item: ItemInfo, default: float, **kwargs) -> float:
         ```
     """
     return default
+
+
+def serving_not_ordered_meals_with_zero_score(meal: Item) -> Tuple[bool, float | int]:
+    """Not ordered meals are accepted but do not affect the score."""
+    return True, 0
diff --git a/overcooked_simulator/overcooked_environment.py b/overcooked_simulator/overcooked_environment.py
index b60ed369..554f1f47 100644
--- a/overcooked_simulator/overcooked_environment.py
+++ b/overcooked_simulator/overcooked_environment.py
@@ -20,7 +20,7 @@ from overcooked_simulator.counters import (
     Trashcan,
     Dispenser,
     ServingWindow,
-    Stove,
+    CookingCounter,
     Sink,
     PlateDispenser,
     SinkAddon,
@@ -135,6 +135,16 @@ class Environment:
             if info.type == ItemType.Meal
         }
 
+        cooking_counter_equipments = {
+            cooking_counter: [
+                equipment
+                for equipment, e_info in self.item_info.items()
+                if e_info.equipment and e_info.equipment.name == cooking_counter
+            ]
+            for cooking_counter, info in self.item_info.items()
+            if info.type == ItemType.Equipment and info.equipment is None
+        }
+
         self.SYMBOL_TO_CHARACTER_MAP = {
             "#": Counter,
             "C": lambda pos: CuttingBoard(
@@ -155,6 +165,11 @@ class Environment:
             ),
             "T": lambda pos: Dispenser(pos, self.item_info["Tomato"]),
             "L": lambda pos: Dispenser(pos, self.item_info["Lettuce"]),
+            "K": lambda pos: Dispenser(pos, self.item_info["Potato"]),  # Kartoffel
+            "I": lambda pos: Dispenser(pos, self.item_info["Fish"]),  # fIIIsh
+            "D": lambda pos: Dispenser(pos, self.item_info["Dough"]),
+            "E": lambda pos: Dispenser(pos, self.item_info["Cheese"]),  # chEEEEse
+            "G": lambda pos: Dispenser(pos, self.item_info["Sausage"]),  # sausaGe
             "P": lambda pos: PlateDispenser(
                 plate_transitions=plate_transitions,
                 pos=pos,
@@ -170,8 +185,10 @@ class Environment:
             "N": lambda pos: Dispenser(pos, self.item_info["Onion"]),  # N for oNioN
             "_": "Free",
             "A": "Agent",
-            "U": lambda pos: Stove(
-                pos,
+            "U": lambda pos: CookingCounter(
+                name="Stove",
+                cooking_counter_equipments=cooking_counter_equipments,
+                pos=pos,
                 occupied_by=CookingEquipment(
                     name="Pot",
                     item_info=self.item_info["Pot"],
@@ -186,8 +203,10 @@ class Environment:
                     },
                 ),
             ),  # Stove with pot: U because it looks like a pot
-            "Q": lambda pos: Stove(
-                pos,
+            "Q": lambda pos: CookingCounter(
+                name="Stove",
+                cooking_counter_equipments=cooking_counter_equipments,
+                pos=pos,
                 occupied_by=CookingEquipment(
                     name="Pan",
                     item_info=self.item_info["Pan"],
@@ -202,6 +221,43 @@ class Environment:
                     },
                 ),
             ),  # Stove with pan: Q because it looks like a pan
+            "O": lambda pos: CookingCounter(
+                name="Oven",
+                cooking_counter_equipments=cooking_counter_equipments,
+                pos=pos,
+                occupied_by=CookingEquipment(
+                    name="Peel",
+                    item_info=self.item_info["Peel"],
+                    transitions={
+                        item: {
+                            "seconds": info.seconds,
+                            "needs": info.needs,
+                            "info": info,
+                        }
+                        for item, info in self.item_info.items()
+                        if info.equipment is not None and info.equipment.name == "Peel"
+                    },
+                ),
+            ),
+            "F": lambda pos: CookingCounter(
+                name="DeepFryer",
+                cooking_counter_equipments=cooking_counter_equipments,
+                pos=pos,
+                occupied_by=CookingEquipment(
+                    name="Basket",
+                    item_info=self.item_info["Basket"],
+                    transitions={
+                        item: {
+                            "seconds": info.seconds,
+                            "needs": info.needs,
+                            "info": info,
+                        }
+                        for item, info in self.item_info.items()
+                        if info.equipment is not None
+                        and info.equipment.name == "Basket"
+                    },
+                ),
+            ),  # Stove with pan: Q because it looks like a pan
             "B": lambda pos: Dispenser(pos, self.item_info["Bun"]),
             "M": lambda pos: Dispenser(pos, self.item_info["Meat"]),
             "S": lambda pos: Sink(
@@ -633,7 +689,9 @@ class Environment:
         with self.lock:
             if not self.game_ended:
                 for counter in self.counters:
-                    if isinstance(counter, (CuttingBoard, Stove, Sink, PlateDispenser)):
+                    if isinstance(
+                        counter, (CuttingBoard, CookingCounter, Sink, PlateDispenser)
+                    ):
                         counter.progress(passed_time=passed_time, now=self.env_time)
                 self.order_and_score.progress(
                     passed_time=passed_time, now=self.env_time
-- 
GitLab