diff --git a/cooperative_cuisine/pygame_2d_vis/gui.py b/cooperative_cuisine/pygame_2d_vis/gui.py
index 28f154fd2dac1eb4bce8d603868eddc32f42ed13..8c6ad90a0c6f3be86cfd927a170a609a2bf6a418 100644
--- a/cooperative_cuisine/pygame_2d_vis/gui.py
+++ b/cooperative_cuisine/pygame_2d_vis/gui.py
@@ -28,6 +28,7 @@ from cooperative_cuisine.game_server import (
 )
 from cooperative_cuisine.pygame_2d_vis.drawing import Visualizer
 from cooperative_cuisine.pygame_2d_vis.game_colors import colors
+from cooperative_cuisine.server_results import PlayerInfo
 from cooperative_cuisine.state_representation import StateRepresentation
 from cooperative_cuisine.utils import (
     url_and_port_arguments,
@@ -42,7 +43,7 @@ class MenuStates(Enum):
     """Enumeration of "Page" types in the 2D pygame vis."""
 
     Start = "Start"
-    ControllerTutorial = "ControllerTutorial"
+    Tutorial = "Tutorial"
     PreGame = "PreGame"
     Game = "Game"
     PostGame = "PostGame"
@@ -96,11 +97,16 @@ class PlayerKeySet:
         self.joystick = joystick
 
     def set_controlled_players(self, controlled_players: list[str]) -> None:
+        """Sets the controlled players for this keyset.
+        Args:
+            controlled_players: The players controlled by this keyset.
+        """
         self.controlled_players = controlled_players
         self.current_player = self.controlled_players[0]
         self.current_idx = 0
 
     def next_player(self) -> None:
+        """Switches to the next player in the list of controlled players."""
         self.current_idx = (self.current_idx + 1) % len(self.controlled_players)
         if self.other_keyset:
             for ok in self.other_keyset:
@@ -112,6 +118,7 @@ class PlayerKeySet:
     def __repr__(self) -> str:
         return f"Keyset(current={self.current_player}, players={self.controlled_players}, joy={self.joystick})"
 
+
 class PyGameGUI:
     """Visualisation of the overcooked environment and reading keyboard inputs using pygame."""
 
@@ -186,6 +193,8 @@ class PyGameGUI:
             self.window_width_fullscreen /= 2
             self.window_height_fullscreen /= 2
 
+        self.game_width = 0
+        self.game_height = 0
         self.window_width_windowed = self.min_width
         self.window_height_windowed = self.min_height
         self.kitchen_width = 1
@@ -290,7 +299,7 @@ class PyGameGUI:
                 )
                 self.send_action(action)
 
-    def handle_joy_stick_input(self, joysticks):
+    def handle_joy_stick_input(self, joysticks: dict[int, pygame.joystick.Joystick]):
         """Handles joystick inputs for movement every frame
         Args:
             joysticks: list of joysticks
@@ -331,7 +340,7 @@ class PyGameGUI:
                     )
                     self.send_action(action)
 
-    def handle_key_event(self, event):
+    def handle_key_event(self, event: pygame.event.Event):
         """Handles key events for the pickup and interaction keys. Pickup is a single action,
         for interaction keydown and keyup is necessary, because the player has to be able to hold
         the key down.
@@ -361,7 +370,9 @@ class PyGameGUI:
                 if event.type == pygame.KEYDOWN:
                     key_set.next_player()
 
-    def handle_joy_stick_event(self, event, joysticks):
+    def handle_joy_stick_event(
+        self, event: pygame.event.Event, joysticks: dict[int, pygame.joystick.Joystick]
+    ):
         """Handles joy stick events for the pickup and interaction keys. Pickup is a single action,
         for interaction buttondown and buttonup is necessary, because the player has to be able to hold
         the button down.
@@ -403,6 +414,7 @@ class PyGameGUI:
                         key_set.next_player()
 
     def set_window_size(self):
+        """Sets the window size based on fullscreen or not."""
         if self.fullscreen:
             flags = pygame.FULLSCREEN
             self.window_width = self.window_width_fullscreen
@@ -420,12 +432,13 @@ class PyGameGUI:
             flags=flags,
         )
 
-    def reset_window_size(self):
-        self.game_width = 0
-        self.game_height = 0
-        self.set_window_size()
+    def set_game_size(self, max_width: float = None, max_height: float = None):
+        """Sets the game size based on the kitchen size and the current window size.
 
