From c509030bb67233bdf7d4c2b78c4e676c0af25694 Mon Sep 17 00:00:00 2001
From: fheinrich <fheinrich@techfak.uni-bielefeld.de>
Date: Wed, 6 Dec 2023 11:51:19 +0100
Subject: [PATCH] Added different types of items. Added a cutting-board-counter
 which can process cuttable items. A player can hold the interact key for the
 cut process to progress. Tied to simulator frequency.

---
 overcooked_simulator/counters.py              | 43 +++++++++++++++++++
 overcooked_simulator/game_items.py            | 38 +++++++++++++++-
 overcooked_simulator/layouts/basic.layout     |  2 +-
 overcooked_simulator/main.py                  |  3 +-
 .../overcooked_environment.py                 | 16 ++++---
 overcooked_simulator/player.py                |  9 ----
 6 files changed, 93 insertions(+), 18 deletions(-)

diff --git a/overcooked_simulator/counters.py b/overcooked_simulator/counters.py
index c51e40e3..46dcda1e 100644
--- a/overcooked_simulator/counters.py
+++ b/overcooked_simulator/counters.py
@@ -1,5 +1,7 @@
 import numpy as np
 
+from overcooked_simulator.game_items import CuttableItem
+
 
 class Counter:
     """Simple class for a counter at a specified position (center of counter). Can hold things on top."""
@@ -53,3 +55,44 @@ class Counter:
 
     def __repr__(self):
         return f"Counter(pos:{str(self.pos)},holds:{self.occupied_by})"
+
+
+class CuttingBoard(Counter):
+    def __init__(self, pos):
+        self.progressing = False
+        super().__init__(pos)
+
+    def progress(self):
+        """Called by environment step function for time progression"""
+        if self.progressing:
+            if isinstance(self.occupied_by, CuttableItem):
+                self.occupied_by.progress()
+
+    def start_progress(self):
+        self.progressing = True
+
+    def pause_progress(self):
+        self.progressing = False
+
+    def drop_off(self, item):
+        if self.occupied_by is None:
+            self.occupied_by = item
+
+    def can_drop_off(self, item):
+        return self.occupied_by is None
+
+    def pick_up(self):
+        return_to_player = self.occupied_by
+        self.occupied_by = None
+        return return_to_player
+
+    def interact_start(self):
+        print("START")
+        self.start_progress()
+
+    def interact_stop(self):
+        print("STOP")
+        self.pause_progress()
+
+    def __repr__(self):
+        return f"CuttingBoard({self.occupied_by})"
diff --git a/overcooked_simulator/game_items.py b/overcooked_simulator/game_items.py
index 8dfb402a..cd784132 100644
--- a/overcooked_simulator/game_items.py
+++ b/overcooked_simulator/game_items.py
@@ -1,6 +1,42 @@
 class HoldableItem:
+    """Base class for game items which can be held by a player."""
+
     pass
 
 
-class Tomato(HoldableItem):
+class ProgressibleItem(HoldableItem):
+    """Class for items which need to be processed (cut, cooked, ...)"""
+
+    def __init__(self, steps_needed):
+        self.progressed_steps = 0
+        self.steps_needed = steps_needed
+        self.finished = False
+        super().__init__()
+
+    def progress(self):
+        """Progresses the item process as long as it is not finished."""
+        print("PROGRESSING ITEM")
+        if self.progressed_steps >= self.steps_needed:
+            self.finished = True
+            self.progressed_steps = 0
+        if not self.finished:
+            self.progressed_steps += 1
+
+    def __repr__(self):
+        if self.finished:
+            return "CutTomato"
+        else:
+            return f"{self.__class__.__name__}({int(self.progressed_steps / self.steps_needed * 100)}%)"
+
+
+class CuttableItem(ProgressibleItem):
+    """Class of item which can be processed by the cutting board."""
+
     pass
