From 6626507886c25c6c2d29df1fbb06cedfbf1d6da4 Mon Sep 17 00:00:00 2001
From: fheinrich <fheinrich@techfak.uni-bielefeld.de>
Date: Thu, 21 Dec 2023 14:29:51 +0100
Subject: [PATCH] Draft: UI with buttons and window changing.

---
 .../game_content/layouts/empty.layout         |  11 +-
 .../{pygame_gui => gui_2d_vis}/__init__.py    |   0
 .../{pygame_gui => gui_2d_vis}/game_colors.py |   0
 .../gui_2d_vis/gui_theme.json                 |  49 +++
 .../images/pixel_cook.png                     | Bin
 .../images/plate.png                          | Bin
 .../images/plate_clean.png                    | Bin
 .../images/plate_dirty.png                    | Bin
 .../{pygame_gui => gui_2d_vis}/images/pot.png | Bin
 .../images/tomato.png                         | Bin
 .../images/tomato_cut.png                     | Bin
 .../images/tomato_soup.png                    | Bin
 .../overcooked_gui.py}                        | 281 +++++++++++++++---
 .../gui_2d_vis/pygame_gui_test.py             |  38 +++
 .../visualization.yaml                        |   0
 overcooked_simulator/main.py                  |   7 +-
 overcooked_simulator/simulation_runner.py     |   4 +-
 setup.py                                      |   2 +-
 18 files changed, 348 insertions(+), 44 deletions(-)
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/__init__.py (100%)
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/game_colors.py (100%)
 create mode 100644 overcooked_simulator/gui_2d_vis/gui_theme.json
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/images/pixel_cook.png (100%)
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/images/plate.png (100%)
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/images/plate_clean.png (100%)
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/images/plate_dirty.png (100%)
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/images/pot.png (100%)
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/images/tomato.png (100%)
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/images/tomato_cut.png (100%)
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/images/tomato_soup.png (100%)
 rename overcooked_simulator/{pygame_gui/pygame_gui.py => gui_2d_vis/overcooked_gui.py} (64%)
 create mode 100644 overcooked_simulator/gui_2d_vis/pygame_gui_test.py
 rename overcooked_simulator/{pygame_gui => gui_2d_vis}/visualization.yaml (100%)

diff --git a/overcooked_simulator/game_content/layouts/empty.layout b/overcooked_simulator/game_content/layouts/empty.layout
index de0f1b9e..68ddca6e 100644
--- a/overcooked_simulator/game_content/layouts/empty.layout
+++ b/overcooked_simulator/game_content/layouts/empty.layout
@@ -1,3 +1,10 @@
+_____#########################
 _____