-    def set_game_size(self, max_width=None, max_height=None):
+        Args:
+            max_width: Maximum width of the game screen.
+            max_height: Maximum height of the game screen.
+        """
         if max_width is None:
             max_width = self.window_width - (2 * self.screen_margin)
         if max_height is None:
@@ -466,6 +479,8 @@ class PyGameGUI:
         )
 
     def init_ui_elements(self):
+        """Creates all UI elements. Creates lists of which elements belong on which screen."""
+
         self.manager = pygame_gui.UIManager(
             (self.window_width, self.window_height),
             starting_language=self.language,
@@ -640,9 +655,8 @@ class PyGameGUI:
         )
 
         size = 50
-        add_player_button_rect = pygame.Rect((0, 0), (size, size))
         self.add_human_player_button = pygame_gui.elements.UIButton(
-            relative_rect=add_player_button_rect,
+            relative_rect=pygame.Rect((0, 0), (size, size)),
             text="+",
             manager=self.manager,
             object_id="#quantity_button",
@@ -650,10 +664,10 @@ class PyGameGUI:
             anchors={"left_target": self.added_bots_label, "centery": "centery"},
         )
 
-        remove_player_button_rect = pygame.Rect((0, 0), (size, size))
-        remove_player_button_rect.right = 0
+        rect = pygame.Rect((0, 0), (size, size))
+        rect.right = 0
         self.remove_human_button = pygame_gui.elements.UIButton(
-            relative_rect=remove_player_button_rect,
+            relative_rect=rect,
             text="-",
             manager=self.manager,
             object_id="#quantity_button",
@@ -665,9 +679,8 @@ class PyGameGUI:
             },
         )
 
-        add_bot_button_rect = pygame.Rect((0, 0), (size, size))
         self.add_bot_button = pygame_gui.elements.UIButton(
-            relative_rect=add_bot_button_rect,
+            relative_rect=pygame.Rect((0, 0), (size, size)),
             text="+",
             manager=self.manager,
             object_id="#quantity_button",
@@ -675,10 +688,10 @@ class PyGameGUI:
             anchors={"left_target": self.added_bots_label, "centery": "centery"},
         )
 
-        remove_bot_button_rect = pygame.Rect((0, 0), (size, size))
-        remove_bot_button_rect.right = 0
+        rect = pygame.Rect((0, 0), (size, size))
+        rect.right = 0
         self.remove_bot_button = pygame_gui.elements.UIButton(
-            relative_rect=remove_bot_button_rect,
+            relative_rect=rect,
             text="-",
             manager=self.manager,
             object_id="#quantity_button",
@@ -838,22 +851,6 @@ class PyGameGUI:
         self.orders_container_width = (
             self.window_width - (2 * self.buttons_width) - (self.buttons_width * 0.7)
         )
-        # rect = pygame.Rect(
-        #     0,
-        #     0,
-        #     self.orders_container_width,
-        #     self.screen_margin,
-        # )
-        # self.orders_container = pygame_gui.elements.UIPanel(
-        #     relative_rect=rect,
-        #     manager=self.manager,
-        #     object_id="#graph_container",
-        #     anchors={
-        #         "top": "top",
-        #         "left": "left",
-        #         "left_target": self.orders_label,
-        #     },
-        # )
 
         self.orders_image = pygame_gui.elements.UIImage(
             relative_rect=pygame.Rect(
@@ -1075,7 +1072,12 @@ class PyGameGUI:
             final_text_container,
         ]
 
-    def show_screen_elements(self, elements: list):
+    def show_screen_elements(self, elements: list[pygame_gui.core.UIElement]):
+        """Hides all UI elements and shows the elements in the list and elements in self.on_all_screens.
+
+        Args:
+            elements: List of UI elements to show.
+        """
         all_elements = (
             self.start_screen_elements
             + self.tutorial_screen_elements
@@ -1091,7 +1093,8 @@ class PyGameGUI:
         for element in elements + self.on_all_screens:
             element.show()
 
-    def update_tutorial_screen(self):
+    def setup_tutorial_screen(self):
+        """Updates the tutorial screen with the current tutorial image and the continue button."""
         self.show_screen_elements(self.tutorial_screen_elements)
 
         self.set_game_size(
@@ -1115,19 +1118,17 @@ class PyGameGUI:
             grid_size=self.window_height / 18,
         )
         self.tutorial_graph_image.set_image(tutorial_graph_surface)
-        # self.tutorial_graph_image.set_dimensions((self.game_width, self.game_height))
 
     def update_screen_elements(self):
+        """Shows and hides the UI elements based on the current menu state."""
         match self.menu_state:
             case MenuStates.Start:
                 self.show_screen_elements(self.start_screen_elements)
-
                 if self.CONNECT_WITH_STUDY_SERVER:
                     self.bot_number_container.hide()
-
                 self.update_selection_elements()
-            case MenuStates.ControllerTutorial:
-                self.update_tutorial_screen()
+            case MenuStates.Tutorial:
+                self.setup_tutorial_screen()
             case MenuStates.PreGame:
                 self.init_ui_elements()
                 self.show_screen_elements(self.pregame_screen_elements)
@@ -1137,7 +1138,7 @@ class PyGameGUI:
             case MenuStates.PostGame:
                 self.init_ui_elements()
                 self.show_screen_elements(self.postgame_screen_elements)
-                self.update_postgame_screen(self.last_state)
+                self.update_post_game_screen(self.last_state)
                 if self.last_level:
                     self.next_game_button.hide()
                     self.finish_study_button.show()
@@ -1148,37 +1149,27 @@ class PyGameGUI:
                 self.show_screen_elements(self.end_screen_elements)
 
     def draw_main_window(self):
+        """Main draw function. Draws the main window and updates the UI.
+        Draws the game screen in Game and ControllerTutorial screen."""
         self.main_window.fill(
             colors[self.visualization_config["GameWindow"]["background_color"]]
         )
 
-        match self.menu_state:
-            case MenuStates.ControllerTutorial:
-                self.draw_tutorial_screen_frame()
-            case MenuStates.Game:
-                self.draw_game_screen_frame()
+        if self.menu_state == MenuStates.Tutorial:
+            self.draw_game_screen_frame(tutorial=True)
+        elif self.menu_state == MenuStates.Game:
+            self.draw_game_screen_frame(tutorial=False)
 
         self.manager.draw_ui(self.main_window)
         self.manager.update(self.time_delta)
         pygame.display.flip()
 
-    def draw_tutorial_screen_frame(self):
-        self.handle_keys()
-        self.handle_joy_stick_input(joysticks=self.joysticks)
-
-        state = self.request_state()
-        self.vis.draw_gamescreen(
-            self.game_screen,
-            state,
-            self.grid_size,
-            [int(k.current_player) for k in self.key_sets],
-        )
-
-        game_screen_rect = self.game_screen.get_rect()
-        game_screen_rect.center = self.game_center
-        self.main_window.blit(self.game_screen, game_screen_rect)
+    def update_post_game_screen(self, state: dict):
+        """Updates the post game screen with the final score and the completed meals.
 
