diff --git a/overcooked_simulator/counters.py b/overcooked_simulator/counters.py
index 616ad6949e266bf9a00207525ab8334054751c95..41bd67eeb5c10e23ea15538dd90fe9ca1afce515 100644
--- a/overcooked_simulator/counters.py
+++ b/overcooked_simulator/counters.py
@@ -3,24 +3,21 @@ from __future__ import annotations
 import logging
 from collections import deque
 from datetime import datetime, timedelta
-from typing import TYPE_CHECKING, Optional
-
-from overcooked_simulator.utils import create_init_env_time
+from typing import TYPE_CHECKING, Optional, Callable
 
 if TYPE_CHECKING:
     from overcooked_simulator.overcooked_environment import (
-        GameScore,
+        OrderAndScoreManager,
     )
 
 import numpy as np
 import numpy.typing as npt
 
 from overcooked_simulator.game_items import (
-    CuttableItem,
     Item,
     CookingEquipment,
-    Meal,
     Plate,
+    ItemInfo,
 )
 
 
@@ -34,6 +31,10 @@ class Counter:
         self.pos: npt.NDArray[float] = pos
         self.occupied_by: Optional[Item] = occupied_by
 
+    @property
+    def occupied(self):
+        return self.occupied_by is not None
+
     def pick_up(self, on_hands: bool = True):
         """Gets called upon a player performing the pickup action. If the counter can give something to
         the player, it does so. In the standard counter this is when an item is on the counter.
@@ -95,15 +96,30 @@ class Counter:
 
 
 class CuttingBoard(Counter):
-    def __init__(self, pos: np.ndarray):
+    def __init__(self, pos: np.ndarray, transitions: dict):
         self.progressing = False
+        self.transitions = transitions
         super().__init__(pos)
 
     def progress(self, passed_time: timedelta, now: datetime):
         """Called by environment step function for time progression"""
-        if self.progressing:
-            if isinstance(self.occupied_by, CuttableItem):
-                self.occupied_by.progress()
+        if (
+            self.occupied
+            and self.progressing
+            and self.occupied_by.name in self.transitions
+        ):
+            percent = (
+                passed_time.total_seconds()
+                / self.transitions[self.occupied_by.name]["seconds"]
+            )
+            self.occupied_by.progress(
+                equipment=self.__class__.__name__, percent=percent
+            )
+            if self.occupied_by.progress_percentage == 1.0:
+                self.occupied_by.reset()
+                self.occupied_by.name = self.transitions[self.occupied_by.name][
+                    "result"
+                ]
 
     def start_progress(self):
         """Starts the cutting process."""
@@ -124,37 +140,32 @@ class CuttingBoard(Counter):
 
 class ServingWindow(Counter):
     def __init__(
-        self, pos, game_score: GameScore, plate_dispenser: PlateDispenser = None
+        self,
+        pos,
+        order_and_score: OrderAndScoreManager,
+        meals: set[str],
+        env_time_func: Callable[[], datetime],
+        plate_dispenser: PlateDispenser = None,
     ):
-        self.game_score = game_score
+        self.order_and_score = order_and_score
         self.plate_dispenser = plate_dispenser
+        self.meals = meals
+        self.env_time_func = env_time_func
         super().__init__(pos)
 
     def drop_off(self, item) -> Item | None:
-        reward = 5
-        log.debug(f"Drop off item {item}")
-        # TODO define rewards
-        self.game_score.increment_score(reward)
-        if self.plate_dispenser is not None:
-            self.plate_dispenser.update_plate_out_of_kitchen()
-        return None
-
-    def can_score(self, item):
-        if (
-            isinstance(item, CookingEquipment)
-            and "Plate" in item.name
-            and item.content is not None
-        ):
-            if isinstance(item.content, Meal) and item.content.progressed_steps:
-                return item.content.finished
-            if not item.content.item_info.steps_needed and len(
-                item.content.item_info.needs
-            ) == len(item.content.parts):
-                return True
-        return False
+        env_time = self.env_time_func()
+        if self.order_and_score.serve_meal(item=item, env_time=env_time):
+            if self.plate_dispenser is not None:
+                self.plate_dispenser.update_plate_out_of_kitchen(env_time=env_time)
+            return None
+        return item
 
     def can_drop_off(self, item: Item) -> bool:
-        return self.can_score(item)
+        return isinstance(item, CookingEquipment) and (
+            (item.content_ready is not None and item.content_ready.name in self.meals)
+            or (len(item.content_list) == 1 and item.content_list[0].name in self.meals)
+        )
 
     def pick_up(self, on_hands: bool = True):
         pass
@@ -164,16 +175,16 @@ class ServingWindow(Counter):
 
 
 class Dispenser(Counter):
-    def __init__(self, pos, dispensing):
+    def __init__(self, pos, dispensing: ItemInfo):
         self.dispensing = dispensing
         super().__init__(
             pos,
-            self.dispensing.create_item(),
+            self.create_item(),
         )
 
     def pick_up(self, on_hands: bool = True):
         return_this = self.occupied_by
-        self.occupied_by = self.dispensing.create_item()
+        self.occupied_by = self.create_item()
         return return_this
 
     def drop_off(self, item: Item) -> Item | None:
@@ -186,17 +197,26 @@ class Dispenser(Counter):
     def __repr__(self):
         return f"{self.dispensing.name}Dispenser"
 
+    def create_item(self):
+        kwargs = {
+            "name": self.dispensing.name,
+            "item_info": self.dispensing,
+        }
+        return Item(**kwargs)
+
 
 class PlateDispenser(Counter):
-    def __init__(self, pos, dispensing, plate_config):
+    def __init__(
+        self, pos, dispensing, plate_config, plate_transitions, **kwargs
+    ) -> None:
+        super().__init__(pos, **kwargs)
         self.dispensing = dispensing
-        super().__init__(pos)
         self.occupied_by = deque()
         self.out_of_kitchen_timer = []
         self.plate_config = {"plate_delay": [5, 10]}
         self.plate_config.update(plate_config)
         self.next_plate_time = datetime.max
-        self.env_time = create_init_env_time()  # is overwritten in progress anyway
+        self.plate_transitions = plate_transitions
         self.setup_plates()
 
     def pick_up(self, on_hands: bool = True):
@@ -222,12 +242,12 @@ class PlateDispenser(Counter):
         return None
 
     def add_dirty_plate(self):
-        self.occupied_by.appendleft(self.dispensing.create_item())
+        self.occupied_by.appendleft(self.create_item())
 
-    def update_plate_out_of_kitchen(self):
+    def update_plate_out_of_kitchen(self, env_time: datetime):
         """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 = self.env_time + timedelta(
+        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],
@@ -242,22 +262,18 @@ class PlateDispenser(Counter):
         """Create plates based on the config. Clean and dirty ones."""
         if "dirty_plates" in self.plate_config:
             self.occupied_by.extend(
-                [
-                    self.dispensing.create_item()
-                    for _ in range(self.plate_config["dirty_plates"])
-                ]
+                [self.create_item() for _ in range(self.plate_config["dirty_plates"])]
             )
         if "clean_plates" in self.plate_config:
             self.occupied_by.extend(
                 [
-                    self.dispensing.create_item(clean_plate=True)
+                    self.create_item(clean=True)
                     for _ in range(self.plate_config["clean_plates"])
                 ]
             )
 
     def progress(self, passed_time: timedelta, now: datetime):
         """Check if plates arrive from outside the kitchen and add a dirty plate accordingly"""
-        self.env_time = now
         if self.next_plate_time < now:
             idx_delete = []
             for i, times in enumerate(self.out_of_kitchen_timer):
@@ -276,6 +292,14 @@ class PlateDispenser(Counter):
     def __repr__(self):
         return "PlateReturn"
 
+    def create_item(self, clean: bool = False):
+        kwargs = {
+            "clean": clean,
+            "transitions": self.plate_transitions,
+            "item_info": self.dispensing,
+        }
+        return Plate(**kwargs)
+
 
 class Trash(Counter):
     def pick_up(self, on_hands: bool = True):
@@ -283,7 +307,7 @@ class Trash(Counter):
 
     def drop_off(self, item: Item) -> Item | None:
         if isinstance(item, CookingEquipment):
-            item.content = None
+            item.reset_content()
             return item
         return None
 
@@ -305,27 +329,44 @@ class Stove(Counter):
             and isinstance(self.occupied_by, CookingEquipment)
             and self.occupied_by.can_progress()
         ):
-            self.occupied_by.progress()
+            self.occupied_by.progress(passed_time, now)
 
 
 class Sink(Counter):
-    def __init__(self, pos, sink_addon=None):
+    def __init__(self, pos, transitions, sink_addon=None):
         super().__init__(pos)
         self.progressing = False
         self.sink_addon: SinkAddon = sink_addon
         self.occupied_by = deque()
+        self.transitions = transitions
+
+    @property
+    def occupied(self):
+        return len(self.occupied_by) != 0
 
     def progress(self, passed_time: timedelta, now: datetime):
         """Called by environment step function for time progression"""
-        if self.progressing:
-            if self.occupied_by:
-                self.occupied_by[-1].progress()
-                if self.occupied_by[-1].finished:
-                    plate = self.occupied_by.pop()
-                    if not self.occupied_by:
-                        self.pause_progress()
-                    plate.finished_call()
-                    self.sink_addon.add_clean_plate(plate)
+        if (
+            self.occupied
+            and self.progressing
+            and self.occupied_by[-1].name in self.transitions
+        ):
+            percent = (
+                passed_time.total_seconds()
+                / self.transitions[self.occupied_by[-1].name]["seconds"]
+            )
+            self.occupied_by[-1].progress(
+                equipment=self.__class__.__name__, percent=percent
+            )
+            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"
+                ]
+                plate = self.occupied_by.pop()
+                plate.clean = True
+                self.sink_addon.add_clean_plate(plate)
 
     def start_progress(self):
         """Starts the cutting process."""
@@ -360,7 +401,7 @@ class Sink(Counter):
 class SinkAddon(Counter):
     def __init__(self, pos, occupied_by=None):
         super().__init__(pos)
-        self.occupied_by = deque(occupied_by) if occupied_by else deque()
+        self.occupied_by = deque([occupied_by]) if occupied_by else deque()
 
     def can_drop_off(self, item: Item) -> bool:
         return self.occupied_by and self.occupied_by[-1].can_combine(item)
diff --git a/overcooked_simulator/game_content/environment_config.yaml b/overcooked_simulator/game_content/environment_config.yaml
index 158a22290759fa0415806ee59ce968972e07029c..b6b7c579ec950edd189c41e2ec09a5cbda2a239f 100644
--- a/overcooked_simulator/game_content/environment_config.yaml
+++ b/overcooked_simulator/game_content/environment_config.yaml
@@ -1,4 +1,63 @@
 plates:
-  clean_plates: 3
+  clean_plates: 1
   dirty_plates: 2
-  plate_delay: [ 5, 10 ]
\ No newline at end of file
+  plate_delay: [ 5, 10 ]
+  # seconds until the dirty plate arrives.
+
+game:
+  time_limit_seconds: 180
+
+meals:
+  all: false
+  # if all: false -> only orders for these meals are generated
+  # TODO: what if this list is empty?
+  list:
+    - TomatoSoup
+    - OnionSoup
+    - Salad
+
+orders:
+  kwargs:
+    duration_sample:
+      # how long should the orders be alive
+      # 'random' library call with getattr, kwargs are passed to the function
+      func: uniform
+      kwargs:
+        a: 40
+        b: 60
+    max_orders: 6
+    # maximum number of active orders at the same time
+    num_start_meals: 3
+    # number of orders generated at the start of the environment
+    sample_on_dur: true
+    # if true, the next order is generated based on the sample_on_dur_func method in seconds
+    # if sample_on_serving is also true, the value is sampled after a meal was served, otherwise it is sampled directly after an order generation.
+    sample_on_dur_func:
+      # 'random' library call with getattr, kwargs are passed to the function
+      func: uniform
+      kwargs:
+        a: 10
+        b: 20
+    sample_on_serving: false
+    # The sample time for a new incoming order is only generated after a meal was served.
+    score_calc_gen_func: !!python/name:overcooked_simulator.order.simple_score_calc_gen_func ''
+    score_calc_gen_kwargs:
+      # the kwargs for the score_calc_gen_func
+      other: 0
+      scores:
+        Burger: 15
+        OnionSoup: 10
+        Salad: 5
+        TomatoSoup: 10
+    expired_penalty_func: !!python/name:overcooked_simulator.order.simple_expired_penalty ''
+    expired_penalty_kwargs:
+      default: -5
+  serving_not_ordered_meals: null
+  # a func that calcs a store for not ordered but served meals. Input: meal
+  order_gen_class: !!python/name:overcooked_simulator.order.RandomOrderGeneration ''
+  # the class to that receives the kwargs. Should be a child class of OrderGeneration in order.py
+
+player_config:
+  radius: 0.4
+  move_dist: 0.15
+  interaction_range: 1.6
\ No newline at end of file
diff --git a/overcooked_simulator/game_content/item_info.yaml b/overcooked_simulator/game_content/item_info.yaml
index f3c79c5f87c03c4ec0a6fa4501f857bcca179d50..777e5f28e106587285ecf8eb6d1e77e23911914e 100644
--- a/overcooked_simulator/game_content/item_info.yaml
+++ b/overcooked_simulator/game_content/item_info.yaml
@@ -1,64 +1,96 @@
+CuttingBoard:
+  type: Equipment
+
+Sink:
+  type: Equipment
+
+Stove:
+  type: Equipment
+
+Pot:
+  type: Equipment
+  equipment: Stove
+
+Pan:
+  type: Equipment
+  equipment: Stove
+
+DirtyPlate:
+  type: Equipment
+
+Plate:
+  type: Equipment
+  needs: [ DirtyPlate ]
+  seconds: 2.0
+  equipment: Sink
+
+# --------------------------------------------------------------------------------
+
 Tomato:
   type: Ingredient