-_____
-____P
\ No newline at end of file
+____P
+##
+#
+#
+#
+#
+#
+#
\ No newline at end of file
diff --git a/overcooked_simulator/pygame_gui/__init__.py b/overcooked_simulator/gui_2d_vis/__init__.py
similarity index 100%
rename from overcooked_simulator/pygame_gui/__init__.py
rename to overcooked_simulator/gui_2d_vis/__init__.py
diff --git a/overcooked_simulator/pygame_gui/game_colors.py b/overcooked_simulator/gui_2d_vis/game_colors.py
similarity index 100%
rename from overcooked_simulator/pygame_gui/game_colors.py
rename to overcooked_simulator/gui_2d_vis/game_colors.py
diff --git a/overcooked_simulator/gui_2d_vis/gui_theme.json b/overcooked_simulator/gui_2d_vis/gui_theme.json
new file mode 100644
index 00000000..3ca11b55
--- /dev/null
+++ b/overcooked_simulator/gui_2d_vis/gui_theme.json
@@ -0,0 +1,49 @@
+{
+  "defaults": {
+    "colours": {
+      "normal_bg": "#45494e",
+      "hovered_bg": "#35393e",
+      "disabled_bg": "#25292e",
+      "selected_bg": "#193754",
+      "dark_bg": "#15191e",
+      "normal_text": "#c5cbd8",
+      "hovered_text": "#FFFFFF",
+      "selected_text": "#FFFFFF",
+      "disabled_text": "#6d736f",
+      "link_text": "#0000EE",
+      "link_hover": "#2020FF",
+      "link_selected": "#551A8B",
+      "text_shadow": "#777777",
+      "normal_border": "#DDDDDD",
+      "hovered_border": "#B0B0B0",
+      "disabled_border": "#808080",
+      "selected_border": "#8080B0",
+      "active_border": "#8080B0",
+      "filled_bar": "#f4251b",
+      "unfilled_bar": "#CCCCCC"
+    }
+  },
+  "button": {
+    "colours": {
+      "normal_bg": "#45494e",
+      "hovered_bg": "#35393e",
+      "disabled_bg": "#25292e",
+      "selected_bg": "#193754",
+      "active_bg": "#193754",
+      "dark_bg": "#15191e",
+      "normal_text": "#c5cbd8",
+      "hovered_text": "#FFFFFF",
+      "selected_text": "#FFFFFF",
+      "disabled_text": "#6d736f",
+      "active_text": "#FFFFFF",
+      "normal_border": "#DDDDDD",
+      "hovered_border": "#B0B0B0",
+      "disabled_border": "#808080",
+      "selected_border": "#8080B0",
+      "active_border": "#8080B0"
+    },
+    "misc": {
+      "tool_tip_delay": "1.5"
+    }
+  }
+}
\ No newline at end of file
diff --git a/overcooked_simulator/pygame_gui/images/pixel_cook.png b/overcooked_simulator/gui_2d_vis/images/pixel_cook.png
similarity index 100%
rename from overcooked_simulator/pygame_gui/images/pixel_cook.png
rename to overcooked_simulator/gui_2d_vis/images/pixel_cook.png
diff --git a/overcooked_simulator/pygame_gui/images/plate.png b/overcooked_simulator/gui_2d_vis/images/plate.png
similarity index 100%
rename from overcooked_simulator/pygame_gui/images/plate.png
rename to overcooked_simulator/gui_2d_vis/images/plate.png
diff --git a/overcooked_simulator/pygame_gui/images/plate_clean.png b/overcooked_simulator/gui_2d_vis/images/plate_clean.png
similarity index 100%
rename from overcooked_simulator/pygame_gui/images/plate_clean.png
rename to overcooked_simulator/gui_2d_vis/images/plate_clean.png
diff --git a/overcooked_simulator/pygame_gui/images/plate_dirty.png b/overcooked_simulator/gui_2d_vis/images/plate_dirty.png
similarity index 100%
rename from overcooked_simulator/pygame_gui/images/plate_dirty.png
rename to overcooked_simulator/gui_2d_vis/images/plate_dirty.png
diff --git a/overcooked_simulator/pygame_gui/images/pot.png b/overcooked_simulator/gui_2d_vis/images/pot.png
similarity index 100%
rename from overcooked_simulator/pygame_gui/images/pot.png
rename to overcooked_simulator/gui_2d_vis/images/pot.png
diff --git a/overcooked_simulator/pygame_gui/images/tomato.png b/overcooked_simulator/gui_2d_vis/images/tomato.png
similarity index 100%
rename from overcooked_simulator/pygame_gui/images/tomato.png
rename to overcooked_simulator/gui_2d_vis/images/tomato.png
diff --git a/overcooked_simulator/pygame_gui/images/tomato_cut.png b/overcooked_simulator/gui_2d_vis/images/tomato_cut.png
similarity index 100%
rename from overcooked_simulator/pygame_gui/images/tomato_cut.png
rename to overcooked_simulator/gui_2d_vis/images/tomato_cut.png
diff --git a/overcooked_simulator/pygame_gui/images/tomato_soup.png b/overcooked_simulator/gui_2d_vis/images/tomato_soup.png
similarity index 100%
rename from overcooked_simulator/pygame_gui/images/tomato_soup.png
rename to overcooked_simulator/gui_2d_vis/images/tomato_soup.png
diff --git a/overcooked_simulator/pygame_gui/pygame_gui.py b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
similarity index 64%
rename from overcooked_simulator/pygame_gui/pygame_gui.py
rename to overcooked_simulator/gui_2d_vis/overcooked_gui.py
index a723373f..d5e4ca3d 100644
--- a/overcooked_simulator/pygame_gui/pygame_gui.py
+++ b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
@@ -3,10 +3,12 @@ import logging
 import math
 import sys
 from collections import deque
+from enum import Enum
 
 import numpy as np
 import numpy.typing as npt
 import pygame
+import pygame_gui
 import yaml
 from scipy.spatial import KDTree
 