-    def update_postgame_screen(self, state):
+        Args:
+            state: The game state returned by the environment, containing served meals and score.
+        """
         score = state["score"]
         # self.score_conclusion.set_text(f"Your final score is {score}. Hurray!")
         self.score_conclusion.set_text(
@@ -1310,50 +1301,23 @@ class PyGameGUI:
         )
 
     def exit_game(self):
+        """Exits the game."""
         self.menu_state = MenuStates.PostGame
 
         if self.CONNECT_WITH_STUDY_SERVER:
             self.send_level_done()
         self.disconnect_websockets()
 
-        self.update_postgame_screen(self.last_state)
+        self.update_post_game_screen(self.last_state)
         self.update_screen_elements()
         self.beeped_once = False
 
-    def draw_game_screen_frame(self):
-        self.last_state = self.request_state()
-
-        self.handle_keys()
-        self.handle_joy_stick_input(joysticks=self.joysticks)
-
-        if not self.beeped_once and self.last_state["all_players_ready"]:
-            self.beeped_once = True
-            self.play_bell_sound()
-
-        if self.last_state["ended"]:
-            self.exit_game()
-
-        else:
-            self.draw_game(self.last_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)
-
-            if not self.last_state["all_players_ready"]:
-                self.wait_players_label.show()
-            else:
-                self.wait_players_label.hide()
-
-    def draw_game(self, state):
+    def draw_game(self, state: dict):
         """Main visualization function.
 