-  is_cuttable: True
-  steps_needed: 500
 
 Lettuce:
   type: Ingredient
-  is_cuttable: True
-  steps_needed: 500
 
 Onion:
   type: Ingredient
-  is_cuttable: True
-  steps_needed: 700
 
 Meat:
   type: Ingredient
-  is_cuttable: True
-  steps_needed: 500
+
+Bun:
+  type: Ingredient
+
+ChoppedTomato:
+  type: Ingredient
+  needs: [ Tomato ]
+  seconds: 4.0
+  equipment: CuttingBoard
+
+ChoppedLettuce:
+  type: Ingredient
+  needs: [ Lettuce ]
+  seconds: 3.0
+  equipment: CuttingBoard
+
+ChoppedOnion:
+  type: Ingredient
+  needs: [ Onion ]
+  seconds: 5.0
+  equipment: CuttingBoard
+
+ChoppedMeat:
+  type: Ingredient
+  needs: [ Meat ]
+  seconds: 4.0
+  equipment: CuttingBoard
 
 CookedPatty:
-  type: Meal
-  steps_needed: 500
+  type: Ingredient
+  seconds: 5.0
   needs: [ ChoppedMeat ]
-  finished_progress_name: CookedPatty
   equipment: Pan
 
-Bun:
-  type: Ingredient
+# --------------------------------------------------------------------------------
 
 Burger:
   type: Meal
   needs: [ Bun, ChoppedLettuce, ChoppedTomato, CookedPatty ]
-  equipment: Plate
+  equipment: ~
 
 Salad:
   type: Meal
-  needs: [ ChoppedLettuce, Tomato ]
-  equipment: Plate
+  needs: [ ChoppedLettuce, ChoppedTomato ]
+  equipment: ~
 
 TomatoSoup:
   type: Meal
-  finished_progress_name: TomatoSoup
-  steps_needed: 500
-  needs: [ Tomato, Tomato, Tomato ]
+  needs: [ ChoppedTomato, ChoppedTomato, ChoppedTomato ]
+  seconds: 6.0
   equipment: Pot
 
 OnionSoup:
   type: Meal
-  finished_progress_name: OnionSoup
-  steps_needed: 500
   needs: [ ChoppedOnion, ChoppedOnion, ChoppedOnion ]
+  seconds: 6.0
   equipment: Pot
-
-Plate:
-  type: Equipment
-  is_cuttable: True
-  steps_needed: 200
-
-Pot:
-  type: Equipment
-
-Pan:
-  type: Equipment
\ No newline at end of file
diff --git a/overcooked_simulator/game_content/item_info_debug.yaml b/overcooked_simulator/game_content/item_info_debug.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..32c8696610ab0e1b3f059071da01d7c182e6b80f
--- /dev/null
+++ b/overcooked_simulator/game_content/item_info_debug.yaml
@@ -0,0 +1,96 @@
+CuttingBoard:
+  type: Equipment
+
+Sink:
+  type: Equipment
+
+Stove:
+  type: Equipment
+
+Pot:
+  type: Equipment
+  equipment: Stove
+
+Pan:
+  type: Equipment
+  equipment: Stove
+
+DirtyPlate:
+  type: Equipment
+
+Plate:
+  type: Equipment
+  needs: [ DirtyPlate ]
+  seconds: 1.0
+  equipment: Sink
+
+# --------------------------------------------------------------------------------
+
+Tomato:
+  type: Ingredient
+
+Lettuce:
+  type: Ingredient
+
+Onion:
+  type: Ingredient
+
+Meat:
+  type: Ingredient
+
+Bun:
+  type: Ingredient
+
+ChoppedTomato:
+  type: Ingredient
+  needs: [ Tomato ]
+  seconds: 0.1
+  equipment: CuttingBoard
+
+ChoppedLettuce:
+  type: Ingredient
+  needs: [ Lettuce ]
+  seconds: 0.1
+  equipment: CuttingBoard
+
+ChoppedOnion:
+  type: Ingredient
+  needs: [ Onion ]
+  seconds: 0.1
+  equipment: CuttingBoard
+
+ChoppedMeat:
+  type: Ingredient
+  needs: [ Meat ]
+  seconds: 0.1
+  equipment: CuttingBoard
+
+CookedPatty:
+  type: Ingredient
+  seconds: 2.0
+  needs: [ ChoppedMeat ]
+  equipment: Pan
+
+# --------------------------------------------------------------------------------
+
+Burger:
+  type: Meal
+  needs: [ Bun, ChoppedLettuce, ChoppedTomato, CookedPatty ]
+  equipment: ~
+
+Salad:
+  type: Meal
+  needs: [ ChoppedLettuce, ChoppedTomato ]
+  equipment: ~
+
+TomatoSoup:
+  type: Meal
+  needs: [ ChoppedTomato, ChoppedTomato, ChoppedTomato ]
+  seconds: 3.0
+  equipment: Pot
+
+OnionSoup:
+  type: Meal
+  needs: [ ChoppedOnion, ChoppedOnion, ChoppedOnion ]
+  seconds: 3.0
+  equipment: Pot
diff --git a/overcooked_simulator/game_content/player_config.yaml b/overcooked_simulator/game_content/player_config.yaml
index 8a26daf15cf4ddecb91d09f191bd70e0a571e456..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/overcooked_simulator/game_content/player_config.yaml
+++ b/overcooked_simulator/game_content/player_config.yaml
@@ -1,3 +0,0 @@
-radius: 0.4
-move_speed_units_per_second: 0.1
-interaction_range: 1.6
\ No newline at end of file
diff --git a/overcooked_simulator/game_items.py b/overcooked_simulator/game_items.py
index 937d061742e2084e2851a294c793953ab5a4bebf..4592e68bbe4057719b3618fbf7b34513fb4d232b 100644
--- a/overcooked_simulator/game_items.py
+++ b/overcooked_simulator/game_items.py
@@ -1,8 +1,11 @@
 from __future__ import annotations
 
+import collections
 import dataclasses
+import datetime
 import logging
 from enum import Enum
+from typing import Optional
 
 log = logging.getLogger(__name__)
 
@@ -17,9 +20,7 @@ class ItemType(Enum):
 class ItemInfo:
     type: ItemType = dataclasses.field(compare=False)
     name: str = dataclasses.field(compare=True)
-    is_cuttable: bool = dataclasses.field(compare=False, default=False)
-    steps_needed: int = dataclasses.field(compare=False, default=0)
-    finished_progress_name: str = dataclasses.field(compare=False, default="Chopped*")
+    seconds: float = dataclasses.field(compare=False, default=0)
     needs: list[ItemInfo] = dataclasses.field(compare=False, default_factory=list)
     equipment: ItemInfo | None = dataclasses.field(compare=False, default=None)
 
@@ -30,56 +31,17 @@ class ItemInfo:
     def __post_init__(self):
         self.type = ItemType(self.type)
 
-    def get_finished_name(self):
-        if "*" in self.finished_progress_name:
-            return self.finished_progress_name.replace("*", self.name)
-        return self.name
-
-    def create_item(self, clean_plate=False, parts=None) -> Item:
-        match self.type:
-            case ItemType.Ingredient:
-                if self.is_cuttable:
-                    return CuttableItem(
-                        name=self.name,
-                        finished=False,
-                        steps_needed=self.steps_needed,
-                        finished_name=self.get_finished_name(),
-                        item_info=self,
-                    )
-                return Item(name=self.name, item_info=self)
-            case ItemType.Equipment:
-                if "Plate" in self.name:
-                    return Plate(
-                        name=self.name,
-                        steps_needed=self.steps_needed,
-                        finished=False,
-                        item_info=self,
-                        clean=clean_plate,
-                    )
-                else:
-                    return CookingEquipment(name=self.name, item_info=self)
-            case ItemType.Meal:
-                return Meal(
-                    name=self.name,
-                    finished=False,
-                    steps_needed=self.steps_needed,
-                    finished_name=self.get_finished_name(),
-                    item_info=self,
-                    parts=parts,
-                )
-
     def add_start_meal_to_equipment(self, start_item: ItemInfo):
         self._start_meals.append(start_item)
 
     def sort_start_meals(self):
         self._start_meals.sort(key=lambda item_info: len(item_info.needs))
 
-    def can_start_meal(self, items: list[Item]):
-        # TODO check specific order / only specific start items
-        return items and self._return_start_meal(items) is not None
+    # def can_start_meal(self, items: list[Item]):
+    #     return items and self._return_start_meal(items) is not None
 
-    def start_meal(self, items: list[Item]) -> Item:
-        return self._return_start_meal(items).create_item(parts=items)
+    # def start_meal(self, items: list[Item]) -> Item:
+    #     return self._return_start_meal(items).create_item(parts=items)
 
     def _return_start_meal(self, items: list[Item]) -> ItemInfo | None:
         for meal in self._start_meals:
@@ -99,15 +61,14 @@ class Item:
     def __init__(self, name: str, item_info: ItemInfo, *args, **kwargs):
         self.name = self.__class__.__name__ if name is None else name
         self.item_info = item_info
-
-    def can_combine(self, other):
-        return False
-
-    def combine(self, other) -> Item | None:
-        pass
+        self.progress_equipment = None
+        self.progress_percentage = 0.0
 
     def __repr__(self):
-        return f"{self.name}({self.extra_repr})"
+        if self.progress_equipment is None:
+            return f"{self.name}({self.extra_repr})"
+        else:
+            return f"{self.name}(progress={round(self.progress_percentage * 100, 2)}%,{self.extra_repr})"
 
     def __eq__(self, other):
         return other and self.name == other.name
@@ -116,207 +77,167 @@ class Item:
     def extra_repr(self):
         return ""
 
+    def can_combine(self, other) -> bool:
+        return False
 
-class ProgressibleItem(Item):
-    """Class for items which need to be processed (cut, cooked, ...)"""
-
-    def __init__(
-        self,
-        finished: bool = False,
-        steps_needed: int = 500,
-        finished_name: str = None,
-        *args,
-        **kwargs,
-    ):
-        super().__init__(*args, **kwargs)
-        self.progressed_steps = steps_needed if finished else 0
-        self.steps_needed = steps_needed
-        self.finished = finished
-        self.finished_name = (
-            f"Chopped{self.name}" if finished_name is None else finished_name
-        )
-
-    def progress(self):
-        """Progresses the item process as long as it is not finished."""
-        if self.progressed_steps >= self.steps_needed:
-            self.finished = True
-            self.finished_call()
-        if not self.finished:
-            self.progressed_steps += 1
+    def combine(self, other) -> Item | None:
+        pass
 
-    def can_progress(self) -> bool:
-        return True
+    def progress(self, equipment: str, percent: float):
+        """Progresses the item process on the given equipment as long as it is not finished."""
+        if self.progress_equipment is None:
+            self.progress_equipment = equipment
 
-    def finished_call(self):
-        self.name = self.finished_name
+        if self.progress_equipment == equipment:
+            self.progress_percentage += percent
+            self.progress_percentage = min(self.progress_percentage, 1.0)
+        else:
+            log.warning(
+                f"{self.name} expected progress on {self.progress_equipment}, but got {percent * 100}% on {equipment}"
+            )
 
     def reset(self):
-        self.finished = False
-        self.progressed_steps = 0
+        self.progress_equipment = None
+        self.progress_percentage = 0.0
 
-    def __repr__(self):
-        if not self.steps_needed or self.finished:
-            return f"{self.name}({self.extra_repr})"
-        else:
-            return f"{self.name}(progress={int(self.progressed_steps / self.steps_needed * 100)}%,{self.extra_repr})"
 