@@ -19,9 +21,9 @@ from overcooked_simulator.game_items import (
     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.overcooked_environment import Action
-from overcooked_simulator.pygame_gui.game_colors import BLUE
-from overcooked_simulator.pygame_gui.game_colors import colors, Color
 from overcooked_simulator.simulation_runner import Simulator
 
 USE_PLAYER_COOK_SPRITES = True
@@ -29,6 +31,12 @@ SHOW_INTERACTION_RANGE = False
 SHOW_COUNTER_CENTERS = False
 
 
+class MenuStates(Enum):
+    Start = "Start"
+    Game = "Game"
+    End = "End"
+
+
 def create_polygon(n, length):
     if n == 0:
         return np.array([0, 0])
@@ -86,11 +94,17 @@ class PyGameGUI:
         player_names: list[str],
         player_keys: list[pygame.key],
     ):
-        self.screen = None
+        self.game_screen = None
         self.FPS = 60
         self.simulator = simulator
         self.counter_size = self.simulator.env.counter_side_length
+
+        self.screen_margin = 100
         self.window_width, self.window_height = (
+            simulator.env.world_width + (2 * self.screen_margin),
+            simulator.env.world_height + (2 * self.screen_margin),
+        )
+        self.game_width, self.game_height = (
             simulator.env.world_width,
             simulator.env.world_height,
         )
@@ -106,7 +120,7 @@ class PyGameGUI:
         ]
 
         # TODO cache loaded images?
-        with open(ROOT_DIR / "pygame_gui" / "visualization.yaml", "r") as file:
+        with open(ROOT_DIR / "gui_2d_vis" / "visualization.yaml", "r") as file:
             self.visualization_config = yaml.safe_load(file)
 
         self.images_path = ROOT_DIR / "pygame_gui" / "images"
@@ -115,6 +129,9 @@ class PyGameGUI:
 
         self.image_cache_dict = {}
 
+        self.menu_state = MenuStates.Start
+        self.manager: pygame_gui.UIManager
+
     def create_player_colors(self) -> list[Color]:
         number_player = len(self.simulator.env.players)
         hue_values = np.linspace(0, 1, number_player + 1)
@@ -183,12 +200,15 @@ class PyGameGUI:
 
     def draw_background(self):
         """Visualizes a game background."""
+        self.game_screen.fill(
+            colors[self.visualization_config["Kitchen"]["ground_tiles_color"]]
+        )
         block_size = self.counter_size // 2  # Set the size of the grid block
         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.screen,
+                    self.game_screen,
                     self.visualization_config["Kitchen"]["background_lines"],
                     rect,
                     1,
@@ -200,7 +220,7 @@ class PyGameGUI:
             image = self.image_cache_dict[cache_entry]
         else:
             image = pygame.image.load(
-                ROOT_DIR / "pygame_gui" / img_path
+                ROOT_DIR / "gui_2d_vis" / img_path
             ).convert_alpha()
             self.image_cache_dict[cache_entry] = image
 
@@ -209,7 +229,7 @@ class PyGameGUI:
             image = pygame.transform.rotate(image, rot_angle)
         rect = image.get_rect()
         rect.center = pos