-        Args:            state: The game state returned by the environment."""
+        Args:
+            state: The game state returned by the environment.
+        """
         self.vis.draw_gamescreen(
             self.game_screen,
             state,
@@ -1361,7 +1325,6 @@ class PyGameGUI:
             [int(k.current_player) for k in self.key_sets],
         )
 
-        # orders_surface = pygame.Surface((self.orders_container_width, self.screen_margin))
         self.vis.draw_orders(
             screen=self.orders_image.image,
             state=state,
@@ -1370,7 +1333,6 @@ class PyGameGUI:
             height=self.screen_margin,
             config=self.visualization_config,
         )
-        # self.orders_image.set_image(orders_surface)
 
         border = self.visualization_config["GameWindow"]["game_border_size"]
         border_rect = pygame.Rect(
@@ -1407,13 +1369,69 @@ class PyGameGUI:
                     ),
                 )
 
-    def update_score_label(self, state):
+    def draw_game_screen_frame(self, tutorial: bool = False):
+        """Main visualization function for the game screen.
+
+        Args:
+            tutorial: If True, the tutorial screen is drawn, which is a simplified version of the game screen.
+        """
+        self.last_state = self.request_state()
+
+        self.handle_keys()
+        self.handle_joy_stick_input(joysticks=self.joysticks)
+
+        if tutorial:
+            self.vis.draw_gamescreen(
+                self.game_screen,
+                self.last_state,
+                self.grid_size,
+                [int(k.current_player) for k in self.key_sets],
+            )
+            game_screen_rect = self.game_screen.get_rect()
+            game_screen_rect.center = self.game_center
+            self.main_window.blit(self.game_screen, game_screen_rect)
+            return
+        else:
+            if not self.beeped_once and self.last_state["all_players_ready"]:
+                self.beeped_once = True
+                self.play_bell_sound()
+
+            if self.last_state["ended"]:
+                self.exit_game()
+
+            else:
+                self.draw_game(self.last_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)
+
+                if not self.last_state["all_players_ready"]:
+                    self.wait_players_label.show()
+                else:
+                    self.wait_players_label.hide()
+
+    def update_score_label(self, state: dict):
+        """Updates the score label.
+
+        Args:
+            state: The game state returned by the environment.
+
+        """
         score = state["score"]
         self.score_label.set_text(
             "translations.score", text_kwargs={"score": str(score)}
         )
 
     def update_remaining_time(self, remaining_time: float):
+        """Updates the remaining time label.
+
+        Args:
+            remaining_time: The remaining time in seconds.
+        """
         hours, rem = divmod(int(remaining_time), 3600)
         minutes, seconds = divmod(rem, 60)
         display_time = f"{minutes}:{'%02d' % seconds}"
@@ -1421,7 +1439,12 @@ class PyGameGUI:
             "translations.time_remaining", text_kwargs={"time": display_time}
         )
 
-    def create_env_on_game_server(self, tutorial):
+    def create_env_on_game_server(self, tutorial: bool):
+        """Starts an environment on the game server.
+
+        Args:
+            tutorial: If True, a tutorial environment is created. Else a normal game environment is created.
+        """
         if tutorial:
             layout_path = ROOT_DIR / "configs" / "layouts" / "tutorial.layout"
             environment_config_path = ROOT_DIR / "configs" / "tutorial_env_config.yaml"
@@ -1486,6 +1509,7 @@ class PyGameGUI:
         self.level_info["number_players"] = num_players
 
     def update_pregame_screen(self):
+        """Updates the pregame screen. Possible recipes in the level are displayed."""
         self.level_name_label.set_text(
             "translations.level_name", text_kwargs={"level": self.level_info["name"]}
         )
@@ -1584,6 +1608,8 @@ class PyGameGUI:
         )
 
     def setup_tutorial(self):
+        """Sets up the tutorial environment. This includes creating the environment
+        on the game server, and setting up the connection."""
         answer = requests.post(
             f"{self.request_url}/connect_to_tutorial/{self.participant_id}"
         )
@@ -1603,7 +1629,9 @@ class PyGameGUI:
             log.warning("Could not create tutorial.")
 
     def get_game_connection(self):
-        if self.menu_state == MenuStates.ControllerTutorial:
+        """Sets up a connection to the game server.
+        This includes getting the player info, level info and player keys"""
+        if self.menu_state == MenuStates.Tutorial:
             self.setup_tutorial()
             self.key_sets = self.setup_player_keys(["0"], 1, False)
             self.vis.create_player_colors(1)