+class CookingEquipment(Item):
+    def __init__(self, transitions: dict, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.transitions = transitions
+        self.active_transition: Optional[dict] = None
 
-class CuttableItem(ProgressibleItem):
-    """Class of item which can be processed by the cutting board."""
+        self.content_ready: Item | None = None
+        self.content_list: list[Item] = []
 
+        log.debug(f"Initialize {self.name}: {self.transitions}")
 
-class CookingEquipment(Item):
-    def __init__(self, content: Meal = None, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.content = content
+        for transition in self.transitions.values():
+            transition["recipe"] = collections.Counter(transition["needs"])
 
-    def can_combine(self, other):
+    def can_combine(self, other) -> bool:
+        # already cooking or nothing to combine
         if other is None:
             return False
-        if self.content is None:
-            if isinstance(other, CookingEquipment):
-                return other.can_release_content()
-            # TODO check other is start of a meal, create meal
-            if isinstance(other, Meal) and "Plate" in self.name:
-                return not other.steps_needed or other.finished
-            return self.item_info.can_start_meal([other])
-        if self.content.can_combine(other):
-            return True
-        if isinstance(other, CookingEquipment) and other.content:
-            other = other.content
-        return self.item_info.can_start_meal(
-            [other]
-            + ([self.content] if self.content.progressed_steps else self.content.parts)
-        )
 
-    def combine(self, other):
-        if self.content is None:
-            if isinstance(other, CookingEquipment):
-                self.content = other.release()
-                return other
-            if isinstance(other, Meal) and "Plate" in self.name:
-                self.content = other
-                return
-            # find starting meal for other
-            self.content = self.item_info.start_meal([other])
-            return
-        if not self.content.can_combine(other):
-            if isinstance(other, CookingEquipment) and other.content:
-                content = other.release()
-                self.content = self.item_info.start_meal(
-                    [content]
-                    + (
-                        [self.content]
-                        if self.content.progressed_steps
-                        else self.content.parts
-                    )
-                )
-                return other
-            else:
-                self.content = self.item_info.start_meal(
-                    [other]
-                    + (
-                        [self.content]
-                        if self.content.progressed_steps
-                        else self.content.parts
-                    )
-                )
-            return
-        self.content.combine(other)
-
-    def can_progress(self, counter_type="Stove") -> bool:
-        return (
-            self.content
-            and isinstance(self.content, ProgressibleItem)
-            and self.content.can_progress()
+        if isinstance(other, CookingEquipment):
+            other = other.content_list
+        else:
+            other = [other]
+
+        # other extends ingredients for meal
+        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()
         )
 
-    def progress(self):
-        self.content.progress()
+    def combine(self, other) -> Item | None:
+        return_value = None
+        if isinstance(other, CookingEquipment):
+            self.content_list.extend(other.content_list)
+            return_value = other
+            other.reset_content()
+        elif isinstance(other, list):
+            self.content_list.extend(other)
+        else:
+            self.content_list.append(other)
+
+        ingredients = collections.Counter(item.name for item in self.content_list)
+        for result, transition in self.transitions.items():
+            recipe = transition["recipe"]
+            if ingredients == recipe:
+                if transition["seconds"] == 0:
+                    self.content_ready = Item(name=result, item_info=transition["info"])
+                else:
+                    self.active_transition = {
+                        "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
+        return return_value
 
-    def can_release_content(self) -> bool:
-        return (
-            self.content
-            and isinstance(self.content, ProgressibleItem)
-            and self.content.finished
-        )
+    def can_progress(self) -> bool:
+        return self.active_transition is not None
+
+    def progress(self, passed_time: datetime.timedelta, now: datetime.datetime):
+        percent = passed_time.total_seconds() / self.active_transition["seconds"]
+        super().progress(equipment=self.name, percent=percent)
+        if self.progress_percentage == 1.0:
+            self.content_list = [self.active_transition["result"]]
+            self.reset()
+
+        # todo set active transition for fire/burnt?
+
+    # def can_release_content(self) -> bool:
+    #     return (
+    #         self.content
+    #         and isinstance(self.content, ProgressibleItem)
+    #         and self.content.finished
+    #     )
+    def reset_content(self):
+        self.content_list = []
+        self.content_ready = None
 
     def release(self):
-        content = self.content
-        self.content = None
+        content = self.content_list
+        self.reset_content()
         return content
 
     @property
     def extra_repr(self):
-        return self.content
-
-
-class Meal(ProgressibleItem):
-    def __init__(self, parts=None, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self.parts = [] if parts is None else parts
-        # self.rules ...
-
-    def can_combine(self, other) -> bool:
-        if other and not self.finished:
-            satisfied = [False for _ in range(len(self.parts))]
-            for n in self.item_info.needs:
-                for i, p in enumerate(self.parts):
-                    if not satisfied[i] and p.name == n:
-                        satisfied[i] = True
-                        break
-                else:
-                    if n == other.name:
-                        return True
-        return False
+        return f"{self.content_list}, {self.content_ready}"
 
-    def combine(self, other):
-        self.parts.append(other)
-
-    def can_progress(self) -> bool:
-        return self.item_info.steps_needed and len(self.item_info.needs) == len(
-            self.parts
-        )
-
-    def finished_call(self):
-        super().finished_call()
+    def reset(self):
+        super().reset()
+        self.active_transition = None
 
-    @property
-    def extra_repr(self):
-        return self.parts
+    def get_potential_meal(self) -> Item | None:
+        if self.content_ready:
+            return self.content_ready
+        if len(self.content_list) == 1:
+            return self.content_list[0]
+        return None
 
 
 class Plate(CookingEquipment):
-    def __init__(
-        self, clean, steps_needed, finished, content: Meal = None, *args, **kwargs
-    ):
-        super().__init__(content, *args, **kwargs)
+    def __init__(self, transitions, clean, *args, **kwargs):
         self.clean = clean
-        self.name = self.create_name()
-        self.steps_needed = steps_needed
-        self.finished = finished
-        self.progressed_steps = steps_needed if finished else 0
+        self.meals = set(transitions.keys())
+        super().__init__(
+            name=self.create_name(),
+            transitions={
+                k: v for k, v in transitions.items() if not v["info"].equipment
+            },
+            *args,
+            **kwargs,
+        )
 
     def finished_call(self):
         self.clean = True
         self.name = self.create_name()
 
-    def progress(self):
-        """Progresses the item process as long as it is not finished."""
-        if self.progressed_steps >= self.steps_needed:
-            self.finished = True
-            self.finished_call()
-        if not self.finished:
-            self.progressed_steps += 1
+    def progress(self, equipment: str, percent: float):
+        Item.progress(self, equipment, percent)
 
-    def can_progress(self, counter_type="Sink") -> bool:
-        return not self.clean
+    def create_name(self):
+        return "Plate" if self.clean else "DirtyPlate"
 
     def can_combine(self, other):
-        return self.clean and super().can_combine(other)
-
-    def combine(self, other):
-        return super().combine(other)
-
-    def create_name(self):
-        return "CleanPlate" if self.clean else "DirtyPlate"
+        if not super().can_combine(other):
+            if (
+                isinstance(other, CookingEquipment)
+                and len(other.content_list) == 1
+                and not self.content_list
+                and self.clean
+            ):
+                return other.content_list[0].name in self.meals
+            return False
+        elif self.clean:
+            return True
+        return False
diff --git a/overcooked_simulator/gui_2d_vis/gui_theme.json b/overcooked_simulator/gui_2d_vis/gui_theme.json
index 3ca11b55479e8d69bbbf8ab0e3fc4b46cad713e7..862d3d963c63ae0abe29c2c37516be2bc750b1cf 100644
--- a/overcooked_simulator/gui_2d_vis/gui_theme.json
+++ b/overcooked_simulator/gui_2d_vis/gui_theme.json
@@ -44,6 +44,44 @@
     },
     "misc": {
       "tool_tip_delay": "1.5"
+    },
+    "font": {
+      "size": 15,
+      "bold": 1
+    }
+  },
+  "#timer_label": {
+    "colours": {
+      "normal_text": "#000000"
+    },
+    "font": {
+      "size": 20,
+      "bold": 0
+    }
+  },
+  "#score_label": {
+    "colours": {
+      "normal_text": "#000000"
+    },
+    "font": {
+      "size": 20,
+      "bold": 1
+    }
+  },
+  "#orders_label": {
+    "colours": {
+      "normal_text": "#000000"
+    },
+    "font": {
+      "size": 20,
+      "bold": 0
+    }
+  },
+  "#quit_button": {
+    "colours": {
+      "normal_bg": "#f71b29",
+      "hovered_bg": "#bf0310",
+      "normal_border": "#DDDDDD"
     }
   }
 }
\ No newline at end of file
diff --git a/overcooked_simulator/gui_2d_vis/images/arrow_right.png b/overcooked_simulator/gui_2d_vis/images/arrow_right.png
new file mode 100644
index 0000000000000000000000000000000000000000..522ec051e8f1ad938c8e53cd0e8b563f1e383cb1
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/arrow_right.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/bun.png b/overcooked_simulator/gui_2d_vis/images/bun.png
new file mode 100644
index 0000000000000000000000000000000000000000..2c2f180610dbfd664acb61d2d733d7a3df75e2e5
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/bun.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/burger.png b/overcooked_simulator/gui_2d_vis/images/burger.png
new file mode 100644
index 0000000000000000000000000000000000000000..df84a65fcca2ff5edcc9e258fe80a4005dc0716e
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/burger.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/cooked_patty.png b/overcooked_simulator/gui_2d_vis/images/cooked_patty.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d5b9270e569211eca7e971b21e8e9db944ac9bc
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/cooked_patty.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/cutting_board_large.png b/overcooked_simulator/gui_2d_vis/images/cutting_board_large.png
new file mode 100644
index 0000000000000000000000000000000000000000..8f50132ce6b8646ec1f35f1599c7210f7d0e1c4f
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/cutting_board_large.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/lettuce_cut_smaller.png b/overcooked_simulator/gui_2d_vis/images/lettuce_cut_smaller.png
new file mode 100644
index 0000000000000000000000000000000000000000..0fc897f72a779a998a264fce5ed4363bd8813d32
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/lettuce_cut_smaller.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/lettuce_smaller.png b/overcooked_simulator/gui_2d_vis/images/lettuce_smaller.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c0abbda27f4ac859d8965d8c0828d0685277c08
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/lettuce_smaller.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/meat.png b/overcooked_simulator/gui_2d_vis/images/meat.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff750e1c4858d0bab5dd2434966ee38c7f88e085
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/meat.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/onion_cut.png b/overcooked_simulator/gui_2d_vis/images/onion_cut.png
new file mode 100644
index 0000000000000000000000000000000000000000..f33ec9ab41c1aea7fbe6553891db86032346d29e
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/onion_cut.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/onion_large.png b/overcooked_simulator/gui_2d_vis/images/onion_large.png
new file mode 100644
index 0000000000000000000000000000000000000000..dd70c1a5dd8a271c353909e4717ebe3629e80d54
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/onion_large.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/onion_soup_plate.png b/overcooked_simulator/gui_2d_vis/images/onion_soup_plate.png
new file mode 100644
index 0000000000000000000000000000000000000000..89a81f7b27509ce34005d3752949e1c4a1dd3084
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/onion_soup_plate.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/onion_soup_pot.png b/overcooked_simulator/gui_2d_vis/images/onion_soup_pot.png
new file mode 100644
index 0000000000000000000000000000000000000000..35ee354b302c94fe9c70a3502a1ebbfd2616cb54
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/onion_soup_pot.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/pan.png b/overcooked_simulator/gui_2d_vis/images/pan.png
new file mode 100644
index 0000000000000000000000000000000000000000..afb288156faee0fdbb6917de0b18e0e1b698862d
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/pan.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/pot_large.png b/overcooked_simulator/gui_2d_vis/images/pot_large.png
new file mode 100644
index 0000000000000000000000000000000000000000..6c702693d5234eb32b148d68cce3024ce17d7aef
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/pot_large.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/pot_smaller.png b/overcooked_simulator/gui_2d_vis/images/pot_smaller.png
new file mode 100644
index 0000000000000000000000000000000000000000..5f8d29c3f66beba620e518b09e210783ff43ebe1
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/pot_smaller.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/raw_patty.png b/overcooked_simulator/gui_2d_vis/images/raw_patty.png
new file mode 100644
index 0000000000000000000000000000000000000000..85a42e2a04efbddf49c99e23705f192e7d089e3b
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/raw_patty.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/salad.png b/overcooked_simulator/gui_2d_vis/images/salad.png
new file mode 100644
index 0000000000000000000000000000000000000000..e227f75660c7c89e4ed7604dc38983bfc084f769
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/salad.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/sink_large.png b/overcooked_simulator/gui_2d_vis/images/sink_large.png
new file mode 100644
index 0000000000000000000000000000000000000000..0758d02bc6f4b2c80cfd4c984f8914b74bb0ff83
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/sink_large.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/tomato3_cut_smaller.png b/overcooked_simulator/gui_2d_vis/images/tomato3_cut_smaller.png
new file mode 100644
index 0000000000000000000000000000000000000000..f9aabe95db07b439e5557f2811d83ff516d0c4aa
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/tomato3_cut_smaller.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/tomato3_smaller.png b/overcooked_simulator/gui_2d_vis/images/tomato3_smaller.png
new file mode 100644
index 0000000000000000000000000000000000000000..64bc2f9a2a48e3ef85ddd5bd990fe38553b1f106
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/tomato3_smaller.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/tomato_soup_plate.png b/overcooked_simulator/gui_2d_vis/images/tomato_soup_plate.png
new file mode 100644
index 0000000000000000000000000000000000000000..006070021fc1c9ca3087309025bffe5d48083132
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/tomato_soup_plate.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/tomato_soup_pot.png b/overcooked_simulator/gui_2d_vis/images/tomato_soup_pot.png
new file mode 100644
index 0000000000000000000000000000000000000000..70ea72b49ea7fc5916d4e1b11f81810f2f7bcfab
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/tomato_soup_pot.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/trash2.png b/overcooked_simulator/gui_2d_vis/images/trash2.png
new file mode 100644
index 0000000000000000000000000000000000000000..76c76c47209ab25636a3952ab77e605fd3a7dd8f
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/trash2.png differ
diff --git a/overcooked_simulator/gui_2d_vis/images/trash3.png b/overcooked_simulator/gui_2d_vis/images/trash3.png
new file mode 100644
index 0000000000000000000000000000000000000000..5a391dc446197664636119fd360eeace6cb17b3c
Binary files /dev/null and b/overcooked_simulator/gui_2d_vis/images/trash3.png differ
diff --git a/overcooked_simulator/gui_2d_vis/overcooked_gui.py b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
index b94b950bd0078eea10f64addafcce755ed41daa5..3353d900b3774c9ad504709dc809fde3a7dbe832 100644
--- a/overcooked_simulator/gui_2d_vis/overcooked_gui.py
+++ b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
@@ -4,6 +4,7 @@ import logging
 import math
 import sys
 from collections import deque
+from datetime import timedelta
 from enum import Enum
 
 import numpy as np
@@ -17,20 +18,19 @@ from websockets.sync.client import connect
 from overcooked_simulator import ROOT_DIR
 from overcooked_simulator.counters import Counter
 from overcooked_simulator.game_items import (
-    ProgressibleItem,
     Item,
     CookingEquipment,
-    Meal,
     Plate,
 )
 from overcooked_simulator.gui_2d_vis.game_colors import BLUE
 from overcooked_simulator.gui_2d_vis.game_colors import colors, Color
+from overcooked_simulator.order import Order
 from overcooked_simulator.overcooked_environment import Action
 from overcooked_simulator.simulation_runner import Simulator
 
-USE_PLAYER_COOK_SPRITES = False
+USE_PLAYER_COOK_SPRITES = True
 SHOW_INTERACTION_RANGE = False
-SHOW_COUNTER_CENTERS = True
+SHOW_COUNTER_CENTERS = False
 
 
 class MenuStates(Enum):
@@ -40,7 +40,7 @@ class MenuStates(Enum):
 
 
 def create_polygon(n, length):
-    if n == 0:
+    if n == 1:
         return np.array([0, 0])
 
     vector = np.array([length, 0])
@@ -116,17 +116,24 @@ class PyGameGUI:
             self.visualization_config = yaml.safe_load(file)
 
         self.screen_margin = self.visualization_config["GameWindow"]["screen_margin"]
-        self.window_width = self.visualization_config["GameWindow"]["start_width"]
-        self.window_height = self.visualization_config["GameWindow"]["start_height"]
+        self.min_width = self.visualization_config["GameWindow"]["min_width"]
+        self.min_height = self.visualization_config["GameWindow"]["min_height"]
+
+        self.buttons_width = self.visualization_config["GameWindow"]["buttons_width"]
+        self.buttons_height = self.visualization_config["GameWindow"]["buttons_height"]
+
+        self.order_bar_height = self.visualization_config["GameWindow"][
+            "order_bar_height"
+        ]
+
+        self.window_width = self.min_width
+        self.window_height = self.min_height
 
         self.main_window = pygame.display.set_mode(
-            (
-                self.window_width,
-                self.window_height,
-            )
+            (self.window_width, self.window_height)
         )
 
-        self.game_width, self.game_height = 0, 0
+        # self.game_width, self.game_height = 0, 0
 
         self.images_path = ROOT_DIR / "pygame_gui" / "images"
 
@@ -135,46 +142,50 @@ class PyGameGUI:
         self.menu_state = MenuStates.Start
         self.manager: pygame_gui.UIManager
 
-    def init_window_sizes(self):
-        state = self.request_state()
-        print("THIS:", state["counters"])
-        counter_positions = np.array([c["pos"] for c in state["counters"]])
-        print(counter_positions)
-        kitchen_width = np.max(counter_positions[:, 0]) + 0.5
-        kitchen_height = np.max(counter_positions[:, 1]) + 0.5
-
-        print(kitchen_width, kitchen_height)
-
-        # if self.visualization_config["GameWindow"]["WhatIsFixed"] == "window_width":
-        #     game_width = self.visualization_config["GameWindow"]["size"]
-        #     kitchen_aspect_ratio = (
-        #         self.simulator.env.kitchen_height / self.simulator.env.kitchen_width
-        #     )
-        #     game_height = int(game_width * kitchen_aspect_ratio)
-        #     grid_size = int(game_width / self.simulator.env.kitchen_width)
-        # elif self.visualization_config["GameWindow"]["WhatIsFixed"] == "grid":
-        #     grid_size = self.visualization_config["GameWindow"]["size"]
-        #     game_width, game_height = (
-        #         self.simulator.env.kitchen_width * grid_size,
-        #         self.simulator.env.kitchen_height * grid_size,
-        #     )
-        # else:
-        #     game_width, game_height = 0, 0
-        #     grid_size = 0
-
-        grid_size = 40
-        game_width = kitchen_width * grid_size
-        game_height = kitchen_height * grid_size
+    def get_window_sizes(self, state: dir):
+        counter_positions = np.array([c.pos for c in state["counters"]])
+        kitchen_width = counter_positions[:, 0].max() + 0.5
+        kitchen_height = counter_positions[:, 1].max() + 0.5
+        if self.visualization_config["GameWindow"]["WhatIsFixed"] == "window_width":
+            game_width = self.visualization_config["GameWindow"]["size"]
+            kitchen_aspect_ratio = kitchen_height / kitchen_width
+            game_height = int(game_width * kitchen_aspect_ratio)
+            grid_size = int(game_width / self.simulator.env.kitchen_width)
+
+        elif self.visualization_config["GameWindow"]["WhatIsFixed"] == "window_height":
+            game_height = self.visualization_config["GameWindow"]["size"]
+            kitchen_aspect_ratio = kitchen_width / kitchen_height
+            game_width = int(game_height * kitchen_aspect_ratio)
+            grid_size = int(game_width / self.simulator.env.kitchen_width)
+
+        elif self.visualization_config["GameWindow"]["WhatIsFixed"] == "grid":
+            grid_size = self.visualization_config["GameWindow"]["size"]
+            game_width, game_height = (
+                kitchen_width * grid_size,
+                kitchen_height * grid_size,
+            )
+
+        else:
+            game_width, game_height = 0, 0
+            grid_size = 0
 
         window_width, window_height = (
             game_width + (2 * self.screen_margin),
-            game_height + (2 * self.screen_margin),
+            game_height + (2 * self.screen_margin),  # bar with orders
         )
 
-        return window_width, window_height, game_width, game_height, grid_size
+        window_width = max(window_width, self.min_width)
+        window_height = max(window_height, self.min_height)
+        return (
+            int(window_width),
+            int(window_height),
+            game_width,
+            game_height,
+            grid_size,
+        )
 
     def create_player_colors(self) -> list[Color]:
-        number_player = len(self.player_keys)
+        number_player = len(self.simulator.env.players)
         hue_values = np.linspace(0, 1, number_player + 1)
 
         colors_vec = np.array([col for col in colors.values()])
@@ -238,8 +249,8 @@ class PyGameGUI:
         self.game_screen.fill(
             colors[self.visualization_config["Kitchen"]["ground_tiles_color"]]
         )
-        for x in range(0, int(self.window_width), block_size):
-            for y in range(0, int(self.window_height), block_size):
+        for x in range(0, self.window_width, block_size):
+            for y in range(0, self.window_height, block_size):
                 rect = pygame.Rect(x, y, block_size, block_size)
                 pygame.draw.rect(
                     self.game_screen,
@@ -248,7 +259,9 @@ class PyGameGUI:
                     1,
                 )
 
-    def draw_image(self, img_path, size, pos, rot_angle=0):
+    def draw_image(
+        self, img_path, size, pos, rot_angle=0, screen: pygame.Surface = None
+    ):
         cache_entry = f"{img_path}"
         if cache_entry in self.image_cache_dict.keys():
             image = self.image_cache_dict[cache_entry]
@@ -263,7 +276,11 @@ class PyGameGUI:
             image = pygame.transform.rotate(image, rot_angle)
         rect = image.get_rect()
         rect.center = pos
-        self.game_screen.blit(image, rect)
+
+        if screen is None:
+            self.game_screen.blit(image, rect)
+        else:
+            screen.blit(image, rect)
 
     def draw_players(self, state, state_dict):
         """Visualizes the players as circles with a triangle for the facing direction.
@@ -354,7 +371,11 @@ class PyGameGUI:
                 )
 
     def draw_thing(
-        self, pos: npt.NDArray[float], parts: list[dict[str]], scale: float = 1.0
+        self,
+        pos: npt.NDArray[float],
+        parts: list[dict[str]],
+        scale: float = 1.0,
+        screen: pygame.Surface = None,
     ):
         """Draws an item, based on its visual parts specified in the visualization config.
 
@@ -364,14 +385,22 @@ class PyGameGUI:
             scale: Rescale the item by this factor.
         """
 
+        if screen is None:
+            screen = self.game_screen
+
         for part in parts:
             part_type = part["type"]
             match part_type:
                 case "image":
+                    if "center_offset" in part:
+                        d = np.array(part["center_offset"]) * self.grid_size
+                        pos += d
+
                     self.draw_image(
-                        parts[0]["path"],
-                        parts[0]["size"] * scale * self.grid_size,
+                        part["path"],
+                        part["size"] * scale * self.grid_size,
                         pos,
+                        screen=screen,
                     )
                 case "rect":
                     height = part["height"] * self.grid_size
@@ -380,7 +409,7 @@ class PyGameGUI:
                     if "center_offset" in part:
                         dx, dy = np.array(part["center_offset"]) * self.grid_size
                         rect = pygame.Rect(pos[0] + dx, pos[1] + dy, height, width)
-                        pygame.draw.rect(self.game_screen, color, rect)
+                        pygame.draw.rect(screen, color, rect)
                     else:
                         rect = pygame.Rect(
                             pos[0] - (height / 2),
@@ -388,7 +417,7 @@ class PyGameGUI:
                             height,
                             width,
                         )
-                    pygame.draw.rect(self.game_screen, color, rect)
+                    pygame.draw.rect(screen, color, rect)
                 case "circle":
                     radius = part["radius"] * self.grid_size
                     color = colors[part["color"]]
@@ -400,9 +429,16 @@ class PyGameGUI:
                             radius,
                         )
                     else:
-                        pygame.draw.circle(self.game_screen, color, pos, radius)
+                        pygame.draw.circle(screen, color, pos, radius)
 
-    def draw_item(self, pos: npt.NDArray[float], item: Item, scale: float = 1.0):
+    def draw_item(
+        self,
+        pos: npt.NDArray[float],
+        item: Item,
+        scale: float = 1.0,
+        plate=False,
+        screen=None,
+    ):
         """Visualization of an item at the specified position. On a counter or in the hands of the player.
         The visual composition of the item is read in from visualization.yaml file, where it is specified as
         different parts to be drawn.
@@ -411,41 +447,70 @@ class PyGameGUI:
             pos: The position of the item to draw.
             item: The item do be drawn in the game.
             scale: Rescale the item by this factor.
+            screen: the pygame screen to draw on.
+            plate: item is on a plate (soup are is different on a plate and pot)
         """
 
-        if not isinstance(item, Meal):
+        if not isinstance(item, list):
             if item.name in self.visualization_config:
+                item_key = item.name
+                if "Soup" in item.name and plate:
+                    item_key += "Plate"
                 self.draw_thing(
-                    pos, self.visualization_config[item.name]["parts"], scale=scale
+                    pos,
+                    self.visualization_config[item_key]["parts"],
+                    scale=scale,
+                    screen=screen,
                 )
 
-        if isinstance(item, (ProgressibleItem, Plate)) and not item.finished:
-            self.draw_progress_bar(pos, item.progressed_steps, item.steps_needed)
-
-        if isinstance(item, CookingEquipment) and item.content:
-            self.draw_item(pos, item.content)
+        if isinstance(item, (Item, Plate)) and item.progress_percentage > 0.0:
+            self.draw_progress_bar(pos, item.progress_percentage)
 
-        if isinstance(item, Meal):
-            if item.finished:
-                if item.name in self.visualization_config:
-                    self.draw_thing(pos, self.visualization_config[item.name]["parts"])
+        if isinstance(item, CookingEquipment) and item.content_list:
+            if (
+                item.content_ready
+                and item.content_ready.name in self.visualization_config
+            ):
+                self.draw_thing(
+                    pos,
+                    self.visualization_config[item.content_ready.name]["parts"],
+                    screen=screen,
+                )
             else:
-                for idx, o in enumerate(item.parts):
-                    triangle_offsets = create_polygon(len(item.parts), length=10)
-                    self.draw_item(pos + triangle_offsets[idx], o, scale=0.6)
+                triangle_offsets = create_polygon(len(item.content_list), length=10)
+                scale = 1 if len(item.content_list) == 1 else 0.6
+                for idx, o in enumerate(item.content_list):
+                    self.draw_item(
+                        pos + triangle_offsets[idx],
+                        o,
+                        scale=scale,
+                        plate=isinstance(item, Plate),
+                        screen=screen,
+                    )
 
-    def draw_progress_bar(self, pos, current, needed):
+        # if isinstance(item, Meal):
+        #     if item.finished:
+        #         if item.name in self.visualization_config:
+        #             self.draw_thing(pos, self.visualization_config[item.name]["parts"])
+        #     else:
+        #         for idx, o in enumerate(item.parts):
+        #             triangle_offsets = create_polygon(len(item.parts), length=10)
+        #             self.draw_item(pos + triangle_offsets[idx], o, scale=0.6)
+
+    def draw_progress_bar(self, pos, percent, screen=None):
         """Visualize progress of progressing item as a green bar under the item."""
-        if current != 0:
-            bar_height = self.grid_size * 0.2
-            progress_width = (current / needed) * self.grid_size
-            progress_bar = pygame.Rect(
-                pos[0] - (self.grid_size / 2),
-                pos[1] - (self.grid_size / 2) + self.grid_size - bar_height,
-                progress_width,
-                bar_height,
-            )
+        bar_height = self.grid_size * 0.2
+        progress_width = percent * self.grid_size
+        progress_bar = pygame.Rect(
+            pos[0] - (self.grid_size / 2),
+            pos[1] - (self.grid_size / 2) + self.grid_size - bar_height,
+            progress_width,
+            bar_height,
+        )
+        if screen is None:
             pygame.draw.rect(self.game_screen, colors["green1"], progress_bar)
+        else:
+            pygame.draw.rect(screen, colors["green1"], progress_bar)
 
     def draw_counter(self, counter):
         """Visualization of a counter at its position. If it is occupied by an item, it is also shown.
@@ -457,7 +522,6 @@ class PyGameGUI:
         """
 
         pos = counter.pos * self.grid_size
-
         self.draw_thing(pos, self.visualization_config["Counter"]["parts"])
         if str(counter) in self.visualization_config:
             self.draw_thing(pos, self.visualization_config[str(counter)]["parts"])
@@ -486,11 +550,81 @@ class PyGameGUI:
         for counter in state["counters"]:
             self.draw_counter(counter)
             if SHOW_COUNTER_CENTERS:
-                pygame.draw.circle(
-                    self.game_screen, colors["green1"], counter.pos * self.grid_size, 3
+                pygame.draw.circle(self.game_screen, colors["green1"], counter.pos, 3)
+
+    def update_score_label(self, state):
+        score = state["score"]
+        self.score_label.set_text(f"Score {score}")
+
+    def update_conclusion_label(self, state):
+        score = state["score"]
+        self.conclusion_label.set_text(f"Your final score is {score}. Hurray!")
+
+    def update_remaining_time(self, remaining_time: timedelta):
+        hours, rem = divmod(remaining_time.seconds, 3600)
+        minutes, seconds = divmod(rem, 60)
+        display_time = f"{minutes}:{'%02d' % seconds}"
+        self.timer_label.set_text(f"Time remaining: {display_time}")
+
+    def draw_orders(self, state):
+        orders_width = self.game_width - 100
+        orders_height = self.screen_margin
+
+        order_screen = pygame.Surface(
+            (orders_width, orders_height),
+        )
+
+        bg_color = colors[self.visualization_config["GameWindow"]["background_color"]]
+        pygame.draw.rect(order_screen, bg_color, order_screen.get_rect())
+
+        order_rects_start = (orders_height // 2) - (self.grid_size // 2)
+        with self.simulator.env.lock:
+            for idx, order in enumerate(state["orders"]):
+                order: Order
+                order_upper_left = [
+                    order_rects_start + idx * self.grid_size * 1.2,
+                    order_rects_start,
+                ]
+                pygame.draw.rect(
+                    order_screen,
+                    colors["red"],
+                    pygame.Rect(
+                        order_upper_left[0],
+                        order_upper_left[1],
+                        self.grid_size,
+                        self.grid_size,
+                    ),
+                    width=2,
+                )
+                center = np.array(order_upper_left) + np.array(
+                    [self.grid_size / 2, self.grid_size / 2]
                 )
+                self.draw_thing(
+                    center,
+                    self.visualization_config["Plate"]["parts"],
+                    screen=order_screen,
+                )
+                self.draw_item(
+                    center,
+                    order.meal,
+                    plate=True,
+                    screen=order_screen,
+                )
+                order_done_seconds = (
+                    (order.start_time + order.max_duration) - state["env_time"]
+                ).total_seconds()
+
+                percentage = order_done_seconds / order.max_duration.total_seconds()
+                self.draw_progress_bar(center, percentage, screen=order_screen)
 
-    def draw(self, state, state_dict):
+        orders_rect = order_screen.get_rect()
+        orders_rect.center = [
+            self.screen_margin + (orders_width // 2),
+            orders_height // 2,
+        ]
+        self.main_window.blit(order_screen, orders_rect)
+
+    def draw(self, state):
         """Main visualization function.
 
         Args:
@@ -500,21 +634,24 @@ class PyGameGUI:
         self.draw_background()
 
         self.draw_counters(state)
-        self.draw_players(state, state_dict)
-        self.manager.draw_ui(self.game_screen)
+        self.draw_players(state)
+        self.manager.draw_ui(self.main_window)
+        self.update_remaining_time(state["remaining_time"])
+
+        self.draw_orders(state)
+        self.update_score_label(state)
 
     def init_ui_elements(self):
         self.manager = pygame_gui.UIManager((self.window_width, self.window_height))
         self.manager.get_theme().load_theme(ROOT_DIR / "gui_2d_vis" / "gui_theme.json")
 
-        button_width, button_height = 200, 60
         self.start_button = pygame_gui.elements.UIButton(
             relative_rect=pygame.Rect(
                 (
-                    (self.window_width // 2) - button_width // 2,
-                    (self.window_height / 2) - button_height // 2,
+                    (self.window_width // 2) - self.buttons_width // 2,
+                    (self.window_height / 2) - self.buttons_height // 2,
                 ),
-                (button_width, button_height),
+                (self.buttons_width, self.buttons_height),
             ),
             text="Start Game",
             manager=self.manager,
@@ -524,25 +661,26 @@ class PyGameGUI:
         self.quit_button = pygame_gui.elements.UIButton(
             relative_rect=pygame.Rect(
                 (
-                    (self.window_width - button_width),
+                    (self.window_width - self.buttons_width),
                     0,
                 ),
-                (button_width, button_height),
+                (self.buttons_width, self.buttons_height),
             ),
             text="Quit Game",
             manager=self.manager,
+            object_id="#quit_button",
         )
         self.quit_button.can_hover()
 
         self.finished_button = pygame_gui.elements.UIButton(
             relative_rect=pygame.Rect(
                 (
-                    (self.window_width - button_width),
-                    (self.window_height - button_height),
+                    (self.window_width - self.buttons_width),
+                    (self.window_height - self.buttons_height),
                 ),
-                (button_width, button_height),
+                (self.buttons_width, self.buttons_height),
             ),
-            text="End screen",
+            text="Finish round",
             manager=self.manager,
         )
         self.finished_button.can_hover()
@@ -551,26 +689,24 @@ class PyGameGUI:
             relative_rect=pygame.Rect(
                 (
                     (0),
-                    (self.window_height - button_height),
+                    (self.window_height - self.buttons_height),
                 ),
-                (button_width, button_height),
+                (self.buttons_width, self.buttons_height),
             ),
-            text="Back to Start",
+            text="Back to menu",
             manager=self.manager,
         )
         self.back_button.can_hover()
 
-        self.score_rect = pygame.Rect(
-            (
-                (self.window_width // 2) - button_width // 2,
-                (self.window_height / 2) - button_height // 2,
-            ),
-            (button_width, button_height),
-        )
-
         self.score_label = pygame_gui.elements.UILabel(
-            text=f"Your score: _",
-            relative_rect=self.score_rect,
+            text=f"Score: _",
+            relative_rect=pygame.Rect(
+                (
+                    (0),
+                    self.window_height - self.screen_margin,
+                ),
+                (self.screen_margin * 2, self.screen_margin),
+            ),
             manager=self.manager,
             object_id="#score_label",
         )
@@ -593,30 +729,51 @@ class PyGameGUI:
             options_list=layout_file_paths,
             starting_option=layout_file_paths[-1],
         )
+        self.timer_label = pygame_gui.elements.UILabel(
+            text="GAMETIME",
+            relative_rect=pygame.Rect(
+                (self.screen_margin, self.window_height - self.screen_margin),
+                (self.game_width, self.screen_margin),
+            ),
+            manager=self.manager,
+            object_id="#timer_label",
+        )
 
-    def setup_windows(self):
-        (
-            self.window_width,
-            self.window_height,
-            self.game_width,
-            self.game_height,
-            self.grid_size,
-        ) = self.init_window_sizes()
+        self.orders_label = pygame_gui.elements.UILabel(
+            text="Orders:",
+            relative_rect=pygame.Rect(0, 0, self.screen_margin, self.screen_margin),
+            manager=self.manager,
+            object_id="#orders_label",
+        )
 
+        self.conclusion_label = pygame_gui.elements.UILabel(
+            text="Your final score was _",
+            relative_rect=pygame.Rect(0, 0, self.window_width, self.window_height),
+            manager=self.manager,
+            object_id="#score_label",
+        )
+
+    def set_window_size(self, window_width, window_height, game_width, game_height):
         self.game_screen = pygame.Surface(
             (
-                self.game_width,
-                self.game_height,
+                game_width,
+                game_height,
             ),
         )
-
         self.main_window = pygame.display.set_mode(
             (
-                self.window_width,
-                self.window_height,
+                window_width,
+                window_height,
             )
         )
-        self.player_colors = self.create_player_colors()
+
+    def reset_window_size(self):
+        self.window_width = self.min_width
+        self.window_height = self.min_height
+        self.game_width = 0
+        self.game_height = 0
+        self.set_window_size(self.min_width, self.min_height, 0, 0)
+        self.init_ui_elements()
 
     def setup_simulation(self, config_path, layout_path):
         self.simulator = Simulator(config_path, layout_path, 600)
@@ -625,6 +782,14 @@ class PyGameGUI:
             player_name = f"p{i}"
             self.simulator.register_player(player_name)
         self.simulator.start()
+        (
+            self.window_width,
+            self.window_height,
+            self.game_width,
+            self.game_height,
+            self.grid_size,
+        ) = self.get_window_sizes(self.simulator.get_state())
+        self.player_colors = self.create_player_colors()
 
     def manage_button_visibility(self):
         match self.menu_state:
@@ -635,21 +800,28 @@ class PyGameGUI:
                 self.score_label.hide()
                 self.finished_button.hide()
                 self.layout_selection.show()
+                self.timer_label.hide()
+                self.orders_label.hide()
+                self.conclusion_label.hide()
             case MenuStates.Game:
                 self.start_button.hide()
-                self.back_button.show()
-                self.score_label.hide()
+                self.back_button.hide()
+                self.score_label.show()
+                self.score_label.show()
                 self.finished_button.show()
                 self.layout_selection.hide()
+                self.timer_label.show()
+                self.orders_label.show()
+                self.conclusion_label.hide()
             case MenuStates.End:
                 self.start_button.hide()
                 self.back_button.show()
-                self.score_label.show()
-                self.score_label.set_text(
-                    f"Your Score is {self.simulator.env.game_score.score}"
-                )
+                self.score_label.hide()
                 self.finished_button.hide()
                 self.layout_selection.hide()
+                self.timer_label.hide()
+                self.orders_label.hide()
+                self.conclusion_label.show()
 
     def start_button_press(self):
         self.menu_state = MenuStates.Game
@@ -663,15 +835,18 @@ class PyGameGUI:
         config_path = ROOT_DIR / "game_content" / "environment_config.yaml"
 
         self.setup_simulation(config_path, layout_path)
-        self.setup_windows()
+
+        self.set_window_size(*(self.get_window_sizes(self.simulator.get_state()))[:-1])
+
         self.init_ui_elements()
         log.debug("Pressed start button")
 
         # self.api.set_sim(self.simulator)
 
     def back_button_press(self):
-        self.simulator.stop()
         self.menu_state = MenuStates.Start
+        self.reset_window_size()
+        self.simulator.stop()
         log.debug("Pressed back button")
 
     def quit_button_press(self):
@@ -680,8 +855,9 @@ class PyGameGUI:
         log.debug("Pressed quit button")
 
     def finished_button_press(self):
-        self.simulator.stop()
         self.menu_state = MenuStates.End
+        self.reset_window_size()
+        self.simulator.stop()
         log.debug("Pressed finished button")
 
     def send_action(self, action: Action):
@@ -718,13 +894,12 @@ class PyGameGUI:
         pygame.init()
         pygame.font.init()
 
-        self.setup_windows()
-        self.init_ui_elements()
-
         pygame.display.set_caption("Simple Overcooked Simulator")
 
         clock = pygame.time.Clock()
 
+        self.reset_window_size()
+        self.init_ui_elements()
         self.manage_button_visibility()
 
         # Game loop
@@ -736,6 +911,8 @@ class PyGameGUI:
                 for event in pygame.event.get():
                     if event.type == pygame.QUIT:
                         self.running = False
+
+                    # UI Buttons:
                     if event.type == pygame_gui.UI_BUTTON_PRESSED:
                         match event.ui_element:
                             case self.start_button:
@@ -759,12 +936,17 @@ class PyGameGUI:
 
                 # drawing:
 
-                self.main_window.fill(colors["lemonchiffon1"])
+                state = self.simulator.get_state()
+
+                self.main_window.fill(
+                    colors[self.visualization_config["GameWindow"]["background_color"]]
+                )
                 self.manager.draw_ui(self.main_window)
 
                 match self.menu_state:
                     case MenuStates.Start:
                         pass
+
                     case MenuStates.Game:
                         self.draw_background()
 
@@ -774,18 +956,23 @@ class PyGameGUI:
                         state = self.simulator.get_state()
                         self.draw(state, state_dict)
 
-                        game_screen_rect = self.game_screen.get_rect()
-                        game_screen_rect.center = [
-                            self.window_width // 2,
-                            self.window_height // 2,
-                        ]
+                        if state["ended"]:
+                            self.finished_button_press()
+                            self.manage_button_visibility()
+                        else:
+                            self.draw(state)
 
-                        self.main_window.blit(self.game_screen, game_screen_rect)
+                            game_screen_rect = self.game_screen.get_rect()
+                            game_screen_rect.center = [
+                                self.window_width // 2,
+                                self.window_height // 2,
+                            ]
+
+                            self.main_window.blit(self.game_screen, game_screen_rect)
 
                     case MenuStates.End:
-                        pygame.draw.rect(
-                            self.game_screen, colors["cornsilk1"], self.score_rect
-                        )
+                        self.update_conclusion_label(state)
+
                 self.manager.update(time_delta)
                 pygame.display.flip()
 
diff --git a/overcooked_simulator/gui_2d_vis/visualization.yaml b/overcooked_simulator/gui_2d_vis/visualization.yaml
index 021761424694300f516cd77cfd63e907ac60fa23..8d4d52deb8f111a56dda65c44501aca3414fe979 100644
--- a/overcooked_simulator/gui_2d_vis/visualization.yaml
+++ b/overcooked_simulator/gui_2d_vis/visualization.yaml
@@ -1,11 +1,18 @@
 # colors: https://www.webucator.com/article/python-color-constants-module/
 
 GameWindow:
-  WhatIsFixed: window_width  # entweder grid oder window_width
-  size: 600
+  WhatIsFixed: grid  # grid or window_width or window_height
+  size: 50
   screen_margin: 100
-  start_width: 600
-  start_height: 600
+  min_width: 700
+  min_height: 600
+  buttons_width: 180
+  buttons_height: 60
+
+  order_bar_height: 100
+  order_size: 50
+
+  background_color: lemonchiffon1
 
 Kitchen:
   ground_tiles_color: sgigray76
@@ -20,15 +27,10 @@ Counter:
 
 CuttingBoard:
   parts:
-    - type: rect
-      height: 0.75
-      width: 0.75
-      color: burlywood1
-    - type: rect
-      height: 0.125
-      width: 0.5
-      center_offset: [ +0.15, -0.2 ]
-      color: silver
+    - type: image
+      path: images/cutting_board_large.png
+      size: 0.9
+
 
 PlateDispenser:
   parts:
@@ -39,12 +41,10 @@ PlateDispenser:
 
 Trash:
   parts:
-    - type: circle
-      radius: 0.4
-      color: black
-    - type: circle
-      radius: 0.375
-      color: gray33
+    - type: image
+      path: images/trash3.png
+      size: 0.9
+      center_offset: [ 0, 0 ]
 
 TomatoDispenser:
   parts:
@@ -84,10 +84,10 @@ BunDispenser:
 
 ServingWindow:
   parts:
-    - color: darkgoldenrod1
-      type: rect
-      height: 0.85
-      width: 0.85
+    - type: image
+      path: images/arrow_right.png
+      size: 1
+      center_offset: [ 0, 0 ]
 
 Stove:
   parts:
@@ -101,13 +101,9 @@ Stove:
 
 Sink:
   parts:
-    - color: black
-      type: rect
-      height: 0.875
-      width: 0.625
-    - color: darkslategray1
-      type: circle
-      radius: 0.45
+    - type: image
+      path: images/sink_large.png
+      size: 0.9
 
 SinkAddon:
   parts:
@@ -134,139 +130,99 @@ SinkAddon:
 Tomato:
   parts:
     - type: image
-      path: images/tomato.png
+      path: images/tomato3_smaller.png
       size: 1
 
 Onion:
   parts:
-    - type: circle
-      radius: 0.3
-      color: black
-    - type: circle
-      radius: 0.25
-      color: deeppink4
+    - type: image
+      path: images/onion_large.png
+      size: 0.8
 
 Bun:
   parts:
-    - type: circle
-      radius: 0.3
-      color: black
-    - type: circle
-      radius: 0.25
-      color: navajowhite2
+    - type: image
+      path: images/bun.png
+      size: 0.9
 
 Lettuce:
   parts:
-    - type: circle
-      radius: 0.3
-      color: black
-    - type: circle
-      radius: 0.25
-      color: emeraldgreen
+    - type: image
+      path: images/lettuce_smaller.png
+      size: 0.8
 
 Meat:
   parts:
-    - type: circle
-      radius: 0.3
-      color: black
-    - type: circle
-      radius: 0.25
-      color: hotpink
-
+    - type: image
+      path: images/meat.png
+      size: 1
 
 ChoppedLettuce:
   parts:
-    - type: circle
-      radius: 0.3
-      color: black
-      center_offset: [ -0.125, 0 ]
-    - type: circle
-      radius: 0.25
-      color: emeraldgreen
-      center_offset: [ -0.125, 0 ]
-    - type: circle
-      radius: 0.3
-      color: black
-    - type: circle
-      radius: 0.25
-      color: emeraldgreen
-    - type: circle
-      radius: 0.3
-      color: black
-      center_offset: [ 0.125, 0 ]
-    - type: circle
-      radius: 0.25
-      color: emeraldgreen
-      center_offset: [ 0.125, 0 ]
+    - type: image
+      path: images/lettuce_cut_smaller.png
+      size: 0.8
 
 ChoppedTomato:
   parts:
     - type: image
-      path: images/tomato_cut.png
+      path: images/tomato3_cut_smaller.png
       size: 1
 
 ChoppedOnion:
   parts:
-    - type: circle
-      radius: 0.3
-      color: black
-      center_offset: [ -0.125, 0 ]
-    - type: circle
-      radius: 0.25
-      color: deeppink4
-      center_offset: [ -0.125, 0 ]
-    - type: circle
-      radius: 0.3
-      color: black
-    - type: circle
-      radius: 0.25
-      color: deeppink4
-    - type: circle
-      radius: 0.3
-      color: black
-      center_offset: [ 0.125, 0 ]
-    - type: circle
-      radius: 0.25
-      color: deeppink4
-      center_offset: [ 0.125, 0 ]
+    - type: image
+      path: images/onion_cut.png
+      size: 0.95
 
 ChoppedMeat:
   parts:
-    - type: circle
-      radius: 0.3
-      color: black
-      center_offset: [ -0.125, 0 ]
-    - type: circle
-      radius: 0.25
-      color: indianred1
-      center_offset: [ -0.125, 0 ]
-    - type: circle
-      radius: 0.3
-      color: black
-    - type: circle
-      radius: 0.25
-      color: indianred1
+    - type: image
+      path: images/raw_patty.png
+      size: 0.9
 
 CookedPatty:
   parts:
-    - type: circle
-      radius: 0.3
-      color: black
-    - type: circle
-      radius: 0.25
-      color: salmon4
+    - type: image
+      path: images/cooked_patty.png
+      size: 0.9
 
+Burger:
+  parts:
+    - type: image
+      path: images/burger.png
+      size: 0.8
+
+Salad:
+  parts:
+    - type: image
+      path: images/salad.png
+      size: 0.8
 
 TomatoSoup:
   parts:
     - type: image
-      path: images/tomato_soup.png
+      path: images/tomato_soup_pot.png
+      size: 1.05
+      center_offset: [ 0, 0 ]
+
+TomatoSoupPlate:
+  parts:
+    - type: image
+      path: images/tomato_soup_plate.png
       size: 0.6
 
 OnionSoup:
   parts:
     - type: image
-      path: images/tomato_soup.png
+      path: images/onion_soup_pot.png
+      size: 1.05
+      center_offset: [ 0, 0 ]
+
+OnionSoupPlate:
+  parts:
+    - type: image
+      path: images/onion_soup_plate.png
       size: 0.6
 
 Cook:
@@ -275,7 +231,7 @@ Cook:
       path: images/pixel_cook.png
       size: 1
 
-CleanPlate:
+Plate:
   parts:
     - type: image
       path: images/plate_clean.png
@@ -290,19 +246,12 @@ DirtyPlate:
 Pot:
   parts:
     - type: image
-      path: images/pot.png
-      size: 0.8
+      path: images/pot_smaller.png
+      size: 1.05
+      center_offset: [ -0.02, -0.1 ]
 
 Pan:
   parts:
-    - type: circle
-      radius: 0.4
-      color: black
-    - type: circle
-      radius: 0.35
-      color: sgigray16
-    - color: orange4
-      type: rect
-      height: 0.5
-      width: 0.1
-      center_offset: [ -0.8, -0.05 ]
\ No newline at end of file
+    - type: image
+      path: images/pan.png
+      size: 1.1
\ No newline at end of file
diff --git a/overcooked_simulator/main.py b/overcooked_simulator/main.py
index 81a5d8f029c82707cb5720a6a8d08c8b16309d8c..dc790c40c467caacdff47c5b2d26a65a4b59dd09 100644
--- a/overcooked_simulator/main.py
+++ b/overcooked_simulator/main.py
@@ -27,6 +27,7 @@ def setup_logging():
             logging.StreamHandler(sys.stdout),
         ],
     )
+    logging.getLogger("matplotlib").setLevel(logging.WARNING)
 
 
 def main():
diff --git a/overcooked_simulator/order.py b/overcooked_simulator/order.py
new file mode 100644
index 0000000000000000000000000000000000000000..5d7abbf72cdbbd1a6d2fe110934b8561d342ca44
--- /dev/null
+++ b/overcooked_simulator/order.py
@@ -0,0 +1,353 @@
+import dataclasses
+import logging
+import random
+from abc import abstractmethod
+from collections import deque
+from datetime import datetime, timedelta
+from typing import Callable, Tuple, Any, Deque
+
+from overcooked_simulator.game_items import Item, Plate, ItemInfo
+
+log = logging.getLogger(__name__)
+
+
+@dataclasses.dataclass
+class Order:
+    meal: ItemInfo
+    start_time: datetime
+    max_duration: timedelta
+    score_calc: Callable[[timedelta, ...], float]
+    timed_penalties: list[
+        Tuple[timedelta, float] | Tuple[timedelta, float, int, timedelta]
+    ]
+    expired_penalty: float
+
+    finished_info: dict[str, Any] = dataclasses.field(default_factory=dict)
+    _timed_penalties: list[Tuple[datetime, float]] = dataclasses.field(
+        default_factory=list
+    )
+
+    def order_time(self, env_time: datetime) -> timedelta:
+        return self.start_time - env_time
+
+    def create_penalties(self, env_time: datetime):
+        for penalty_info in self.timed_penalties:
+            match penalty_info:
+                case (offset, penalty):
+                    self._timed_penalties.append((env_time + offset, penalty))
+                case (duration, penalty, number_repeat, offset):
+                    self._timed_penalties.extend(
+                        [
+                            (env_time + offset + (duration * i), penalty)
+                            for i in range(number_repeat)
+                        ]
+                    )
+
+
+class OrderGeneration:
+    def __init__(self, available_meals: dict[str, ItemInfo], **kwargs):
+        self.available_meals: list[ItemInfo] = list(available_meals.values())
+
+    @abstractmethod
+    def init_orders(self, now) -> list[Order]:
+        ...
+
+    @abstractmethod
+    def get_orders(
+        self,
+        passed_time: timedelta,
+        now: datetime,
+        new_finished_orders: list[Order],
+        expired_orders: list[Order],
+    ) -> list[Order]:
+        ...
+
+
+def zero(item: ItemInfo, **kwargs) -> float:
+    return 0.0
+
+
+@dataclasses.dataclass
+class RandomOrderKwarg:
+    num_start_meals: int
+    sample_on_serving: bool
+    sample_on_dur: bool
+    sample_on_dur_func: dict
+    max_orders: int
+    duration_sample: dict
+    score_calc_gen_func: Callable[
+        [ItemInfo, timedelta, datetime, Any], Callable[[timedelta, Order], float]
+    ]
+    score_calc_gen_kwargs: dict
+    expired_penalty_func: Callable[[ItemInfo], float] = zero
+    expired_penalty_kwargs: dict = dataclasses.field(default_factory=dict)
+
+
+class RandomOrderGeneration(OrderGeneration):
+    def __init__(self, available_meals: dict[str, ItemInfo], **kwargs):
+        super().__init__(available_meals, **kwargs)
+        self.kwargs: RandomOrderKwarg = RandomOrderKwarg(**kwargs["kwargs"])
+        self.next_order_time: datetime | None = datetime.max
+        self.number_cur_orders = 0
+        self.needed_orders: int = 0
+        """For the sample on dur but when it was restricted due to max order number."""
+
+    def init_orders(self, now) -> list[Order]:
+        self.number_cur_orders = self.kwargs.num_start_meals
+        if self.kwargs.sample_on_dur:
+            self.create_random_next_time_delta(now)
+        return self.create_orders_for_meals(
+            random.choices(self.available_meals, k=self.kwargs.num_start_meals),
+            now,
+            self.kwargs.sample_on_serving,
+        )
+
+    def get_orders(
+        self,
+        passed_time: timedelta,
+        now: datetime,
+        new_finished_orders: list[Order],
+        expired_orders: list[Order],
+    ) -> list[Order]:
+        self.number_cur_orders -= len(new_finished_orders)
+        self.number_cur_orders -= len(expired_orders)
+        if self.kwargs.sample_on_serving:
+            if new_finished_orders:
+                self.create_random_next_time_delta(now)
+                return []
+        if self.needed_orders:
+            self.needed_orders -= len(new_finished_orders)
+            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)),
+                now,
+            )
+        if self.next_order_time <= now:
+            if self.number_cur_orders >= self.kwargs.max_orders:
+                self.needed_orders += 1
+            else:
+                if self.kwargs.sample_on_dur:
+                    self.create_random_next_time_delta(now)
+                else:
+                    self.next_order_time = datetime.max
+                self.number_cur_orders += 1
+                return self.create_orders_for_meals(
+                    [random.choice(self.available_meals)],
+                    now,
+                )
+        return []
+
+    def create_orders_for_meals(
+        self, meals: list[ItemInfo], now: datetime, no_time_limit: bool = False
+    ) -> list[Order]:
+        orders = []
+        for meal in meals:
+            if no_time_limit:
+                duration = datetime.max - now
+            else:
+                duration = timedelta(
+                    seconds=getattr(random, self.kwargs.duration_sample["func"])(
+                        **self.kwargs.duration_sample["kwargs"]
+                    )
+                )
+            log.info(f"Create order for meal {meal} with duration {duration}")
+            orders.append(
+                Order(
+                    meal=meal,
+                    start_time=now,
+                    max_duration=duration,
+                    score_calc=self.kwargs.score_calc_gen_func(
+                        meal=meal,
+                        duration=duration,
+                        now=now,
+                        kwargs=self.kwargs.score_calc_gen_kwargs,
+                    ),
+                    timed_penalties=[],
+                    expired_penalty=self.kwargs.expired_penalty_func(
+                        meal, **self.kwargs.expired_penalty_kwargs
+                    ),
+                )
+            )
+
+        return orders
+
+    def create_random_next_time_delta(self, now: datetime):
+        self.next_order_time = now + timedelta(
+            seconds=getattr(random, self.kwargs.sample_on_dur_func["func"])(
+                **self.kwargs.sample_on_dur_func["kwargs"]
+            )
+        )
+        log.info(f"Next order in {self.next_order_time}")
+
+
+def simple_score_calc_gen_func(
+    meal: Item, duration: timedelta, now: datetime, kwargs: dict, **other_kwargs
+) -> Callable:
+    scores = kwargs["scores"]
+    other = kwargs["other"]
+
+    def score_calc(relative_order_time: timedelta, order: Order) -> float:
+        if order.meal.name in scores:
+            return scores[order.meal.name]
+        return other
+
+    return score_calc
+
+
+def simple_expired_penalty(item: ItemInfo, default: float, **kwargs) -> float:
+    return default
+
+
+class OrderAndScoreManager:
+    def __init__(self, order_config, available_meals: dict[str, ItemInfo]):
+        self.score = 0
+        self.order_gen: OrderGeneration = order_config["order_gen_class"](
+            available_meals=available_meals, kwargs=order_config["kwargs"]
+        )
+        self.kwargs_for_func = order_config["kwargs"]
+        self.serving_not_ordered_meals = order_config["serving_not_ordered_meals"]
+        self.available_meals = available_meals
+        self.open_orders: Deque[Order] = deque()
+
+        # for logs or history in the future
+        # TODO log who / which player served which meal -> for split scores
+        self.served_meals: list[Tuple[Item, datetime]] = []
+        self.last_finished = []
+        self.next_relevant_time = datetime.max
+        self.last_expired = []
+
+    def update_next_relevant_time(self):
+        next_relevant_time = datetime.max
+        for order in self.open_orders:
+            next_relevant_time = min(
+                next_relevant_time, order.start_time + order.max_duration
+            )
+            for penalty in order._timed_penalties:
+                next_relevant_time = min(next_relevant_time, penalty[0])
+        self.next_relevant_time = next_relevant_time
+
+    def serve_meal(self, item: Item, env_time: datetime) -> bool:
+        if isinstance(item, Plate):
+            meal = item.get_potential_meal()
+            if meal is not None:
+                if meal.name in self.available_meals:
+                    order = self.find_order_for_meal(meal)
+                    if order is None:
+                        if self.serving_not_ordered_meals:
+                            accept, score = self.serving_not_ordered_meals(meal)
+                            if accept:
+                                log.info(
+                                    f"Serving meal without order {meal.name} with score {score}"
+                                )
+                                self.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"
+                        )
+                        return False
+                    order, index = order
+                    score = order.score_calc(
+                        relative_order_time=env_time - order.start_time,
+                        order=order,
+                    )
+                    self.score += score
+                    order.finished_info = {
+                        "end_time": env_time,
+                        "score": score,
+                    }
+                    log.info(f"Serving meal {meal.name} with order with score {score}")
+                    self.last_finished.append(order)
+                    del self.open_orders[index]
+                    self.served_meals.append((meal, env_time))
+                    return True
+        log.info(f"Do not serve item {item}")
+        return False
+
+    def increment_score(self, score: int):
+        self.score += score
+        log.debug(f"Score: {self.score}")
+
+    def create_init_orders(self, env_time):
+        init_orders = self.order_gen.init_orders(env_time)
+        self.open_orders.extend(init_orders)
+
+    def progress(self, passed_time: timedelta, now: datetime):
+        new_orders = self.order_gen.get_orders(
+            passed_time=passed_time,
+            now=now,
+            new_finished_orders=self.last_finished,
+            expired_orders=self.last_expired,
+        )
+        self.open_orders.extend(new_orders)
+        self.last_finished = []
+        self.last_expired = []
+        if new_orders or self.next_relevant_time <= now:
+            remove_orders = []
+            for index, order in enumerate(self.open_orders):
+                if now >= order.start_time + order.max_duration:
+                    self.score += order.expired_penalty
+                    remove_orders.append(index)
+                remove_penalties = []
+                for i, (penalty_time, penalty) in enumerate(order.timed_penalties):
+                    if penalty_time < now:
+                        self.score -= penalty
+                        remove_penalties.append(i)
+
+                for i in reversed(remove_penalties):
+                    # or del order.timed_penalties[index]
+                    order.timed_penalties.pop(i)
+            expired_orders = []
+            for remove_order in reversed(remove_orders):
+                expired_orders.append(self.open_orders[remove_order])
+                del self.open_orders[remove_order]
+            self.last_expired = expired_orders
+
+            self.update_next_relevant_time()
+
+    def find_order_for_meal(self, meal) -> Tuple[Order, int] | None:
+        for index, order in enumerate(self.open_orders):
+            if order.meal.name == meal.name:
+                return order, index
+
+    def setup_penalties(self, new_orders: list[Order], env_time: datetime):
+        for order in new_orders:
+            order.create_penalties(env_time)
+
+
+if __name__ == "__main__":
+    import yaml
+
+    order_config = yaml.safe_load(
+        """orders:
+  kwargs:
+    duration_sample:
+      func: uniform
+      kwargs:
+        a: 30
+        b: 50
+    max_orders: 5
+    num_start_meals: 3
+    sample_on_dur: false
+    sample_on_dur_func:
+      func: uniform
+      kwargs:
+        a: 30
+        b: 50
+    sample_on_serving: true
+    score_calc_gen_func: null
+    score_calc_gen_kwargs:
+      other: 0
+      scores:
+        Burger: 15
+        OnionSoup: 10
+        Salad: 5
+        TomatoSoup: 10
+    score_calc_gen_func: ~''
+  order_gen_class: ~
+  serving_not_ordered_meals: null"""
+    )
+    order_config["orders"]["order_gen_class"] = RandomOrderGeneration
+    order_config["orders"]["kwargs"]["score_calc_gen_func"] = simple_score_calc_gen_func
+    print(yaml.dump(order_config))
diff --git a/overcooked_simulator/overcooked_environment.py b/overcooked_simulator/overcooked_environment.py
index c3bf550256bc7f40abbd14bf6362a05c3865cc33..eff35464f09aff983d3e6bbf56e2b0ea506d54de 100644
--- a/overcooked_simulator/overcooked_environment.py
+++ b/overcooked_simulator/overcooked_environment.py
@@ -23,25 +23,18 @@ from overcooked_simulator.counters import (
     PlateDispenser,
     SinkAddon,
 )