-        self.screen.blit(image, rect)
+        self.game_screen.blit(image, rect)
 
     def draw_players(self, state):
         """Visualizes the players as circles with a triangle for the facing direction.
@@ -235,14 +255,14 @@ class PyGameGUI:
                 color1 = self.player_colors[p_idx]
                 color2 = colors["white"]
 
-                pygame.draw.circle(self.screen, color2, pos, size)
-                pygame.draw.circle(self.screen, BLUE, pos, size, width=1)
-                pygame.draw.circle(self.screen, colors[color1], pos, size // 2)
+                pygame.draw.circle(self.game_screen, color2, pos, size)
+                pygame.draw.circle(self.game_screen, BLUE, pos, size, width=1)
+                pygame.draw.circle(self.game_screen, colors[color1], pos, size // 2)
 
                 pos = player.pos
                 facing = player.facing_direction
                 pygame.draw.polygon(
-                    self.screen,
+                    self.game_screen,
                     BLUE,
                     (
                         (
@@ -259,13 +279,15 @@ class PyGameGUI:
 
             if SHOW_INTERACTION_RANGE:
                 pygame.draw.circle(
-                    self.screen,
+                    self.game_screen,
                     BLUE,
                     player.facing_point,
                     player.interaction_range * self.counter_size,
                     width=1,
                 )
-                pygame.draw.circle(self.screen, colors["red1"], player.facing_point, 4)
+                pygame.draw.circle(
+                    self.game_screen, colors["red1"], player.facing_point, 4
+                )
 
             if player.holding is not None:
                 holding_item_pos = player.pos + (20 * player.facing_direction)
@@ -274,7 +296,7 @@ class PyGameGUI:
             if player.current_nearest_counter:
                 counter: Counter = player.current_nearest_counter
                 pygame.draw.rect(
-                    self.screen,
+                    self.game_screen,
                     colors[self.player_colors[p_idx]],
                     rect=pygame.Rect(
                         counter.pos[0] - (self.counter_size // 2),
@@ -308,7 +330,7 @@ class PyGameGUI:
                 if "center_offset" in part:
                     dx, dy = np.array(part["center_offset"]) * self.counter_size
                     rect = pygame.Rect(pos[0] + dx, pos[1] + dy, height, width)
-                    pygame.draw.rect(self.screen, color, rect)
+                    pygame.draw.rect(self.game_screen, color, rect)
                 else:
                     rect = pygame.Rect(
                         pos[0] - (height / 2),
@@ -316,19 +338,19 @@ class PyGameGUI:
                         height,
                         width,
                     )
-                pygame.draw.rect(self.screen, color, rect)
+                pygame.draw.rect(self.game_screen, color, rect)
             elif part_type == "circle":
                 radius = part["radius"] * self.counter_size
                 color = colors[part["color"]]
                 if "center_offset" in part:
                     pygame.draw.circle(
-                        self.screen,
+                        self.game_screen,
                         color,
                         pos + np.array(part["center_offset"]),
                         radius,
                     )
                 else:
-                    pygame.draw.circle(self.screen, color, pos, radius)
+                    pygame.draw.circle(self.game_screen, color, pos, radius)
 
     def draw_item(self, pos: npt.NDArray[float], item: Item, scale: float = 1.0):
         """Visualization of an item at the specified position. On a counter or in the hands of the player.
@@ -373,7 +395,7 @@ class PyGameGUI:
                 progress_width,
                 bar_height,
             )
-            pygame.draw.rect(self.screen, colors["green1"], progress_bar)
+            pygame.draw.rect(self.game_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.
@@ -416,7 +438,7 @@ class PyGameGUI:
         for counter in state["counters"]:
             self.draw_counter(counter)
             if SHOW_COUNTER_CENTERS:
-                pygame.draw.circle(self.screen, colors["green1"], counter.pos, 3)
+                pygame.draw.circle(self.game_screen, colors["green1"], counter.pos, 3)
 
     def draw(self, state):
         """Main visualization function.
@@ -424,15 +446,148 @@ class PyGameGUI:
         Args:
             state: The game state returned by the environment.
         """
-        self.screen.fill(
-            colors[self.visualization_config["Kitchen"]["ground_tiles_color"]]
-        )
+
         self.draw_background()
 
         self.draw_counters(state)
         self.draw_players(state)
+        self.manager.draw_ui(self.game_screen)
+
+    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,
+                ),
+                (button_width, button_height),
+            ),
+            text="Start Game",
+            manager=self.manager,
+        )
+        self.start_button.can_hover()
+
+        self.quit_button = pygame_gui.elements.UIButton(
+            relative_rect=pygame.Rect(
+                (
+                    (self.window_width - button_width),
+                    0,
+                ),
+                (button_width, button_height),
+            ),
+            text="Quit Game",
+            manager=self.manager,
+        )
+        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),
+                ),
+                (button_width, button_height),
+            ),
+            text="End screen",
+            manager=self.manager,
+        )
+        self.finished_button.can_hover()
+
+        self.back_button = pygame_gui.elements.UIButton(
+            relative_rect=pygame.Rect(
+                (
+                    (0),
+                    (self.window_height - button_height),
+                ),
+                (button_width, button_height),
+            ),
+            text="Back to Start",
+            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,
+            manager=self.manager,
+            object_id="#score_label",
+        )
+
+        layout_file_paths = [
+            str(p.name)
+            for p in (ROOT_DIR / "game_content" / "layouts").glob("*.layout")
+        ]
+        assert len(layout_file_paths) != 0, "No layout files."
+        dropdown_width, dropdown_height = 200, 40
+        self.layout_selection = pygame_gui.elements.UIDropDownMenu(
+            relative_rect=pygame.Rect(
+                (
+                    0,
+                    0,
+                ),
+                (dropdown_width, dropdown_height),
+            ),
+            manager=self.manager,
+            options_list=layout_file_paths,
+            starting_option=layout_file_paths[-1],
+        )
 
