diff --git a/overcooked_simulator/example_study_server.py b/overcooked_simulator/example_study_server.py index 4b7c82f45ed4a2885a59e52c66a5abfaf3151386..d6538fa83bcc02aa11e297bb0723b6ddbe780913 100644 --- a/overcooked_simulator/example_study_server.py +++ b/overcooked_simulator/example_study_server.py @@ -28,6 +28,7 @@ from fastapi import FastAPI from overcooked_simulator import ROOT_DIR from overcooked_simulator.game_server import CreateEnvironmentConfig +from overcooked_simulator.overcooked_environment import EnvironmentConfig from overcooked_simulator.server_results import PlayerInfo from overcooked_simulator.utils import ( url_and_port_arguments, @@ -57,6 +58,12 @@ class LevelConfig(TypedDict): item_info_path: str +class LevelInfo(TypedDict): + name: str + last_level: bool + recipes: list[str] + + class StudyConfig(TypedDict): levels: list[LevelConfig] num_players: int @@ -103,9 +110,8 @@ class StudyState: def can_add_participant(self, num_participants: int) -> bool: filled = self.num_connected_players + num_participants <= self.study_config["num_players"] - print(self.num_connected_players, num_participants, self.study_config["num_players"]) - print("CAN ADD", self, filled) return filled and not self.is_full + def create_env(self, level): with open(ROOT_DIR / "game_content" / level["item_info_path"], "r") as file: item_info = file.read() @@ -115,7 +121,9 @@ class StudyState: layout = file.read() with open(ROOT_DIR / "game_content" / level["config_path"], "r") as file: environment_config = file.read() - + self.current_config: EnvironmentConfig = yaml.load( + environment_config, Loader=yaml.Loader + ) creation_json = CreateEnvironmentConfig( manager_id=server_manager_id, number_players=self.study_config["num_players"] + self.study_config["num_bots"], @@ -138,7 +146,6 @@ class StudyState: for idx, (player_id, player_info) in enumerate(player_info.items()): if idx >= self.study_config["num_players"]: self.create_and_connect_bot(player_id, player_info) - return env_info def start(self): @@ -183,7 +190,14 @@ class StudyState: def get_connection(self, participant_id: str): player_info = self.participant_id_to_player_info[participant_id] - return player_info, self.last_level + mock_recipes = ["Onion?", "Tomato?", "HAMBURGER!"] + current_level = self.levels[self.current_level_idx] + if self.current_config["meals"]["all"]: + return ["all"] + else: + recipes = self.current_config["meals"]["list"] + level_info = LevelInfo(name=current_level["name"], last_level=self.last_level, recipes=recipes) + return player_info, level_info def create_and_connect_bot(self, player_id, player_info): player_hash = player_info["player_hash"] @@ -265,11 +279,8 @@ class StudyManager: if not self.running_studies or all([not s.can_add_participant(number_players) for s in self.running_studies]): self.create_study() - print("all", self.running_studies) - for study in self.running_studies: if study.can_add_participant(number_players): - print("connect", study) player_info = study.add_participant(participant_id, number_players) self.participant_id_to_study_map[participant_id] = study return player_info @@ -279,10 +290,9 @@ class StudyManager: assigned_study.player_finished_level(participant_id) def get_participant_game_connection(self, participant_id: str): - print(self.participant_id_to_study_map) assigned_study = self.participant_id_to_study_map[participant_id] - player_info, last_level = assigned_study.get_connection(participant_id) - return player_info, last_level + player_info, level_info = assigned_study.get_connection(participant_id) + return player_info, level_info study_manager = StudyManager() @@ -290,7 +300,6 @@ study_manager = StudyManager() @app.post("/start_study/{participant_id}/{number_players}") async def start_study(participant_id: str, number_players: int): - print("NUMBER PLAYERS TO BE ADDED", number_players) player_info = study_manager.add_participant(participant_id, number_players) return player_info @@ -302,10 +311,10 @@ async def level_done(participant_id: str): @app.post("/get_game_connection/{participant_id}") async def get_game_connection(participant_id: str): - player_info, last_level = study_manager.get_participant_game_connection( + player_info, level_info = study_manager.get_participant_game_connection( participant_id ) - return {"player_info": player_info, "last_level": last_level} + return {"player_info": player_info, "level_info": level_info} @@ -332,7 +341,6 @@ async def want_to_play_tutorial(participant_id: str): seed=1234567890, ).model_dump(mode="json") # todo async - print("CREATING TUTORIAL ENVIRONMENT") env_info = requests.post( game_server_url + "/manage/create_env/", json=creation_json ) diff --git a/overcooked_simulator/game_content/study/level1/level1_config.yaml b/overcooked_simulator/game_content/study/level1/level1_config.yaml index ce382a5edcb7a62b255a96d6be20426147740590..b1cab18a7b15997c588bf9ffb05baca55cd9e902 100644 --- a/overcooked_simulator/game_content/study/level1/level1_config.yaml +++ b/overcooked_simulator/game_content/study/level1/level1_config.yaml @@ -5,7 +5,7 @@ plates: # range of seconds until the dirty plate arrives. game: - time_limit_seconds: 10 + time_limit_seconds: 20 meals: all: False diff --git a/overcooked_simulator/game_content/study/level2/level2_config.yaml b/overcooked_simulator/game_content/study/level2/level2_config.yaml index bea2049e75c04407198c5395859be5bb8021c513..e9eb286d48e27c3f76ea2e8e2f19ee3996ca167d 100644 --- a/overcooked_simulator/game_content/study/level2/level2_config.yaml +++ b/overcooked_simulator/game_content/study/level2/level2_config.yaml @@ -5,7 +5,7 @@ plates: # range of seconds until the dirty plate arrives. game: - time_limit_seconds: 300 + time_limit_seconds: 5 meals: all: False @@ -98,8 +98,8 @@ player_config: player_speed_units_per_seconds: 8 interaction_range: 1.6 restricted_view: True - view_angle: 50 - view_range: 5 # in grid units, can be "null" + view_angle: 90 + view_range: 3.5 # in grid units, can be "null" effect_manager: FireManager: diff --git a/overcooked_simulator/game_content/study/study_config.yaml b/overcooked_simulator/game_content/study/study_config.yaml index 5df5da4388422285a42e1e22ce1371171c6df60e..51e89ad042770901b589e7afefd5de8276a6ca28 100644 --- a/overcooked_simulator/game_content/study/study_config.yaml +++ b/overcooked_simulator/game_content/study/study_config.yaml @@ -3,10 +3,10 @@ levels: - # - config_path: study/level1/level1_config.yaml - # layout_path: overcooked-1/1-1-far-apart.layout - # item_info_path: study/level1/level1_item_info.yaml - # name: "Level 1-1: Far Apart" + - config_path: study/level1/level1_config.yaml + layout_path: overcooked-1/1-1-far-apart.layout + item_info_path: study/level1/level1_item_info.yaml + name: "Level 1-1: Far Apart" - config_path: study/level2/level2_config.yaml layout_path: overcooked-1/1-4-bottleneck.layout @@ -14,5 +14,5 @@ levels: name: "Level 1-4: Bottleneck" -num_players: 2 +num_players: 1 num_bots: 0 diff --git a/overcooked_simulator/gui_2d_vis/gui_theme.json b/overcooked_simulator/gui_2d_vis/gui_theme.json index 24d6386e11db7011bc512f63ed2d43e05ea74227..55195b18cf90a08e451030fdb45db83deb8c0778 100644 --- a/overcooked_simulator/gui_2d_vis/gui_theme.json +++ b/overcooked_simulator/gui_2d_vis/gui_theme.json @@ -166,5 +166,25 @@ "size": 45, "bold": 1 } + }, + "#level_name": { + "font": { + "size": 35, + "bold": 1, + "colour": "#000000" + } + }, + "#recipe": { + "font": { + "size": 20, + "bold": 1, + "colour": "#000000" + } + }, + "#recipes_container": { + "colours": { + "dark_bg": "#ffffff", + "normal_border": "#fffacd" + } } } diff --git a/overcooked_simulator/gui_2d_vis/overcooked_gui.py b/overcooked_simulator/gui_2d_vis/overcooked_gui.py index cf00dd78bfea8bfc32f46114e45f479240ab4764..510ce83a3b2baa2849997ccee63b9854cf0a159d 100644 --- a/overcooked_simulator/gui_2d_vis/overcooked_gui.py +++ b/overcooked_simulator/gui_2d_vis/overcooked_gui.py @@ -152,9 +152,9 @@ class PyGameGUI: self.window_height_fullscreen, ) = pygame.display.get_desktop_sizes()[0] - # if self.window_width_fullscreen >= 3840 and self.window_height_fullscreen >= 2160: - # self.window_width_fullscreen /= 2 - # self.window_height_fullscreen /= 2 + if self.window_width_fullscreen >= 3840 and self.window_height_fullscreen >= 2160: + self.window_width_fullscreen /= 2 + self.window_height_fullscreen /= 2 self.window_width_windowed = self.min_width self.window_height_windowed = self.min_height @@ -610,7 +610,6 @@ class PyGameGUI: anchors={"top": "top", "left": "left"}, ) img_width = self.window_width * 0.8 - # img_width = img_height * (image_rect.width / image_rect.height) img_height = img_width * (image_rect.height / image_rect.width) new_dims = (img_width, img_height) self.tutorial_image.set_dimensions(new_dims) @@ -640,33 +639,21 @@ class PyGameGUI: # PreGame screen ######################################################################## - image = pygame.image.load( - ROOT_DIR / "gui_2d_vis" / "tutorial_files" / "recipe_mock.png" - ).convert_alpha() - image_rect = image.get_rect() - image_rect.top = 50 - self.recipe_image = pygame_gui.elements.UIImage( - image_rect, - image, - manager=self.manager, - anchors={"centerx": "centerx", "top": "top"}, - ) - img_height = self.window_height * 0.7 - img_width = img_height * (image_rect.width / image_rect.height) - new_dims = (img_width, img_height) - self.recipe_image.set_dimensions(new_dims) - - self.level_name = pygame_gui.elements.UILabel( - text=f"Next level: {self.layout_file_paths[self.current_layout_idx].stem}", + self.level_name_label = pygame_gui.elements.UILabel( + text=f"not set", relative_rect=pygame.Rect( (0, 0), (self.window_width * 0.7, self.window_height * 0.2), ), manager=self.manager, - object_id="#score_label", + object_id="#level_name", anchors={"centerx": "centerx", "top": "top"}, ) + self.all_recipes_labels = [] + self.last_recipes_labels = [] + + ######################################################################## # Game screen ######################################################################## @@ -743,7 +730,7 @@ class PyGameGUI: anchors={"centerx": "centerx", "top": "top"}, ) - next_game_button_rect = pygame.Rect((0, 0), (190, 50)) + next_game_button_rect = pygame.Rect((0, 0), (220, 80)) next_game_button_rect.center = (self.buttons_width // 2, 200) self.next_game_button = pygame_gui.elements.UIButton( relative_rect=next_game_button_rect, @@ -753,18 +740,8 @@ class PyGameGUI: object_id="#split_players_button", ) - retry_button_rect = pygame.Rect((0, 0), (190, 50)) - retry_button_rect.center = (self.buttons_width // 2 - 200, 200) - self.retry_button = pygame_gui.elements.UIButton( - relative_rect=retry_button_rect, - manager=self.manager, - text="Retry last game", - anchors={"center": "center"}, - object_id="#split_players_button", - ) - - finish_study_rect = pygame.Rect((0, 0), (190, 50)) - finish_study_rect.center = (self.buttons_width // 2 + 200, 200) + finish_study_rect = pygame.Rect((0, 0), (220, 80)) + finish_study_rect.center = (self.buttons_width // 2, 200) self.finish_study_button = pygame_gui.elements.UIButton( relative_rect=finish_study_rect, manager=self.manager, @@ -796,7 +773,7 @@ class PyGameGUI: self.fullscreen_button, self.player_selection_container, self.bot_number_container, - self.press_a_image, + self.press_a_image ] self.tutorial_screen_elements = [ @@ -807,10 +784,9 @@ class PyGameGUI: ] self.pregame_screen_elements = [ - self.recipe_image, - self.level_name, + # self.recipes_container, + self.level_name_label, self.quit_button, - self.press_a_image, self.continue_button, ] @@ -838,7 +814,6 @@ class PyGameGUI: self.rest = [ self.fullscreen_button, self.quit_button, - self.retry_button, self.finished_button, ] @@ -851,11 +826,13 @@ class PyGameGUI: + self.postgame_screen_elements + self.end_screen_elements + self.rest + + self.all_recipes_labels ): element.hide() for element in elements: element.show() + def update_screen_elements(self): match self.menu_state: case MenuStates.Start: @@ -867,7 +844,13 @@ class PyGameGUI: self.update_selection_elements() case MenuStates.ControllerTutorial: self.show_screen_elements(self.tutorial_screen_elements) + + if self.CONNECT_WITH_STUDY_SERVER: + self.get_game_connection(tutorial=True) + else: + self.create_env_on_game_server(tutorial=True) self.setup_game(tutorial=True) + self.set_game_size( max_height=self.window_height * 0.3, max_width=self.window_width * 0.3, @@ -878,6 +861,8 @@ class PyGameGUI: ) case MenuStates.PreGame: self.show_screen_elements(self.pregame_screen_elements) + for el in self.last_recipes_labels: + el.show() case MenuStates.Game: self.show_screen_elements(self.game_screen_elements) case MenuStates.PostGame: @@ -1080,6 +1065,41 @@ class PyGameGUI: if tutorial: self.player_ids = [str(list(self.player_info.keys())[0])] + + def set_level_info_screen(self): + self.level_name_label.set_text(f"{self.level_info['name']}") + + self.last_recipes_labels = [] + height = 30 + width = 300 + for idx, recipe in enumerate(self.level_info["recipes"]): + if idx == 0: + recipe_label = pygame_gui.elements.UILabel( + text=recipe, + relative_rect=pygame.Rect( + (0, 0), + (width, height), + ), + manager=self.manager, + # container=self.recipes_container, + object_id="#recipe", + anchors={"centerx": "centerx", "top_target": self.level_name_label}, + ) + else: + recipe_label = pygame_gui.elements.UILabel( + text=recipe, + relative_rect=pygame.Rect( + (0, 0), + (width, height), + ), + manager=self.manager, + # container=self.recipes_container, + object_id="#recipe", + anchors={"centerx": "centerx", "top_target": self.last_recipes_labels[idx-1]}, + ) + self.last_recipes_labels.append(recipe_label) + self.all_recipes_labels.append(recipe_label) + def get_game_connection(self, tutorial): if self.menu_state == MenuStates.ControllerTutorial: self.player_info = requests.post( @@ -1092,7 +1112,11 @@ class PyGameGUI: f"http://localhost:8080/get_game_connection/{self.participant_id}" ).json() self.player_info = answer["player_info"] - self.last_level = answer["last_level"] + self.level_info = answer["level_info"] + self.last_level = self.level_info["last_level"] + self.set_level_info_screen() + + print("LEVEL_INFO", self.level_info) if tutorial: self.key_sets = self.setup_player_keys(["0"], 1, False) @@ -1162,7 +1186,6 @@ class PyGameGUI: for p, (player_id, player_info) in enumerate(self.player_info.items()): if p < self.number_humans_to_be_added: # add player websockets - print(player_info) websocket = connect(self.websocket_url + player_info["client_id"]) websocket.send( json.dumps( @@ -1182,10 +1205,6 @@ class PyGameGUI: self.state_player_id = player_id def setup_game(self, tutorial=False): - if self.CONNECT_WITH_STUDY_SERVER: - self.get_game_connection(tutorial) - else: - self.create_env_on_game_server(tutorial) self.connect_websockets() @@ -1357,6 +1376,12 @@ class PyGameGUI: self.last_level = True else: log.debug(f"LEVEL: {self.layout_file_paths[self.current_layout_idx]}") + else: + if not self.last_level: + if self.CONNECT_WITH_STUDY_SERVER: + self.get_game_connection(tutorial=False) + else: + self.create_env_on_game_server(tutorial=False) self.menu_state = MenuStates.PreGame def manage_button_event(self, event): @@ -1423,6 +1448,7 @@ class PyGameGUI: match event.ui_element: case self.continue_button: self.setup_game() + self.set_game_size() self.menu_state = MenuStates.Game @@ -1443,14 +1469,11 @@ class PyGameGUI: case MenuStates.PostGame: match event.ui_element: - case self.retry_button: - if not self.CONNECT_WITH_STUDY_SERVER: - self.stop_game("Retry button") - self.menu_state = MenuStates.PreGame case self.next_game_button: self.button_continue_postgame_pressed() + case self.finish_study_button: self.menu_state = MenuStates.End @@ -1533,6 +1556,7 @@ class PyGameGUI: self.update_screen_elements() case MenuStates.PreGame: self.setup_game() + self.set_game_size() self.menu_state = MenuStates.Game self.update_screen_elements() @@ -1585,8 +1609,12 @@ class PyGameGUI: if self.CONNECT_WITH_STUDY_SERVER: self.send_tutorial_finished() self.start_study() + self.get_game_connection(tutorial=False) + else: self.stop_game("tutorial_finished") + self.create_env_on_game_server(tutorial=False) + self.disconnect_websockets()