-from overcooked_simulator.game_items import ItemInfo, ItemType
+from overcooked_simulator.game_items import (
+    ItemInfo,
+    ItemType,
+    CookingEquipment,
+)
+from overcooked_simulator.order import OrderAndScoreManager
 from overcooked_simulator.player import Player
 from overcooked_simulator.utils import create_init_env_time
 
 log = logging.getLogger(__name__)
 
 
-class GameScore:
-    def __init__(self):
-        self.score = 0
-
-    def increment_score(self, score: int):
-        self.score += score
-        log.debug(f"Score: {self.score}")
-
-    def read_score(self):
-        return self.score
-
-
 class Action:
     """Action class, specifies player, action type and action itself."""
 
@@ -59,18 +52,6 @@ class Action:
         return f"Action({self.player},{self.act_type},{self.action})"
 
 
-class GameScore:
-    def __init__(self):
-        self.score = 0
-
-    def increment_score(self, score: int):
-        self.score += score
-        log.debug(f"Score: {self.score}")
-
-    def read_score(self):
-        return self.score
-
-
 class Environment:
     """Environment class which handles the game logic for the overcooked-inspired environment.
 
@@ -83,41 +64,115 @@ class Environment:
         self.players: dict[str, Player] = {}
 
         with open(env_config_path, "r") as file:
-            environment_config = yaml.safe_load(file)
+            self.environment_config = yaml.load(file, Loader=yaml.Loader)
         self.layout_path: Path = layout_path
         # self.counter_side_length = 1  # -> this changed! is 1 now
 
         self.item_info_path: Path = item_info_path
         self.item_info = self.load_item_info()
-
-        self.game_score = GameScore()
+        self.validate_item_info()
+        if self.environment_config["meals"]["all"]:
+            self.allowed_meal_names = set(
+                [
+                    item
+                    for item, info in self.item_info.items()
+                    if info.type == ItemType.Meal
+                ]
+            )
+        else:
+            self.allowed_meal_names = set(self.environment_config["meals"]["list"])
+
+        self.order_and_score = OrderAndScoreManager(
+            order_config=self.environment_config["orders"],
+            available_meals={
+                item: info
+                for item, info in self.item_info.items()
+                if info.type == ItemType.Meal and item in self.allowed_meal_names
+            },
+        )
+        plate_transitions = {
+            item: {
+                "seconds": info.seconds,
+                "needs": info.needs,
+                "info": info,
+            }
+            for item, info in self.item_info.items()
+            if info.type == ItemType.Meal
+        }
 
         self.SYMBOL_TO_CHARACTER_MAP = {
             "#": Counter,
-            "C": CuttingBoard,
+            "C": lambda pos: CuttingBoard(
+                pos,
+                {
+                    info.needs[0]: {"seconds": info.seconds, "result": item}
+                    for item, info in self.item_info.items()
+                    if info.equipment is not None
+                    and info.equipment.name == "CuttingBoard"
+                },
+            ),
             "X": Trash,
-            "W": lambda pos: ServingWindow(pos, self.game_score),
+            "W": lambda pos: ServingWindow(
+                pos,
+                self.order_and_score,
+                meals=self.allowed_meal_names,
+                env_time_func=self.get_env_time,
+            ),
             "T": lambda pos: Dispenser(pos, self.item_info["Tomato"]),
             "L": lambda pos: Dispenser(pos, self.item_info["Lettuce"]),
             "P": lambda pos: PlateDispenser(
-                pos,
-                self.item_info["Plate"],
-                environment_config["plates"] if "plates" in environment_config else {},
+                plate_transitions=plate_transitions,
+                pos=pos,
+                dispensing=self.item_info["Plate"],
+                plate_config=self.environment_config["plates"]
+                if "plates" in self.environment_config
+                else {},
             ),
             "N": lambda pos: Dispenser(pos, self.item_info["Onion"]),  # N for oNioN
             "_": "Free",
             "A": "Agent",
             "U": lambda pos: Stove(
                 pos,
-                self.item_info["Pot"].create_item(),
+                occupied_by=CookingEquipment(
+                    name="Pot",
+                    item_info=self.item_info["Pot"],
+                    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 == "Pot"
+                    },
+                ),
             ),  # Stove with pot: U because it looks like a pot
             "Q": lambda pos: Stove(
                 pos,
-                self.item_info["Pan"].create_item(),
-            ),  # Stove with pot: U because it looks like a pot
+                occupied_by=CookingEquipment(
+                    name="Pan",
+                    item_info=self.item_info["Pan"],
+                    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 == "Pan"
+                    },
+                ),
+            ),  # 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(pos),
+            "S": lambda pos: Sink(
+                pos,
+                transitions={
+                    info.needs[0]: {"seconds": info.seconds, "result": item}
+                    for item, info in self.item_info.items()
+                    if info.equipment is not None and info.equipment.name == "Sink"
+                },
+            ),
             "+": SinkAddon,
         }
 
@@ -132,9 +187,20 @@ class Environment:
 
         self.init_counters()
 
-        self.score: int = 0
-
         self.env_time = create_init_env_time()
+        self.order_and_score.create_init_orders(self.env_time)
+        self.beginning_time = self.env_time
+        self.env_time_end = self.env_time + timedelta(
+            seconds=self.environment_config["game"]["time_limit_seconds"]
+        )
+        log.debug(f"End time: {self.env_time_end}")
+
+    def get_env_time(self):
+        return self.env_time
+
+    @property
+    def game_ended(self) -> bool:
+        return self.env_time >= self.env_time_end
 
     def load_item_info(self) -> dict[str, ItemInfo]:
         with open(self.item_info_path, "r") as file:
@@ -152,6 +218,62 @@ class Environment:
                 item_info.sort_start_meals()
         return item_lookup
 
+    def validate_item_info(self):
+        pass
+        # infos = {t: [] for t in ItemType}
+        # graph = nx.DiGraph()
+        # for info in self.item_info.values():
+        #     infos[info.type].append(info)
+        #     graph.add_node(info.name)
+        #     match info.type:
+        #         case ItemType.Ingredient:
+        #             if info.is_cuttable:
+        #                 graph.add_edge(
+        #                     info.name, info.finished_progress_name[:-1] + info.name
+        #                 )
+        #         case ItemType.Equipment:
+        #             ...
+        #         case ItemType.Meal:
+        #             if info.equipment is not None:
+        #                 graph.add_edge(info.equipment.name, info.name)
+        #             for ingredient in info.needs:
+        #                 graph.add_edge(ingredient, info.name)
+
+        # graph = nx.DiGraph()
+        # for item_name, item_info in self.item_info.items():
+        #     graph.add_node(item_name, type=item_info.type.name)
+        #     if len(item_info.equipment) == 0:
+        #         for item in item_info.needs:
+        #             graph.add_edge(item, item_name)
+        #     else:
+        #         for item in item_info.needs:
+        #             for equipment in item_info.equipment:
+        #                 graph.add_edge(item, equipment)
+        #                 graph.add_edge(equipment, item_name)
+
+        # plt.figure(figsize=(10, 10))
+        # pos = nx.nx_agraph.graphviz_layout(graph, prog="twopi", args="")
+        # nx.draw(graph, pos=pos, with_labels=True, node_color="white", node_size=500)
+        # print(nx.multipartite_layout(graph, subset_key="type", align="vertical"))
+
+        # pos = {
+        #     node: (
+        #         len(nx.ancestors(graph, node)) - len(nx.descendants(graph, node)),
+        #         y,
+        #     )
+        #     for y, node in enumerate(graph)
+        # }
+        # nx.draw(
+        #     graph,
+        #     pos=pos,
+        #     with_labels=True,
+        #     node_shape="s",
+        #     node_size=500,
+        #     node_color="white",
+        # )
+        # TODO add colors for ingredients, equipment and meals
+        # plt.show()
+
     def parse_layout_file(self, layout_file: Path):
         """Creates layout of kitchen counters in the environment based on layout file.
         Counters are arranged in a fixed size grid starting at [0,0]. The center of the first counter is at