-        pygame.display.flip()
+    def setup_simulation(self, config_path, layout_path):
+        self.simulator = Simulator(config_path, layout_path, 600)
+
+        number_player = len(self.player_names)
+        for i in range(number_player):
+            player_name = f"p{i}"
+            self.simulator.register_player(player_name)
+        self.simulator.start()
+
+    def change_to_start_window(self):
+        self.menu_state = MenuStates.Start
+        self.back_button.hide()
+        self.quit_button.show()
+        self.start_button.show()
+        self.score_label.hide()
+        self.finished_button.hide()
+        self.layout_selection.show()
+
+    def change_to_game_window(self):
+        self.menu_state = MenuStates.Game
+        self.start_button.hide()
+        self.back_button.show()
+        self.score_label.hide()
+        self.finished_button.show()
+        self.layout_selection.hide()
+        layout_path = (
+            ROOT_DIR
+            / "game_content"
+            / "layouts"
+            / self.layout_selection.selected_option
+        )
+        config_path = ROOT_DIR / "game_content" / "environment_config.yaml"
+        self.setup_simulation(config_path, layout_path)
+
+    def change_to_end_window(self):
+        self.menu_state = 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.finished_button.hide()
+        self.layout_selection.hide()
 
     def start_pygame(self):
         """Starts pygame and the gui loop. Each frame the game state is visualized and keyboard inputs are read."""
@@ -440,32 +595,90 @@ class PyGameGUI:
         pygame.init()
         pygame.font.init()
 