@@ -1634,10 +1662,17 @@ class PyGameGUI:
             )
         self.player_ids = list(self.player_info.keys())
 
-    def create_and_connect_bot(self, player_id, player_info):
+    def create_and_connect_bot(self, player_id: str, player_info: PlayerInfo):
+        """Creates a bot process and connects it to the game server.
+
+        Args:
+            player_id: The id/name of the player.
+            player_info: Player info containing client_id, player_hash player_id and websocket_url.
+        """
         player_hash = player_info["player_hash"]
         print(
-            f'--general_plus="agent_websocket:{player_info["websocket_url"]};player_hash:{player_hash};agent_id:{player_id}"'
+            f'--general_plus="agent_websocket:{player_info["websocket_url"]};'
+            f'player_hash:{player_hash};agent_id:{player_id}"'
         )
         if self.USE_AAAMBOS_AGENT:
             sub = Popen(
@@ -1650,7 +1685,8 @@ class PyGameGUI:
                         str(ROOT_DIR / "configs" / "agents" / "arch_config.yml"),
                         "--run_config",
                         str(ROOT_DIR / "configs" / "agents" / "run_config.yml"),
-                        f'--general_plus="agent_websocket:{player_info["websocket_url"]};player_hash:{player_hash};agent_id:{player_id}"',
+                        f'--general_plus="agent_websocket:{player_info["websocket_url"]};'
+                        f'player_hash:{player_hash};agent_id:{player_id}"',
                         f"--instance={player_hash}",
                     ]
                 ),
@@ -1672,6 +1708,8 @@ class PyGameGUI:
         self.sub_processes.append(sub)
 
     def connect_websockets(self):
+        """Connects the websockets of the players to the game server.
+        If the player is a bot, a bot process is created"""
         for p, (player_id, player_info) in enumerate(self.player_info.items()):
             if p < self.number_humans_to_be_added:
                 # add player websockets
@@ -1688,18 +1726,23 @@ class PyGameGUI:
                 self.websockets[player_id] = websocket
 
             else:
-                # create bots and add bot websockets
                 self.create_and_connect_bot(player_id, player_info)
 
             if p == 0:
                 self.state_player_id = player_id
 
     def setup_game(self):
+        """Sets up prerequisites for the game. This includes connecting the websockets, creating player colors
+        in the vis and setting the kitchen size."""
         self.connect_websockets()
         self.vis.create_player_colors(self.level_info["number_players"])
         self.kitchen_width, self.kitchen_height = self.level_info["kitchen_size"]
 
     def stop_game_on_server(self, reason: str) -> None:
+        """Stops the game directly on the game server.
+        Args:
+            reason: The reason for stopping the game.
+        """
         log.debug(f"Stopping game: {reason}")
         if not self.CONNECT_WITH_STUDY_SERVER:
             answer = requests.post(
@@ -1718,11 +1761,14 @@ class PyGameGUI:
                 )
 
     def send_tutorial_finished(self):
+        """Signals the study server that the tutorial was finished."""
         requests.post(
             f"{self.request_url}/disconnect_from_tutorial/{self.participant_id}",
         )
 
     def finished_button_press(self):
+        """Gets called when the finished button is pressed.
+        Stops the game on the game server if the study server is not used."""
         if not self.CONNECT_WITH_STUDY_SERVER:
             self.stop_game_on_server("finished_button_pressed")
         self.menu_state = MenuStates.PostGame
@@ -1730,6 +1776,7 @@ class PyGameGUI:
         self.update_screen_elements()
 
     def fullscreen_button_press(self):
+        """Toggles between fullscreen and windowed mode."""
         self.fullscreen = not self.fullscreen
         self.set_window_size()
         self.init_ui_elements()
@@ -1737,6 +1784,7 @@ class PyGameGUI:
         self.update_screen_elements()
 
     def reset_gui_values(self):
+        """Reset the values of the GUI elements to their default values. Default values are defined here."""
         self.currently_controlled_player_idx = 0
         self.number_humans_to_be_added = 1
         self.number_bots_to_be_added = 0
@@ -1747,6 +1795,8 @@ class PyGameGUI:
         self.multiple_keysets = False
 
     def update_selection_elements(self):