@@ -207,18 +329,19 @@ class Environment:
         Args:
             action: The action to be performed
         """
-
         assert action.player in self.players.keys(), "Unknown player."
         player = self.players[action.player]
 
         if action.act_type == "movement":
-            self.perform_movement(player, action.action)
+            with self.lock:
+                self.perform_movement(player, action.action)
 
         else:
             counter = self.get_facing_counter(player)
             if player.can_reach(counter):
                 if action.act_type == "pickup":
-                    player.pick_action(counter)
+                    with self.lock:
+                        player.pick_action(counter)
 
                 elif action.act_type == "interact":
                     if action.action == "keydown":
@@ -226,7 +349,10 @@ class Environment:
                         player.last_interacted_counter = counter
             if action.action == "keyup":
                 if player.last_interacted_counter:
-                    player.perform_interact_hold_stop(player.last_interacted_counter)
+                    with self.lock:
+                        player.perform_interact_hold_stop(
+                            player.last_interacted_counter
+                        )
 
     def get_closest_counter(self, point: np.ndarray):
         """Determines the closest counter for a given 2d-coordinate point in the env.
@@ -405,7 +531,9 @@ class Environment:
 
     def add_player(self, player_name: str, pos: npt.NDArray = None):
         log.debug(f"Add player {player_name} to the game")