+
+
+class Tomato(CuttableItem):
+    """Item class representing a tomato. Can be cut on the cutting board"""
+
+    def __init__(self):
+        super().__init__(steps_needed=1500)
diff --git a/overcooked_simulator/layouts/basic.layout b/overcooked_simulator/layouts/basic.layout
index 82524fc6..beb5fc01 100644
--- a/overcooked_simulator/layouts/basic.layout
+++ b/overcooked_simulator/layouts/basic.layout
@@ -7,5 +7,5 @@ ECEEEEEEEEE
 ECEEEEEEEEE
 ECEEEEEEECE
 ECEEEEEEECE
-ECCCCCCCCCE
+ECCBBCCCCCE
 EEEEEEEEEEE
diff --git a/overcooked_simulator/main.py b/overcooked_simulator/main.py
index 393255aa..48b34e3c 100644
--- a/overcooked_simulator/main.py
+++ b/overcooked_simulator/main.py
@@ -8,7 +8,7 @@ from overcooked_simulator.simulation_runner import Simulator
 
 
 def main():
-    simulator = Simulator(Path("overcooked_simulator/layouts/basic.layout"), 300)
+    simulator = Simulator(Path("overcooked_simulator/layouts/basic.layout"), 600)
     simulator.register_player(Player("p1", [100, 200]))
     simulator.register_player(Player("p2", [200, 100]))
 
@@ -17,7 +17,6 @@ def main():
     gui = PyGameGUI(simulator)
 
     simulator.start()
-
     gui.start_pygame()
 
     simulator.stop()
diff --git a/overcooked_simulator/overcooked_environment.py b/overcooked_simulator/overcooked_environment.py
index 4b3d48e2..209485bf 100644
--- a/overcooked_simulator/overcooked_environment.py
+++ b/overcooked_simulator/overcooked_environment.py
@@ -7,7 +7,7 @@ if TYPE_CHECKING:
 from pathlib import Path
 import numpy as np
 from scipy.spatial import distance_matrix
-from overcooked_simulator.counters import Counter
+from overcooked_simulator.counters import Counter, CuttingBoard
 
 
 class Action:
@@ -65,9 +65,12 @@ class Environment:
                 if character == "C":
                     counter = Counter(np.array([current_x, current_y]))
                     counters.append(counter)
-                    current_x += self.counter_side_length
+                elif character == "B":
+                    counter = CuttingBoard(np.array([current_x, current_y]))
+                    counters.append(counter)
                 elif character == "E":
-                    current_x += self.counter_side_length
+                    pass
+                current_x += self.counter_side_length
             current_y += self.counter_side_length
         return counters
 
@@ -80,7 +83,8 @@ class Environment:
             action: The action to be performed
         """
 
-        # print("RECEIVED ACTION:", action)
+        # if action.act_type != "movement":
+        #     print("RECEIVED ACTION:", action)
 
         assert action.player in self.players.keys(), "Unknown player."
         player = self.players[action.player]
@@ -256,7 +260,9 @@ class Environment:
         """Performs a step of the environment. Affects time based events such as cooking or cutting things, orders
         and time limits.
         """
-        pass
+        for counter in self.counters:
+            if isinstance(counter, CuttingBoard):
+                counter.progress()
 
     def get_state(self):
         """Get the current state of the game environment. The state here is accessible by the current python objects.
diff --git a/overcooked_simulator/player.py b/overcooked_simulator/player.py
index 4920f0dc..28f812bd 100644
--- a/overcooked_simulator/player.py
+++ b/overcooked_simulator/player.py
@@ -75,15 +75,6 @@ class Player:
             counter.drop_off(self.holding)
             self.holding = None
 
-    def interact(self, counter: Counter):
-        """Performs the interact-action with the counter. Handles logic of starting processes
-        like for e.g. cutting onions. TODO holding the button vs pressing once?
-
-        Args:
-            counter: The counter to interact with.
-        """
-        pass
-
     def perform_interact_hold_start(self, counter: Counter):
         """Starts an interaction with the counter. Should be called for a
         keydown event, for holding down a key on the keyboard.
-- 
GitLab