+        """Updates the selection elements of the GUI. This includes the number of players,
+        the number of bots, the split players button and the multiple keysets button."""
         if self.number_humans_to_be_added == 1:
             self.remove_human_button.disable()
             self.multiple_keysets_button.hide()
@@ -1821,6 +1871,7 @@ class PyGameGUI:
         self.websockets[action.player].recv()
 
     def request_state(self):
+        """Requests the current state of the game environment from the game server."""
         message_dict = {
             "type": PlayerRequestType.GET_STATE.value,
             "action": None,
@@ -1831,7 +1882,19 @@ class PyGameGUI:
         state = json.loads(self.websockets[self.state_player_id].recv())
         return state
 
+    def exit_tutorial(self):
+        """Exits the tutorial. Disconnects the websockets and signals the study server that the tutorial was
+        finished if the study server is used. Otherwise, the game is stopped on the game server.
+        """
+        self.disconnect_websockets()
+        self.menu_state = MenuStates.PreGame
+        if self.CONNECT_WITH_STUDY_SERVER:
+            self.send_tutorial_finished()
+        else:
+            self.stop_game_on_server("tutorial_finished")
+
     def disconnect_websockets(self):
+        """Disconnects the websockets. Kills all subprocesses that are running bots."""
         for sub in self.sub_processes:
             try:
                 if self.USE_AAAMBOS_AGENT:
@@ -1850,15 +1913,18 @@ class PyGameGUI:
         for websocket in self.websockets.values():
             websocket.close()
 
-    def play_bell_sound(self):
+    @staticmethod
+    def play_bell_sound():
+        """Plays a bell sound when the game starts."""
         bell_path = str(ROOT_DIR / "pygame_2d_vis" / "sync_bell.wav")
         mixer.init()
         mixer.music.load(bell_path)
-        mixer.music.set_volume(0.9)
+        mixer.music.set_volume(0.7)
         mixer.music.play()
         log.log(logging.INFO, "Started game, played bell sound")
 
     def start_study(self):
+        """Starts the study on the study server."""
         answer = requests.post(
             f"{self.request_url}/start_study/{self.participant_id}/{self.number_humans_to_be_added}"
         )
@@ -1874,6 +1940,7 @@ class PyGameGUI:
             )
 
     def send_level_done(self):
+        """Sends a message to the study server that the level was finished."""
         answer = requests.post(f"{self.request_url}/level_done/{self.participant_id}")
         if answer.status_code != 200:
             log.warning(
@@ -1883,22 +1950,24 @@ class PyGameGUI:
             )
 
     def button_continue_postgame_pressed(self):
+        """Handles the continue button press on the postgame screen. If the study server is used, the connection to
+        a new game is set up. Otherwise, the start screen is shown.
+        """
         if self.CONNECT_WITH_STUDY_SERVER:
             if not self.last_level:
                 self.get_game_connection()
         else:
-            # self.current_layout_idx += 1
             self.menu_state = MenuStates.Start
             return
-            # self.create_env_on_game_server(tutorial=False)
-            # if self.current_layout_idx == len(self.layout_file_paths) - 1:
-            #     self.last_level = True
-            # else:
-            #     log.debug(f"LEVEL: {self.layout_file_paths[self.current_layout_idx]}")
-
         self.menu_state = MenuStates.PreGame
 
     def manage_button_event(self, button: pygame_gui.core.UIElement | None = None):
+        """Manages the button events. The button events are filtered by the current menu state and the button that
+        was pressed.
+
+        Args:
+            button: The button that was pressed.
+        """
         if button == self.quit_button:
             if self.fullscreen:
                 self.fullscreen_button_press()
@@ -1926,7 +1995,7 @@ class PyGameGUI:
                         ):
                             pass
                         else:
-                            self.menu_state = MenuStates.ControllerTutorial
+                            self.menu_state = MenuStates.Tutorial
                             if self.CONNECT_WITH_STUDY_SERVER:
                                 self.get_game_connection()
                             else:
@@ -1954,7 +2023,7 @@ class PyGameGUI:
 
             ############################################
 
-            case MenuStates.ControllerTutorial:
+            case MenuStates.Tutorial:
                 self.exit_tutorial()
                 if self.CONNECT_WITH_STUDY_SERVER:
                     self.start_study()
@@ -1984,6 +2053,89 @@ class PyGameGUI:
 
             ############################################
 