-        player = Player(player_name, pos)
+        player = Player(
+            player_name, player_config=self.environment_config["player_config"], pos=pos
+        )
         self.players[player.name] = player
         if player.pos is None:
             if len(self.designated_player_positions) > 0:
@@ -439,10 +567,12 @@ class Environment:
         and time limits.
         """
         self.env_time += passed_time
+
         with self.lock:
             for counter in self.counters:
                 if isinstance(counter, (CuttingBoard, Stove, 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)
 
     def get_state(self):
         """Get the current state of the game environment. The state here is accessible by the current python objects.
@@ -450,7 +580,15 @@ class Environment:
         Returns: Dict of lists of the current relevant game objects.
 
         """
-        return {"players": self.players, "counters": self.counters, "score": self.score}
+        return {
+            "players": self.players,
+            "counters": self.counters,
+            "score": self.order_and_score.score,
+            "orders": self.order_and_score.open_orders,
+            "ended": self.game_ended,
+            "env_time": self.env_time,
+            "remaining_time": max(self.env_time_end - self.env_time, timedelta(0)),
+        }
 
     def get_state_simple_json(self):
         """Get the current state of the game environment as a json-like nested dictionary.
diff --git a/overcooked_simulator/player.py b/overcooked_simulator/player.py
index 4f85046ff9a100c8b0b95a43246a9acccaff4b04..63f803c86a1c2575c2cf2a8b8248e8c0a62d116a 100644
--- a/overcooked_simulator/player.py
+++ b/overcooked_simulator/player.py
@@ -1,15 +1,12 @@
 import logging
 from collections import deque
