From b5e8fe651d2e4190527c0aa11d1ade774f6a93e0 Mon Sep 17 00:00:00 2001
From: fheinrich <fheinrich@techfak.uni-bielefeld.de>
Date: Thu, 7 Dec 2023 10:22:49 +0100
Subject: [PATCH] Added Counter types: ServingWindow, Trash, TomatoDispenser,
 PlateReturn

---
 overcooked_simulator/counters.py              | 77 ++++++++++++++++++-
 overcooked_simulator/game_items.py            |  6 +-
 overcooked_simulator/layouts/basic.layout     |  8 +-
 overcooked_simulator/main.py                  | 13 ++--
 .../overcooked_environment.py                 | 26 ++++++-
 overcooked_simulator/player.py                | 12 ++-
 overcooked_simulator/pygame_gui/pygame_gui.py | 55 +++++++++++--
 7 files changed, 169 insertions(+), 28 deletions(-)

diff --git a/overcooked_simulator/counters.py b/overcooked_simulator/counters.py
index 742b9bb1..aa372ed9 100644
--- a/overcooked_simulator/counters.py
+++ b/overcooked_simulator/counters.py
@@ -2,7 +2,13 @@ from typing import Optional
 
 import numpy as np
 
-from overcooked_simulator.game_items import CuttableItem, HoldableItem
+from overcooked_simulator.game_items import (
+    CuttableItem,
+    HoldableItem,
+    ProgressibleItem,
+    Plate,
+    Tomato,
+)
 
 
 class Counter:
@@ -90,3 +96,72 @@ class CuttingBoard(Counter):
 
     def __repr__(self):
         return f"CuttingBoard({self.occupied_by})"
+
+
+class ServingWindow(Counter):
+    def drop_off(self, item):
+        return 5
+
+    def can_score(self, item):
+        if isinstance(item, Plate) and isinstance(item.holds, ProgressibleItem):
+            return item.holds.finished
+
+    def can_drop_off(self, item: HoldableItem):
+        return self.can_score(item)
+
+    def pick_up(self):
+        return None
+
+    def __repr__(self):
+        return "ServingWindow"
+
+
+class PlateReturn(Counter):
+    def __init__(self, pos):
+        super().__init__(pos)
+        self.occupied_by = Plate()
+
+    def pick_up(self):
+        return Plate()
+
+    def drop_off(self, item):
+        return 0
+
+    def can_drop_off(self, item):
+        return False
+
+    def __repr__(self):
+        return "PlateReturn"
+
+
+class TomatoDispenser(Counter):
+    def __init__(self, pos):
+        super().__init__(pos)
+
+    def pick_up(self):
+        return Tomato()
+
+    def drop_off(self, item: HoldableItem):
+        return 0
+
+    def can_drop_off(self, item: HoldableItem):
+        return False
+
+    def __repr__(self):
+        return f"{self.occupied_by}Dispenser"
+
+
+class Trash(Counter):
+    def pick_up(self):
+        pass
+
+    def drop_off(self, item: HoldableItem):
+        if isinstance(item, Plate):
+            item.holds = None
+        return -1
+
+    def can_drop_off(self, item: HoldableItem):
+        return True
+
+    def __repr__(self):
+        return "Trash"
diff --git a/overcooked_simulator/game_items.py b/overcooked_simulator/game_items.py
index a40ad99d..b713fbfd 100644
--- a/overcooked_simulator/game_items.py
+++ b/overcooked_simulator/game_items.py
@@ -17,10 +17,8 @@ class Plate(HoldableItem):
 
         super().__init__()
 
-    def can_combine(self, other):
-        if self.holds is None:
-            if isinstance(other, HoldableItem):
-                return True
+    def can_combine(self, other: HoldableItem):
+        return self.holds is None
 
     def combine(self, other):
         self.holds = other
diff --git a/overcooked_simulator/layouts/basic.layout b/overcooked_simulator/layouts/basic.layout
index beb5fc01..d4d883fc 100644
--- a/overcooked_simulator/layouts/basic.layout
+++ b/overcooked_simulator/layouts/basic.layout
@@ -1,11 +1,11 @@
 EEEEEEEEEEE