-        self.screen = pygame.display.set_mode((self.window_width, self.window_height))
-        pygame.display.set_caption("Simple Overcooked Simulator")
-        self.screen.fill(
-            colors[self.visualization_config["Kitchen"]["ground_tiles_color"]]
+        self.init_ui_elements()
+
+        self.screen_margin = 100
+        self.main_window = pygame.display.set_mode(
+            (
+                self.window_width,
+                self.window_height,
+            )
+        )
+        self.game_screen = pygame.Surface(
+            (
+                self.game_width,
+                self.game_height,
+            ),
         )
+        pygame.display.set_caption("Simple Overcooked Simulator")
 
         clock = pygame.time.Clock()
 
+        self.change_to_start_window()
         # Game loop
         running = True
         while running:
             try:
+                time_delta = clock.tick(self.FPS) / 1000.0
+
                 for event in pygame.event.get():
                     if event.type == pygame.QUIT:
                         running = False
-                    if event.type in [pygame.KEYDOWN, pygame.KEYUP]:
+                    if event.type == pygame_gui.UI_BUTTON_PRESSED:
+                        if event.ui_element == self.start_button:
+                            self.change_to_game_window()
+                        if event.ui_element == self.back_button:
+                            self.change_to_start_window()
+                        if event.ui_element == self.finished_button:
+                            self.change_to_end_window()
+                        if event.ui_element == self.quit_button:
+                            running = False
+                            log.debug("Quitting game")
+
+                    if (
+                        event.type in [pygame.KEYDOWN, pygame.KEYUP]
+                        and self.menu_state == MenuStates.Game
+                    ):
                         self.handle_key_event(event)
 
-                self.handle_keys()
-                clock.tick(self.FPS)
-                state = self.simulator.get_state()
-                self.draw(state)
+                    self.manager.process_events(event)
+
+                # drawing:
+
+                self.main_window.fill(colors["lemonchiffon1"])
+                self.manager.draw_ui(self.main_window)
+
+                match self.menu_state:
+                    case MenuStates.Start:
+                        pass
+                    case MenuStates.Game:
+                        self.draw_background()
+
+                        self.handle_keys()
+
+                        state = self.simulator.get_state()
+                        self.draw(state)
+
+                        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.manager.update(time_delta)
+                pygame.display.flip()
 
             except KeyboardInterrupt:
-                pygame.quit()
                 self.simulator.stop()
+                pygame.quit()
                 sys.exit()
 
+        self.simulator.stop()
         pygame.quit()
+        sys.exit()
diff --git a/overcooked_simulator/gui_2d_vis/pygame_gui_test.py b/overcooked_simulator/gui_2d_vis/pygame_gui_test.py
new file mode 100644
index 00000000..2a59f534
--- /dev/null
+++ b/overcooked_simulator/gui_2d_vis/pygame_gui_test.py
@@ -0,0 +1,38 @@
+import pygame
+import pygame_gui
+
+pygame.init()
+
+pygame.display.set_caption("Quick Start")
+window_surface = pygame.display.set_mode((800, 600))
+
+background = pygame.Surface((800, 600))
+background.fill(pygame.Color("#000000"))
+
+manager = pygame_gui.UIManager((800, 600))
+
+hello_button = pygame_gui.elements.UIButton(
+    relative_rect=pygame.Rect((350, 275), (100, 50)), text="Say Hello", manager=manager
+)
+
+clock = pygame.time.Clock()
+is_running = True
+
+while is_running:
+    time_delta = clock.tick(60) / 1000.0
+    for event in pygame.event.get():
+        if event.type == pygame.QUIT:
+            is_running = False
+
+        if event.type == pygame_gui.UI_BUTTON_PRESSED:
+            if event.ui_element == hello_button:
+                print("Hello World!")
+
+        manager.process_events(event)
+
+    manager.update(time_delta)
+
+    window_surface.blit(background, (0, 0))
+    manager.draw_ui(window_surface)
+
+    pygame.display.update()
diff --git a/overcooked_simulator/pygame_gui/visualization.yaml b/overcooked_simulator/gui_2d_vis/visualization.yaml
similarity index 100%
rename from overcooked_simulator/pygame_gui/visualization.yaml
rename to overcooked_simulator/gui_2d_vis/visualization.yaml
diff --git a/overcooked_simulator/main.py b/overcooked_simulator/main.py
index 239779f4..86caba35 100644
--- a/overcooked_simulator/main.py
+++ b/overcooked_simulator/main.py
@@ -7,7 +7,7 @@ from datetime import datetime
 import pygame
 
 from overcooked_simulator import ROOT_DIR
-from overcooked_simulator.pygame_gui.pygame_gui import PyGameGUI
+from overcooked_simulator.gui_2d_vis.overcooked_gui import PyGameGUI
 from overcooked_simulator.simulation_runner import Simulator
 
 log = logging.getLogger(__name__)
@@ -50,12 +50,9 @@ def main():
         pygame.K_i,
     ]
     keys2 = [pygame.K_a, pygame.K_d, pygame.K_w, pygame.K_s, pygame.K_f, pygame.K_e]
-    gui = PyGameGUI(simulator, [f"p{i}" for i in range(number_player)], [keys1, keys2])
 
-    simulator.start()
+    gui = PyGameGUI(simulator, [f"p{i}" for i in range(number_player)], [keys1, keys2])
     gui.start_pygame()
-
-    simulator.stop()
     sys.exit()
 
 
diff --git a/overcooked_simulator/simulation_runner.py b/overcooked_simulator/simulation_runner.py
index 795b61e6..d479efb8 100644
--- a/overcooked_simulator/simulation_runner.py
+++ b/overcooked_simulator/simulation_runner.py
@@ -26,7 +26,7 @@ class Simulator(Thread):
 
     def __init__(
         self,
-        env_layout_path,
+        env_config_path,
         layout_path,
         frequency: int,
         item_info_path=ROOT_DIR / "game_content" / "item_info.yaml",
@@ -39,7 +39,7 @@ class Simulator(Thread):
         self.step_frequency: int = frequency
         self.preferred_sleep_time_ns: float = 1e9 / self.step_frequency
         self.env: Environment = Environment(
-            env_layout_path, layout_path, item_info_path
+            env_config_path, layout_path, item_info_path
         )
 
         super().__init__()
diff --git a/setup.py b/setup.py
index 69f30ff3..c1b16d40 100644
--- a/setup.py
+++ b/setup.py
@@ -10,7 +10,7 @@ with open("README.md") as readme_file:
 with open("CHANGELOG.md") as history_file:
     history = history_file.read()
 
-requirements = ["numpy", "pygame", "scipy", "pytest>=3", "pyyaml"]
+requirements = ["numpy", "pygame", "scipy", "pytest>=3", "pyyaml", "pygame_gui"]
 
 test_requirements = [
     "pytest>=3",
-- 
GitLab