-from pathlib import Path
-from typing import Optional
+from typing import Optional, Any
 
 import numpy as np
 import numpy.typing as npt
-import yaml
 
-from overcooked_simulator import ROOT_DIR
 from overcooked_simulator.counters import Counter
-from overcooked_simulator.game_items import Item
+from overcooked_simulator.game_items import Item, Plate
 
 log = logging.getLogger(__name__)
 
@@ -24,9 +21,11 @@ class Player:
     def __init__(
         self,
         name: str,
+        player_config: dict[str, Any],
         pos: Optional[npt.NDArray[float]] = None,
     ):
         self.name: str = name
+        self.player_config = player_config
         if pos is not None:
             self.pos: npt.NDArray[float] = np.array(pos, dtype=float)
         else:
@@ -34,12 +33,6 @@ class Player:
 
         self.holding: Optional[Item] = None
 
-        self.player_config_path: Path = Path(
-            ROOT_DIR / "game_content" / "player_config.yaml"
-        )
-        with open(self.player_config_path, "r") as file:
-            self.player_config = yaml.safe_load(file)
-
         self.radius: float = self.player_config["radius"]
         self.move_dist: int = self.player_config["move_dist"]
         self.interaction_range: int = self.player_config["interaction_range"]
@@ -120,6 +113,8 @@ class Player:
         log.debug(
             f"Self: {self.holding}, {counter.__class__.__name__}: {counter.occupied_by}"
         )