-ECCCCCCCCCE
+ECCCCTCCCCE
 ECEEEEEEECE
 ECEEEEEEECE
+EWEEEEEEEEE
 ECEEEEEEEEE
-ECEEEEEEEEE
-ECEEEEEEEEE
-ECEEEEEEECE
+EPEEEEEEEEE
 ECEEEEEEECE
+ECEEEEEEEXE
 ECCBBCCCCCE
 EEEEEEEEEEE
diff --git a/overcooked_simulator/main.py b/overcooked_simulator/main.py
index 5138cab1..3bdc5528 100644
--- a/overcooked_simulator/main.py
+++ b/overcooked_simulator/main.py
@@ -4,7 +4,6 @@ from pathlib import Path
 import numpy as np
 import pygame
 
-from overcooked_simulator.game_items import Tomato, Plate
 from overcooked_simulator.player import Player
 from overcooked_simulator.pygame_gui.pygame_gui import PyGameGUI
 from overcooked_simulator.simulation_runner import Simulator
@@ -14,13 +13,13 @@ def main():
     simulator = Simulator(Path("overcooked_simulator/layouts/basic.layout"), 600)
     player_one_name = "p1"
     player_two_name = "p2"
-    simulator.register_player(Player(player_one_name, np.array([100, 200])))
-    simulator.register_player(Player(player_two_name, np.array([200, 100])))
+    simulator.register_player(Player(player_one_name, np.array([200, 200])))
+    simulator.register_player(Player(player_two_name, np.array([100, 200])))
 