+    def add_joystick(self, event: pygame.event.Event):
+        """Adds a joystick to the list of joysticks and assigns it to a key set.
+        A pygame.JOYDEVICEADDED event is generated for every joystick connected at the start of the program.
+
+        Args:
+            event: The event that is triggered when a joystick is connected.
+        """
+        joy = pygame.joystick.Joystick(event.device_index)
+        self.joysticks[joy.get_instance_id()] = joy
+
+        for key_set in self.key_sets:
+            if key_set.joystick is None:
+                key_set.joystick = joy.get_instance_id()
+                break
+        log.debug(f"Joystick {joy.get_instance_id()} connected")
+
+    def remove_joystick(self, event: pygame.event.Event):
+        """Removes a joystick from the list of joysticks and unassigns it from a key set.
+
+        Args:
+            event: The event that is triggered when a joystick is disconnected.
+        """
+        del self.joysticks[event.instance_id]
+        for key_set in self.key_sets:
+            if key_set.joystick == event.instance_id:
+                key_set.joystick = None
+        log.debug(f"Joystick {event.instance_id} disconnected")
+        log.debug(f"Number of joysticks:" + str(pygame.joystick.get_count()))
+
+    def process_gui_event(self, event: pygame.event.Event):
+        """Processes the pygame events. The events are filtered by their type and the current menu state.
+
+        Args:
+            event: The pygame event that is processed.
+        """
+        if event.type == pygame.QUIT:
+            self.disconnect_websockets()
+            self.running = False
+
+        if event.type == pygame.JOYDEVICEADDED and pygame.joystick.get_count() > 0:
+            self.add_joystick(event)
+        if event.type == pygame.JOYDEVICEREMOVED:
+            self.remove_joystick(event)
+
+        # Press enter key or controller start button instead of mouse button press
+        if (
+            event.type == pygame.JOYBUTTONDOWN
+            and any([joy.get_button(7) for joy in self.joysticks.values()])
+            or (event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN)
+        ):
+            if self.menu_state == MenuStates.Start:
+                self.manage_button_event(self.start_button)
+                self.update_screen_elements()
+
+            elif self.menu_state in [
+                MenuStates.Tutorial,
+                MenuStates.PreGame,
+                MenuStates.PostGame,
+            ]:
+                self.manage_button_event(self.continue_button)
+                self.update_screen_elements()
+
+        if event.type == pygame_gui.UI_BUTTON_PRESSED:
+            button = event.ui_element
+            self.manage_button_event(button)
+            if button in [
+                self.start_button,
+                self.continue_button,
+                self.finish_study_button,
+                self.next_game_button,
+            ]:
+                self.update_screen_elements()
+            elif self.menu_state == MenuStates.Start:
+                self.update_selection_elements()
+
+        if self.menu_state in [MenuStates.Game, MenuStates.Tutorial]:
+            if event.type in [pygame.KEYDOWN, pygame.KEYUP]:
+                self.handle_key_event(event)
+            if event.type in [pygame.JOYBUTTONDOWN, pygame.JOYBUTTONUP]:
+                self.handle_joy_stick_event(event, joysticks=self.joysticks)
+
+        self.manager.process_events(event)
+
     def start_pygame(self):
         """Starts pygame and the gui loop. Each frame the game state is visualized and keyboard inputs are read."""
         log.debug(f"Starting pygame gui at {self.FPS} fps")
@@ -1994,17 +2146,14 @@ class PyGameGUI:
 
         clock = pygame.time.Clock()
 
-        self.reset_window_size()
+        self.set_window_size()
         self.init_ui_elements()
-        self.reset_window_size()
         self.reset_gui_values()
         self.update_screen_elements()
 
         # Game loop
         self.running = True
-        # This dict can be left as-is, since pygame will generate a
-        # pygame.JOYDEVICEADDED event for every joystick connected
-        # at the start of the program.
+        # pygame.JOYDEVICEADDED event is generated for every joystick connected at the start of the program.
         self.joysticks = {}
 
         while self.running:
@@ -2013,120 +2162,22 @@ class PyGameGUI:
 
                 # PROCESSING EVENTS
                 for event in pygame.event.get():