+        if isinstance(self.holding, Plate):
+            log.debug(self.holding.clean)
 
     def perform_interact_hold_start(self, counter: Counter):
         """Starts an interaction with the counter. Should be called for a
diff --git a/overcooked_simulator/simulation_runner.py b/overcooked_simulator/simulation_runner.py
index f004ecdd68d52be1e5d596c0bef5434948aa610b..5c5754d0745b24396ef0487a47da9ba42ee69703 100644
--- a/overcooked_simulator/simulation_runner.py
+++ b/overcooked_simulator/simulation_runner.py
@@ -98,9 +98,11 @@ class Simulator(Thread):
 
         overslept_in_ns = 0
         self.env.reset_env_time()
+        last_step_start = time.time_ns()
         while not self.finished:
             step_start = time.time_ns()
-            self.step(timedelta(seconds=overslept_in_ns / 1_000_000_000))
+            self.step(timedelta(seconds=(step_start - last_step_start) / 1_000_000_000))
+            last_step_start = step_start
             step_duration = time.time_ns() - step_start
 
             time_to_sleep_ns = self.preferred_sleep_time_ns - (
diff --git a/setup.py b/setup.py
index f03249639eb04f12e8b9bbd12d70ac9d792a9e19..df6cec6fdeed8e45f88a8e987cfb04f951c6f514 100644
--- a/setup.py
+++ b/setup.py
@@ -41,7 +41,7 @@ setup(
     license="MIT license",
     long_description=readme + "\n\n" + history,
     include_package_data=True,
-    keywords=["aaambos", "overcooked_simulator"],
+    keywords=["overcooked_simulator"],
     name="overcooked_simulator",
     packages=find_packages(include=["overcooked_simulator", "overcooked_simulator.*"]),
     test_suite="tests",
diff --git a/tests/test_start.py b/tests/test_start.py
index 64fce11daf8fb14381605e96180e55b2db40e827..d6010a9e0e373c12673601c8f6a99c55651296d0 100644
--- a/tests/test_start.py
+++ b/tests/test_start.py
@@ -6,7 +6,7 @@ import pytest
 
 from overcooked_simulator import ROOT_DIR
 from overcooked_simulator.counters import Counter, CuttingBoard
-from overcooked_simulator.game_items import CuttableItem
+from overcooked_simulator.game_items import Item
 from overcooked_simulator.overcooked_environment import Action, Environment
 from overcooked_simulator.simulation_runner import Simulator
 from overcooked_simulator.utils import create_init_env_time
@@ -118,25 +118,27 @@ def test_collision_detection():
     player2 = sim.env.players["p2"]
 
     sim.start()
-    assert not sim.env.detect_collision_counters(player1), "Should not collide"
-    assert not sim.env.detect_player_collision(player1), "Should not collide yet."
-
-    assert not sim.env.detect_collision(player1), "Does not collide yet."
-
-    player1.move_abs(counter_pos)
-    assert sim.env.detect_collision_counters(
-        player1
-    ), "Player and counter at same pos. Not detected."
-    player2.move_abs(counter_pos)
-    assert sim.env.detect_player_collision(
-        player1
-    ), "Players at same pos. Not detected."
-
-    player1.move_abs(np.array([0, 0]))
-    assert sim.env.detect_collision_world_bounds(
-        player1
-    ), "Player collides with world bounds."
-    sim.stop()
+    try:
+        assert not sim.env.detect_collision_counters(player1), "Should not collide"
+        assert not sim.env.detect_player_collision(player1), "Should not collide yet."
+
+        assert not sim.env.detect_collision(player1), "Does not collide yet."
+
+        player1.move_abs(counter_pos)
+        assert sim.env.detect_collision_counters(
+            player1
+        ), "Player and counter at same pos. Not detected."
+        player2.move_abs(counter_pos)
+        assert sim.env.detect_player_collision(
+            player1
+        ), "Players at same pos. Not detected."
+
+        player1.move_abs(np.array([0, 0]))
+        assert sim.env.detect_collision_world_bounds(
+            player1
+        ), "Player collides with world bounds."
+    finally:
+        sim.stop()
 
 
 def test_player_reach():
@@ -169,7 +171,7 @@ def test_pickup():
 
     counter_pos = np.array([2, 2])
     counter = Counter(counter_pos)
-    counter.occupied_by = CuttableItem(name="Tomato", item_info=None)
+    counter.occupied_by = Item(name="Tomato", item_info=None)
     sim.env.counters = [counter]
 
     sim.register_player("p1", np.array([2, 3]))
@@ -217,36 +219,38 @@ def test_processing():
         sim_frequency,
     )
     sim.start()
+    try:
+        counter_pos = np.array([2, 2])
+        counter = CuttingBoard(
+            counter_pos,
+            transitions={"Tomato": {"seconds": 1, "result": "ChoppedTomato"}},
+        )
+        sim.env.counters.append(counter)
 
-    counter_pos = np.array([2, 2])
-    counter = CuttingBoard(counter_pos)
-    sim.env.counters.append(counter)
+        tomato = Item(name="Tomato", item_info=None)
+        sim.register_player("p1", np.array([2, 3]))
+        player = sim.env.players["p1"]
+        player.holding = tomato
 
-    tomato = CuttableItem(name="Tomato", item_info=None)
-    sim.register_player("p1", np.array([2, 3]))
-    player = sim.env.players["p1"]
-    player.holding = tomato
+        move = Action("p1", "movement", np.array([0, -1]))
+        pick = Action("p1", "pickup", "pickup")
 
-    move = Action("p1", "movement", np.array([0, -1]))
-    pick = Action("p1", "pickup", "pickup")
+        sim.enter_action(move)
+        sim.enter_action(pick)
 
-    sim.enter_action(move)
-    sim.enter_action(pick)
-
-    hold_down = Action("p1", "interact", "keydown")
-    sim.enter_action(hold_down)
+        hold_down = Action("p1", "interact", "keydown")
+        sim.enter_action(hold_down)
 
-    assert not tomato.finished, "Tomato is not finished yet."
+        assert tomato.name != "ChoppedTomato", "Tomato is not finished yet."
 
-    sleep_time = (tomato.steps_needed / sim_frequency) + 0.1
-    time.sleep(sleep_time)
+        time.sleep(1)
 
-    assert tomato.finished, "Tomato should be finished."
+        assert tomato.name == "ChoppedTomato", "Tomato should be finished."
 
-    button_up = Action("p1", "interact", "keyup")
-    sim.enter_action(button_up)
-
-    sim.stop()
+        button_up = Action("p1", "interact", "keyup")
+        sim.enter_action(button_up)
+    finally:
+        sim.stop()
 
 
 def test_time_passed():
@@ -268,3 +272,27 @@ def test_time_passed():
     assert (
         env.env_time == create_init_env_time() + passed_time + passed_time_2
     ), "Env time needs to be updated via the step function"
+
+
+def test_time_limit():
+    np.random.seed(42)
+    env = Environment(
+        ROOT_DIR / "game_content" / "environment_config.yaml",
+        layouts_folder / "empty.layout",
+        ROOT_DIR / "game_content" / "item_info.yaml",
+    )
+    env.reset_env_time()
+
+    assert not env.game_ended, "Game has not ended yet"
+
+    passed_time = timedelta(seconds=10)
+    env.step(passed_time)
+
+    assert not env.game_ended, "Game has not ended yet"
+
+    passed_time_2 = timedelta(
+        seconds=(env.env_time_end - env.beginning_time).total_seconds()
+    )
+    env.step(passed_time_2)
+
+    assert env.game_ended, "Game has ended now."