-    simulator.env.counters[3].occupied_by = Tomato()
-    simulator.env.counters[4].occupied_by = Tomato()
-    simulator.env.counters[6].occupied_by = Plate()
-    simulator.env.counters[7].occupied_by = Plate()
+    # simulator.env.counters[3].occupied_by = Tomato()
+    # simulator.env.counters[4].occupied_by = Tomato()
+    # simulator.env.counters[6].occupied_by = Plate()
+    # simulator.env.counters[7].occupied_by = Plate()
 
     # TODO maybe read the player names and keyboard keys from config file?
     keys1 = [
diff --git a/overcooked_simulator/overcooked_environment.py b/overcooked_simulator/overcooked_environment.py
index 8aa9f198..2d677389 100644
--- a/overcooked_simulator/overcooked_environment.py
+++ b/overcooked_simulator/overcooked_environment.py
@@ -7,7 +7,14 @@ if TYPE_CHECKING:
 from pathlib import Path
 import numpy as np
 from scipy.spatial import distance_matrix
-from overcooked_simulator.counters import Counter, CuttingBoard
+from overcooked_simulator.counters import (
+    Counter,
+    CuttingBoard,
+    Trash,
+    ServingWindow,
+    TomatoDispenser,
+    PlateReturn,
+)
 
 
 class Action:
@@ -62,12 +69,25 @@ class Environment:
             current_x = self.counter_side_length / 2
             for character in line:
                 character = character.capitalize()
+                pos = np.array([current_x, current_y])
                 match character:
                     case "C":
-                        counter = Counter(np.array([current_x, current_y]))
+                        counter = Counter(pos)
                         counters.append(counter)
                     case "B":
-                        counter = CuttingBoard(np.array([current_x, current_y]))
+                        counter = CuttingBoard(pos)
+                        counters.append(counter)
+                    case "X":
+                        counter = Trash(pos)
+                        counters.append(counter)
+                    case "W":
+                        counter = ServingWindow(pos)
+                        counters.append(counter)
+                    case "T":
+                        counter = TomatoDispenser(pos)
+                        counters.append(counter)
+                    case "P":
+                        counter = PlateReturn(pos)
                         counters.append(counter)
                     case "E":
                         pass
diff --git a/overcooked_simulator/player.py b/overcooked_simulator/player.py
index 48dd8c44..971f9024 100644
--- a/overcooked_simulator/player.py
+++ b/overcooked_simulator/player.py
@@ -2,8 +2,8 @@ from typing import Optional
 
 import numpy as np
 
-from overcooked_simulator.counters import Counter
-from overcooked_simulator.game_items import HoldableItem
+from overcooked_simulator.counters import Counter, Trash
+from overcooked_simulator.game_items import HoldableItem, Plate
 
 
 class Player:
@@ -72,12 +72,16 @@ class Player:
         Args:
             counter: The counter to pick things up from or put things down.
         """
+
         if self.holding is None:
             self.holding = counter.pick_up()
 
         elif counter.can_drop_off(self.holding):
-            counter.drop_off(self.holding)
-            self.holding = None
+            if isinstance(counter, Trash) and isinstance(self.holding, Plate):
+                self.holding.holds = None
+            else:
+                counter.drop_off(self.holding)
+                self.holding = None
 
         elif self.holding.can_combine(counter.occupied_by):
             returned_by_counter = counter.pick_up()
diff --git a/overcooked_simulator/pygame_gui/pygame_gui.py b/overcooked_simulator/pygame_gui/pygame_gui.py
index 2db2b590..2149535a 100644
--- a/overcooked_simulator/pygame_gui/pygame_gui.py
+++ b/overcooked_simulator/pygame_gui/pygame_gui.py
@@ -1,7 +1,13 @@
 import numpy as np
 import pygame
 
-from overcooked_simulator.counters import CuttingBoard
+from overcooked_simulator.counters import (
+    CuttingBoard,
+    Trash,
+    TomatoDispenser,
+    PlateReturn,
+    ServingWindow,
+)
 from overcooked_simulator.game_items import ProgressibleItem, Plate
 from overcooked_simulator.game_items import Tomato
 from overcooked_simulator.overcooked_environment import Action
@@ -51,10 +57,10 @@ class PyGameGUI:
     """Visualisation of the overcooked environmnent and reading keyboard inputs using pygame."""
 
     def __init__(
-            self,
-            simulator: Simulator,
-            player_names: list[str],
-            player_keys: list[pygame.key],
+        self,
+        simulator: Simulator,
+        player_names: list[str],
+        player_keys: list[pygame.key],
     ):
         self.FPS = 60
         self.simulator = simulator
@@ -232,6 +238,45 @@ class PyGameGUI:
             knife_rect = pygame.Rect(counter.pos[0] + 6, counter.pos[1] - 8, 5, 20)
             pygame.draw.rect(self.screen, KNIFE_COLOR, knife_rect)
 
+        if isinstance(counter, PlateReturn):
+            RETURN = (170, 170, 240)
+            size = 38
+            inner = pygame.Rect(
+                counter.pos[0] - (size / 2),
+                counter.pos[1] - (size / 2),
+                size,
+                size,
+            )
+            pygame.draw.rect(self.screen, RETURN, inner)
+
+        if isinstance(counter, Trash):
+            pygame.draw.circle(
+                self.screen,
+                (80, 80, 80),
+                counter.pos,
+                self.counter_size * 0.4,
+            )
+            pygame.draw.circle(self.screen, BLACK, counter.pos, self.counter_size * 0.3)
+        if isinstance(counter, TomatoDispenser):
+            board_size = 32
+            board_rect = pygame.Rect(
+                counter.pos[0] - (board_size / 2),
+                counter.pos[1] - (board_size / 2),
+                board_size,
+                board_size,
+            )
+            pygame.draw.rect(self.screen, RED, board_rect)
+            self.draw_item(counter.pos, Tomato())
+        if isinstance(counter, ServingWindow):
+            board_size = 33
+            board_rect = pygame.Rect(
+                counter.pos[0] - (board_size / 2),
+                counter.pos[1] - (board_size / 2),
+                board_size,
+                board_size,
+            )
+            pygame.draw.rect(self.screen, YELLOW, board_rect)
+
         if counter.occupied_by is not None:
             self.draw_item(counter.pos, counter.occupied_by)
 
-- 
GitLab