-                    if event.type == pygame.QUIT:
-                        self.disconnect_websockets()
-                        self.running = False
-
-                    # connect joystick
-                    if (
-                        pygame.joystick.get_count() > 0
-                        and event.type == pygame.JOYDEVICEADDED
-                    ):
-                        # This event will be generated when the program starts for every
-                        # joystick, filling up the list without needing to create them manually.
-                        joy = pygame.joystick.Joystick(event.device_index)
-                        self.joysticks[joy.get_instance_id()] = joy
-
-                        for key_set in self.key_sets:
-                            if key_set.joystick is None:
-                                key_set.joystick = joy.get_instance_id()
-                                break
-                        log.debug(f"Joystick {joy.get_instance_id()} connected")
-
-                    # disconnect joystick
-                    if event.type == pygame.JOYDEVICEREMOVED:
-                        del self.joysticks[event.instance_id]
-                        for key_set in self.key_sets:
-                            if key_set.joystick == event.instance_id:
-                                key_set.joystick = None
-                        log.debug(f"Joystick {event.instance_id} disconnected")
-                        log.debug(f"Number of joysticks:"+str(pygame.joystick.get_count()))
-
-                    # Press enter key or controller start button instead of mouse button press
-                    if (
-                        event.type == pygame.JOYBUTTONDOWN
-                        and any(
-                            [
-                                joy.get_button(7) for joy in self.joysticks.values()
-                            ]
-                        )
-                        or (
-                            event.type == pygame.KEYDOWN
-                            and event.key == pygame.K_RETURN
-                        )
-                    ):
-                        if self.menu_state == MenuStates.Start:
-                            self.manage_button_event(self.start_button)
-                            self.update_screen_elements()
-
-                        elif self.menu_state in [
-                            MenuStates.ControllerTutorial,
-                            MenuStates.PreGame,
-                            MenuStates.PostGame,
-                        ]:
-                            self.manage_button_event(self.continue_button)
-                            self.update_screen_elements()
-
-                    if event.type == pygame_gui.UI_BUTTON_PRESSED:
-                        button = event.ui_element
-                        self.manage_button_event(button)
-                        if button in [
-                            self.start_button,
-                            self.continue_button,
-                            self.finish_study_button,
-                            self.next_game_button,
-                        ]:
-                            self.update_screen_elements()
-                        elif self.menu_state == MenuStates.Start:
-                            self.update_selection_elements()
-                    if event.type in [
-                        pygame.KEYDOWN,
-                        pygame.KEYUP,
-                    ] and self.menu_state in [
-                        MenuStates.Game,
-                        MenuStates.ControllerTutorial,
-                    ]:
-                        self.handle_key_event(event)
-
-                    if event.type in [
-                        pygame.JOYBUTTONDOWN,
-                        pygame.JOYBUTTONUP,
-                    ] and self.menu_state in [
-                        MenuStates.Game,
-                        MenuStates.ControllerTutorial,
-                    ]:
-                        self.handle_joy_stick_event(event, joysticks=self.joysticks)
-
-                    self.manager.process_events(event)
+                    self.process_gui_event(event)
 
                 # DRAWING
                 self.draw_main_window()
 
             except (KeyboardInterrupt, SystemExit):
                 self.running = False
-                self.disconnect_websockets()
-                if not self.CONNECT_WITH_STUDY_SERVER:
-                    self.stop_game_on_server("Program exited.")
-                if self.fullscreen_button:
-                    self.fullscreen_button_press()
 
         self.disconnect_websockets()
         if not self.CONNECT_WITH_STUDY_SERVER:
             self.stop_game_on_server("Program exited.")
-
         if self.fullscreen:
             self.fullscreen_button_press()
         pygame.quit()
         sys.exit()
 
-    def exit_tutorial(self):
-        self.disconnect_websockets()
-        self.menu_state = MenuStates.PreGame
-        if self.CONNECT_WITH_STUDY_SERVER:
-            self.send_tutorial_finished()
-        else:
-            self.stop_game_on_server("tutorial_finished")
-
 
 def main(
     study_url: str,
@@ -2156,7 +2207,8 @@ if __name__ == "__main__":
     parser = argparse.ArgumentParser(
         prog="Cooperative Cuisine 2D PyGame Visualization",
         description="PyGameGUI: a PyGame 2D Visualization window.",
-        epilog="For further information, see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html",
+        epilog="For further information, "
+        "see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html",
     )
 
     url_and_port_arguments(parser)
@@ -2172,4 +2224,4 @@ if __name__ == "__main__":
         args.manager_ids,
         CONNECT_WITH_STUDY_SERVER=True,
         debug=args.do_study,
-    )
\ No newline at end of file
+    )