diff --git a/CHANGELOG.md b/CHANGELOG.md index ed94902e28ac695f216e31d825221807c49bbf36..ec679566d9e8b19dafd0afbada2f84c0a4999689 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,9 +26,16 @@ - Scoring optionally dependent on remaining times of orders - Score of order is shown in game - Possible max score is shown next to recipe graphs +- Control screenshot, replay, single game/study server, gui via cli (sub commands) ### Changed +- cli control changed `cooperative_cuisine` is now `cooperative-cuisine` (`-` instead of `_`) +- cli now uses commands. Use `cooperative-cuisine start` now for the same behaviour as before (add arguments). +- `cooperative-cuisine` with arguments does not work anymore. The "no arguments" call results in running with defaults. + If you want to add arguments use the `start` subcommand +- `cooperative-cuisine -h` provides an overview of all sub commands. For individual help for each coomand, + run `cooperative-cuisine COMMAND -h`. - `extra_setup_functions` now only contain hook callback classes (`hook_callbacks`). Therefore, the `func` and `kwargs` fields were removed and the kwargs are now the standard values for defined hooks. This reduced the complexity of the config. diff --git a/README.md b/README.md index a4d18ba3c632c9023e719086b710e7636be44ee3..209683444740c4abdef7d1c8551fc1351e1609ca 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ pip install -e . Run it via the command line (in your pyenv/conda environment): ```bash -cooperative_cuisine -s localhost -sp 8080 -g localhost -gp 8000 +cooperative-cuisine start -s localhost -sp 8080 -g localhost -gp 8000 ``` *The arguments shown are the defaults.* @@ -47,11 +47,11 @@ You can also start the **Game Server**m **Study Server** (Matchmaking),and the * terminals. ```bash -python3 cooperative_cuisine/game_server.py -g localhost -gp 8000 --manager-ids SECRETKEY1 SECRETKEY2 +cooperative-cuisine game-server -g localhost -gp 8000 --manager-ids SECRETKEY1 SECRETKEY2 -python3 cooperative_cuisine/study_server.py -s localhost -sp 8080 --manager-ids SECRETKEY1 +cooperative-cuisine study-server -s localhost -sp 8080 -g localhost -gp 8000 --manager-ids SECRETKEY1 -python3 cooperative_cuisine/pygame_2d_vis/gui.py -s localhost -sp 8080 -g localhost -gp 8000 +cooperative-cuisine gui -s localhost -sp 8080 -g localhost -gp 8000 ``` You can start also several GUIs. The study server does the matchmaking. diff --git a/cooperative_cuisine/__init__.py b/cooperative_cuisine/__init__.py index 17dfc078daff4bf21f66270c4c72fa6d2c460cbf..e30632759d68efe1389179f31f912fecd9ac26f7 100644 --- a/cooperative_cuisine/__init__.py +++ b/cooperative_cuisine/__init__.py @@ -21,7 +21,7 @@ like a "real", cooperative, human partner. # Installation -You need a Python **3.10** or newer codna environment. +You need a Python **3.10** or newer conda environment. ```bash conda install -c conda-forge pygraphviz pip install cooperative_cuisine@git+https://gitlab.ub.uni-bielefeld.de/scs/cocosy/overcooked-simulator@main @@ -43,7 +43,7 @@ options. Run it via the command line (in your pyenv/conda environment): ```bash -cooperative_cuisine -s localhost -sp 8080 -g localhost -gp 8000 +cooperative-cuisine start -s localhost -sp 8080 -g localhost -gp 8000 ``` *The arguments shown are the defaults.* @@ -51,13 +51,15 @@ cooperative_cuisine -s localhost -sp 8080 -g localhost -gp 8000 You can also start the **Game Server**, **Study Server** (Matchmaking),and the **PyGame GUI** individually in different terminals. ```bash -python3 cooperative_cuisine/game_server.py -g localhost -gp 8000 --manager-ids SECRETKEY1 SECRETKEY2 +cooperative-cuisine game-server -g localhost -gp 8000 --manager-ids SECRETKEY1 SECRETKEY2 -python3 cooperative_cuisine/study_server.py -s localhost -sp 8080 --manager-ids SECRETKEY1 +cooperative-cuisine study-server -s localhost -sp 8080 -g localhost -gp 8000 --manager-ids SECRETKEY1 -python3 cooperative_cuisine/pygame_2d_vis/gui.py -s localhost -sp 8080 -g localhost -gp 8000 +cooperative-cuisine gui -s localhost -sp 8080 ``` +For more CLI arguments and a description of possible arguments, run `cooperative-cuisine -h` + ## Connect with agent and receive game state Or you start a game server, create an environment and connect each player/agent via a websocket connection. @@ -210,7 +212,7 @@ returns. You might have stored some json states and now you want to visualize them via the pygame-2d visualization. You can do that by running the `drawing.py` script and referencing a json file. ```bash -python3 cooperative_cuisine/pygame_2d_vis/drawing.py --state my_state.json +cooperative-cuisine screenshot --state my_state.json ``` - You can specify a different visualization config with `-v` or `--visualization_config`. - You can specify the name of the output file with `-o` or `--output_file`. The default is `screenshot.jpg`. @@ -370,6 +372,7 @@ num_bots: 0 The API documentation follows the file and content structure in the repo. On the left you can find the navigation panel that brings you to the implementation of - the **action**s the player can perform, +- the **argument parser** definitions for the cli, - the **counter factory** converts the characters in the layout file to counter instances, - the **counters**, including the kitchen utility objects like dispenser, cooking counter (stove, deep fryer, oven), sink, etc., diff --git a/cooperative_cuisine/__main__.py b/cooperative_cuisine/__main__.py index 685d2402c807800d377b561eaef4c1010009aead..7d63c878ff435371ddcf344f476cf9bc34e89130 100644 --- a/cooperative_cuisine/__main__.py +++ b/cooperative_cuisine/__main__.py @@ -2,13 +2,15 @@ import argparse import time from multiprocessing import Process -from cooperative_cuisine.utils import ( - url_and_port_arguments, - disable_websocket_logging_arguments, - add_list_of_manager_ids_arguments, - add_study_arguments, - add_gui_arguments, +from cooperative_cuisine.argument_parser import ( + create_screenshot_parser, + create_study_server_parser, + create_game_server_parser, + create_normal_parser, + create_gui_parser, + create_server_parser, ) +from cooperative_cuisine.pygame_2d_vis.video_replay import create_replay_parser USE_STUDY_SERVER = True @@ -29,6 +31,7 @@ def start_study_server(cli_args): game_port=cli_args.game_port, manager_ids=cli_args.manager_ids, study_config_path=cli_args.study_config, + use_ssl=cli_args.ssl, ) @@ -41,31 +44,18 @@ def start_pygame_gui(cli_args): cli_args.game_url, cli_args.game_port, cli_args.manager_ids, - CONNECT_WITH_STUDY_SERVER=USE_STUDY_SERVER, - debug=cli_args.do_study, + debug=cli_args.debug, + do_study=cli_args.do_study, + use_ssl=cli_args.ssl, ) -def main(cli_args=None): - study_server = None - - parser = argparse.ArgumentParser( - prog="Cooperative Cuisine", - description="Game Engine Server + PyGameGUI: Starts overcooked game engine server and a PyGame 2D Visualization window in two processes.", - epilog="For further information, see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html", - ) - url_and_port_arguments(parser) - disable_websocket_logging_arguments(parser) - add_list_of_manager_ids_arguments(parser) - add_study_arguments(parser) - add_gui_arguments(parser) - - cli_args = parser.parse_args() - +def start_server_and_gui(cli_args, no_gui=False): + study_sever = None game_server = None pygame_gui = None try: - if USE_STUDY_SERVER: + if no_gui or cli_args.do_study or not cli_args.debug: print("Start study server:") study_server = Process(target=start_study_server, args=(cli_args,)) study_server.start() @@ -75,38 +65,21 @@ def main(cli_args=None): game_server = Process(target=start_game_server, args=(cli_args,)) game_server.start() time.sleep(0.5) + if no_gui: + while True: + time.sleep(1) + else: + print("Start PyGame GUI 1:") + pygame_gui = Process(target=start_pygame_gui, args=(cli_args,)) + pygame_gui.start() - print("Start PyGame GUI 1:") - pygame_gui = Process(target=start_pygame_gui, args=(cli_args,)) - pygame_gui.start() - - if USE_STUDY_SERVER: - pass - # print("Start PyGame GUI 2:") - # pygame_gui_2 = Process(target=start_pygame_gui, args=(cli_args,)) - # pygame_gui_2.start() - # # # - # print("Start PyGame GUI 3:") - # pygame_gui_3 = Process(target=start_pygame_gui, args=(cli_args,)) - # pygame_gui_3.start() - # - # print("Start PyGame GUI 4:") - # pygame_gui_4 = Process(target=start_pygame_gui, args=(cli_args,)) - # pygame_gui_4.start() - # while ( - # pygame_gui.is_alive() - # and pygame_gui_2.is_alive() - # and pygame_gui_3.is_alive() - # ): - # time.sleep(1) - - while pygame_gui.is_alive(): - time.sleep(1) + while pygame_gui.is_alive(): + time.sleep(1) except KeyboardInterrupt: print("Received Keyboard interrupt") finally: - if USE_STUDY_SERVER and study_server is not None and study_server.is_alive(): + if study_server is not None and study_server.is_alive(): print("Terminate study server") study_server.terminate() if game_server is not None and game_server.is_alive(): @@ -118,5 +91,88 @@ def main(cli_args=None): time.sleep(0.1) +def main(cli_args=None): + parser = argparse.ArgumentParser( + prog="Cooperative Cuisine", + description="CLI control of cooperative cuisine. Start server, guis, execute utility code, etc. Use sub " + "commands to configure the execution. No commands or arguments results in starting a study " + "server, game server and a gui.", + epilog="For further information, see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html", + ) + + subparsers = parser.add_subparsers( + help="Available CLI of Cooperative Cuisine", dest="command" + ) + + normal_parser = subparsers.add_parser( + "start", + help="Start a game server, study server and a PyGame 2D Visualization.", + ) + create_normal_parser(normal_parser) + + study_server_parser = subparsers.add_parser( + "study-server", help="Start a study server.", aliases=["study"] + ) + create_study_server_parser(study_server_parser) + + game_server_parser = subparsers.add_parser( + "game-server", help="Start a game server.", aliases=["game"] + ) + create_game_server_parser(game_server_parser) + + gui_parser = subparsers.add_parser( + "gui", help="Start a PyGame 2D Visualization (a client).", aliases=["client"] + ) + create_gui_parser(gui_parser) + + screenshot_parser = subparsers.add_parser( + "screenshot", help="Create a screenshot from a json state.", aliases=["image"] + ) + create_screenshot_parser(screenshot_parser) + + replay_parser = subparsers.add_parser( + "replay", + help="Create replay from json states or recordings.", + aliases=["video", "video-replay"], + ) + create_replay_parser(replay_parser) + + server_parser = subparsers.add_parser( + "server", + help="Start game and study server.", + aliases=["game-and-study", "study-and-game"], + ) + create_server_parser(server_parser) + + cli_args = parser.parse_args() + + if cli_args.command: + match cli_args.command: + case "screenshot" | "image": + from cooperative_cuisine.pygame_2d_vis.drawing import main + + main(cli_args) + case "study-server" | "study": + start_study_server(cli_args) + case "game-server" | "game": + start_game_server(cli_args) + case "replay" | "video" | "video-replay": + from cooperative_cuisine.pygame_2d_vis.video_replay import main + + main(cli_args) + case "start": + start_server_and_gui(cli_args) + case "gui" | "client": + start_pygame_gui(cli_args) + case "server" | "game-and-study" | "study-and-game": + start_server_and_gui(cli_args, no_gui=True) + case _: + parser.print_help() + else: + default_parser = argparse.ArgumentParser() + create_normal_parser(default_parser) + start_server_and_gui(default_parser.parse_args()) + + if __name__ == "__main__": main() diff --git a/cooperative_cuisine/argument_parser.py b/cooperative_cuisine/argument_parser.py new file mode 100644 index 0000000000000000000000000000000000000000..f6bc86eb06d4c011239af26b467f2ea42c3541a6 --- /dev/null +++ b/cooperative_cuisine/argument_parser.py @@ -0,0 +1,359 @@ +import uuid +from argparse import ArgumentParser, FileType + +from cooperative_cuisine import ROOT_DIR + +DEFAULT_SERVER_URL = "localhost" +"""Default server URL of game and server study.""" + +DEFAULT_SERVER_PORT = 8080 +"""Default study server port.""" + +DEFAULT_GAME_PORT = 8000 +"""Default game server port.""" + + +def study_server_arguments( + parser: ArgumentParser, + default_study_port=DEFAULT_SERVER_PORT, + default_server_url=DEFAULT_SERVER_URL, +): + """Add arguments for study server ip and port. + + Args: + parser: the parser to add the arguments. + default_study_port: the default port of the study server `DEFAULT_STUDY_PORT` -> 8080 + default_server_url: the default ip/url/host of the study server `DEFAULT_SERVER_URL` -> `localhost` + """ + parser.add_argument( + "-s", + "--study-url", + "--study-host", + type=str, + default=default_server_url, + help=f"Overcooked Study Server host url.", + ) + parser.add_argument( + "-p", + "--study-port", + type=int, + default=default_study_port, + help=f"Port number for the Study Server", + ) + + +def game_server_arguments( + parser: ArgumentParser, + default_game_port=DEFAULT_GAME_PORT, + default_server_url=DEFAULT_SERVER_URL, +): + """Add arguments for game server ip and port. + + Args: + parser: the parser to add the arguments. + default_game_port: the default port of the game server `DEFAULT_GAME_PORT` -> 8000 + default_server_url: the default ip/url/host of the game server `DEFAULT_SERVER_URL` -> `localhost` + """ + parser.add_argument( + "-g", + "--game-url", + "--game-host", + type=str, + default=default_server_url, + help=f"Overcooked Game Server url.", + ) + parser.add_argument( + "-gp", + "--game-port", + type=int, + default=default_game_port, + help=f"Port number for the Game Server", + ) + + +def disable_websocket_logging_arguments(parser): + """Disables the logging of WebSocket arguments in the provided parser. + + Args: + parser: The argument parser object (argparse.ArgumentParser) to which the + "--enable-websocket-logging" argument will be added. + + """ + parser.add_argument( + "--enable-websocket-logging" "", action="store_true", default=True + ) + + +def add_list_of_manager_ids_arguments(parser): + """This function adds the manager ids argument to the given argument parser. + + Args: + parser: An ArgumentParser object used to parse command line arguments. + + Returns: + None + """ + parser.add_argument( + "-m", + "--manager-ids", + nargs="+", + type=str, + default=[uuid.uuid4().hex], + help="List of manager IDs that can create environments.", + ) + + +def add_study_arguments(parser): + """This function adds the study configuration argument to the given argument parser. + + Args: + parser (argparse.ArgumentParser): The argument parser object. + + + Example: + ```python + import argparse + parser = argparse.ArgumentParser() + add_study_arguments(parser) + ``` + """ + parser.add_argument( + "--study-config", + type=str, + default=ROOT_DIR / "configs" / "study" / "study_config.yaml", + help="The config of the study.", + ) + + +def add_gui_arguments(parser): + """Adds the gui debug argument to the given argument parser. + If set, additional debug / admin elements are shown. + + Args: + parser (argparse.ArgumentParser): The argument parser object. + + + Example: + ```python + import argparse + parser = argparse.ArgumentParser() + add_gui_arguments(parser) + ``` + """ + parser.add_argument( + "--do-study", + help="Start GUI in Fullscreen and do not show configuration and quit buttons.", + action="store_true", + ) + parser.add_argument( + "--debug", + help="Debug GUI. No need for a study server. Select layouts in GUI. You need to specify a manager id for the game server.", + action="store_true", + ) + + +def ssl_argument(parser: ArgumentParser): + """Add the ssl argument to a parser. + + Args: + parser: the parser to add the argument. + """ + parser.add_argument( + "--ssl", + action="store_true", + help="Use SSL certificate. Connect to https and wss.", + ) + + +def visualization_config_argument(parser: ArgumentParser): + """Add the visualization config argument to a parser. + + Args: + parser: the parser to add the argument. + """ + parser.add_argument( + "-v", + "--visualization-config", + type=FileType("r", encoding="UTF-8"), + default=ROOT_DIR / "pygame_2d_vis" / "visualization.yaml", + ) + + +def output_file_argument(parser: ArgumentParser, default_file: str): + """Add the output file argument to a parser. + + Args: + parser: the parser to add the argument. + default_file: the default file name (relative location) + """ + parser.add_argument( + "-o", + "--output-file", + type=str, + default=default_file, + ) + + +def create_replay_parser(parser: ArgumentParser): + """Add individually arguments for parsers that use the replay function of cooperative cuisine. + + Args: + parser (ArgumentParser): The argument parser object. + """ + parser.add_argument("-j", "--json_state", help="Json states file path", type=str) + visualization_config_argument(parser) + parser.add_argument( + "-o", + "--output", + type=str, + default="<json_state_name>", + ) + parser.add_argument( + "-p", + "--player-id", + type=str, + default=None, + help="Render view for specific player", + ) + parser.add_argument( + "-g", + "--grid-size", + type=int, + default=40, + help="Number pixel for one cell in the grid.", + ) + parser.add_argument( + "-a", + "--action-recording", + type=str, + default=None, + help="The path to the action recording", + ) + parser.add_argument( + "-e", + "--env-configs", + type=str, + default=None, + help="The path to the environment config logs", + ) + parser.add_argument( + "-s", + "--step-duration", + type=float, + default=1 / 200, + help="Step duration in seconds between environment steps.", + ) + parser.add_argument( + "-f", + "--fps", + type=int, + default=24, + help="Frames per second to render images from the environment", + ) + parser.add_argument( + "-d", "--display", action="store_true", help="Show generated images." + ) + parser.add_argument( + "-n", + "--number-player", + type=int, + default=1, + help="Number of player to visualize. Should be the same as played the game.", + ) + parser.add_argument( + "-b", + "--break-when-no-action", + action="store_true", + help="Stop rendering when no more actions are available.", + ) + parser.add_argument( + "--video", + "--video-source", + type=str, + help="Create a video from a folder full of images.", + ) + + +def create_screenshot_parser(parser: ArgumentParser): + """Add cli arguments for running only the screenshot generation. + + Args: + parser (ArgumentParser): The argument parser object. + """ + parser.add_argument( + "-s", + "--state", + type=FileType("r", encoding="UTF-8"), + default=ROOT_DIR / "pygame_2d_vis" / "sample_state.json", + ) + visualization_config_argument(parser) + output_file_argument(parser, ROOT_DIR / "generated" / "screenshot.jpg") + + +def create_game_server_parser(parser: ArgumentParser): + """Add cli arguments for running only a game server. For the game-server subcommand. + + Args: + parser (ArgumentParser): The argument parser object. + """ + game_server_arguments(parser) + disable_websocket_logging_arguments(parser) + add_list_of_manager_ids_arguments(parser) + ssl_argument(parser) + + +def create_study_server_parser(parser: ArgumentParser): + """Add cli arguments for running only a study server. For the study-server subcommand. + + Args: + parser (ArgumentParser): The argument parser object. + """ + study_server_arguments(parser) + # TODO study server can handle several game server + game_server_arguments(parser) + add_list_of_manager_ids_arguments(parser=parser) + add_study_arguments(parser=parser) + ssl_argument(parser) + + +def create_gui_parser(parser: ArgumentParser): + """Add cli arguments for running only a gui instance. For the gui / client subcommand. + + Args: + parser (ArgumentParser): The argument parser object. + """ + study_server_arguments(parser) + disable_websocket_logging_arguments(parser) + add_list_of_manager_ids_arguments(parser) + add_gui_arguments(parser) + ssl_argument(parser) + game_server_arguments(parser) + + +def create_normal_parser(parser: ArgumentParser): + """Add cli arguments for running the servers and one gui instance. For the start subcommand. + + Args: + parser (ArgumentParser): The argument parser object. + """ + game_server_arguments(parser) + study_server_arguments(parser) + disable_websocket_logging_arguments(parser) + add_list_of_manager_ids_arguments(parser) + add_gui_arguments(parser) + add_study_arguments(parser) + ssl_argument(parser) + + +def create_server_parser(parser: ArgumentParser): + """Add cli arguments for running the servers (game and study but no gui). For the server subcommand. + + Args: + parser (ArgumentParser): The argument parser object. + """ + game_server_arguments(parser) + study_server_arguments(parser) + disable_websocket_logging_arguments(parser) + ssl_argument(parser) + add_list_of_manager_ids_arguments(parser) + add_study_arguments(parser) diff --git a/cooperative_cuisine/configs/study/study_config.yaml b/cooperative_cuisine/configs/study/study_config.yaml index b50ac573c318683646d62cedbc5ce247d60c603d..d491af8801982c1acb218c46c6b8483aaad2610e 100644 --- a/cooperative_cuisine/configs/study/study_config.yaml +++ b/cooperative_cuisine/configs/study/study_config.yaml @@ -187,3 +187,5 @@ levels: num_players: 1 num_bots: 0 + +study_log_path: USER_LOG_DIR/ENV_NAME/ \ No newline at end of file diff --git a/cooperative_cuisine/game_server.py b/cooperative_cuisine/game_server.py index 5b5d2fa38a7aebebb906db5ffcc7f1551988607b..e565cbe91a1652af87d51cbc3b7de066e8903700 100644 --- a/cooperative_cuisine/game_server.py +++ b/cooperative_cuisine/game_server.py @@ -30,6 +30,7 @@ from starlette.websockets import WebSocketDisconnect from typing_extensions import TypedDict from cooperative_cuisine.action import Action +from cooperative_cuisine.argument_parser import create_game_server_parser from cooperative_cuisine.environment import Environment from cooperative_cuisine.server_results import ( CreateEnvResult, @@ -37,9 +38,6 @@ from cooperative_cuisine.server_results import ( PlayerRequestResult, ) from cooperative_cuisine.utils import ( - url_and_port_arguments, - add_list_of_manager_ids_arguments, - disable_websocket_logging_arguments, setup_logging, UUID_CUTOFF, ) @@ -831,6 +829,7 @@ async def websocket_player_endpoint(websocket: WebSocket, client_id: str): def main( host: str, port: int, manager_ids: list[str], enable_websocket_logging: bool = False ): + print("Manager IDs:", manager_ids) setup_logging(enable_websocket_logging) loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) @@ -849,10 +848,7 @@ if __name__ == "__main__": epilog="For further information, see " "https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/cooperative_cuisine.html", ) - - url_and_port_arguments(parser) - disable_websocket_logging_arguments(parser) - add_list_of_manager_ids_arguments(parser) + create_game_server_parser(parser) args = parser.parse_args() main(args.game_url, args.game_port, args.manager_ids, args.enable_websocket_logging) """ diff --git a/cooperative_cuisine/pygame_2d_vis/drawing.py b/cooperative_cuisine/pygame_2d_vis/drawing.py index 96b7e3b0119db7d0eabd74c0f98598f454b7b533..d69b12eebbadf21f1ee144d00521a61cf01c2229 100644 --- a/cooperative_cuisine/pygame_2d_vis/drawing.py +++ b/cooperative_cuisine/pygame_2d_vis/drawing.py @@ -2,7 +2,6 @@ import argparse import colorsys import json import os -import sys from datetime import datetime, timedelta from pathlib import Path @@ -13,6 +12,7 @@ import yaml from scipy.spatial import KDTree from cooperative_cuisine import ROOT_DIR +from cooperative_cuisine.argument_parser import create_screenshot_parser from cooperative_cuisine.environment import Environment from cooperative_cuisine.pygame_2d_vis.game_colors import colors, RGB from cooperative_cuisine.state_representation import ( @@ -1038,7 +1038,7 @@ def generate_recipe_images(config: dict, folder_path: str | Path): pygame.image.save(screen, f"{folder_path}/{graph_dict['meal']}.png") -def main(args): +def main(cli_args): """Runs the Cooperative Cuisine Image Generation process. This method takes command line arguments to specify the state file, visualization configuration file, and output @@ -1052,37 +1052,21 @@ def main(args): -o, --output_file: A command line argument of type `str`. Specifies the output file path for the generated image. If not provided, the default value is 'ROOT_DIR / "generated" / "screenshot.jpg"'. """ - parser = argparse.ArgumentParser( - prog="Cooperative Cuisine Image Generation", - description="Generate images for a state in json.", - epilog="For further information, see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html", - ) - parser.add_argument( - "-s", - "--state", - type=argparse.FileType("r", encoding="UTF-8"), - default=ROOT_DIR / "pygame_2d_vis" / "sample_state.json", - ) - parser.add_argument( - "-v", - "--visualization_config", - type=argparse.FileType("r", encoding="UTF-8"), - default=ROOT_DIR / "pygame_2d_vis" / "visualization.yaml", - ) - parser.add_argument( - "-o", - "--output_file", - type=str, - default=ROOT_DIR / "generated" / "screenshot.jpg", - ) - args = parser.parse_args(args) - with open(args.visualization_config, "r") as f: + + with open(cli_args.visualization_config, "r") as f: viz_config = yaml.safe_load(f) - with open(args.state, "r") as f: + with open(cli_args.state, "r") as f: state = json.load(f) - save_screenshot(state, viz_config, args.output_file) - generate_recipe_images(viz_config, args.output_file.parent) + save_screenshot(state, viz_config, cli_args.output_file) + generate_recipe_images(viz_config, cli_args.output_file.parent) if __name__ == "__main__": - main(sys.argv[1:]) + parser = argparse.ArgumentParser( + prog="Cooperative Cuisine Image Generation", + description="Generate images for a state in json.", + epilog="For further information, see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html", + ) + create_screenshot_parser(parser) + args = parser.parse_args() + main(args) diff --git a/cooperative_cuisine/pygame_2d_vis/gui.py b/cooperative_cuisine/pygame_2d_vis/gui.py index 735505e3713df64a5fef5957e280973a74ef355f..4ad01fc36073e8402dc4c0fe45842dd045e8a048 100644 --- a/cooperative_cuisine/pygame_2d_vis/gui.py +++ b/cooperative_cuisine/pygame_2d_vis/gui.py @@ -21,6 +21,7 @@ from websockets.sync.client import connect from cooperative_cuisine import ROOT_DIR from cooperative_cuisine.action import ActionType, InterActionData, Action +from cooperative_cuisine.argument_parser import create_gui_parser from cooperative_cuisine.game_server import ( CreateEnvironmentConfig, WebsocketMessage, @@ -31,11 +32,7 @@ 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, - disable_websocket_logging_arguments, - add_list_of_manager_ids_arguments, setup_logging, - add_gui_arguments, ) @@ -129,14 +126,14 @@ class PyGameGUI: game_host: str, game_port: int, manager_ids: list[str], - CONNECT_WITH_STUDY_SERVER: bool, USE_AAAMBOS_AGENT: bool, debug: bool, + do_study: bool, + use_ssl: bool, ): - self.CONNECT_WITH_STUDY_SERVER = CONNECT_WITH_STUDY_SERVER self.USE_AAAMBOS_AGENT = USE_AAAMBOS_AGENT - - self.show_debug_elements = debug + self.show_debug_elements = not do_study + self.CONNECT_WITH_STUDY_SERVER = not debug pygame.init() pygame.display.set_icon( @@ -152,10 +149,12 @@ class PyGameGUI: self.websockets = {} - if CONNECT_WITH_STUDY_SERVER: - self.request_url = f"http://{study_host}:{study_port}" + if self.CONNECT_WITH_STUDY_SERVER: + self.request_url = ( + f"http{'s' if use_ssl else ''}://{study_host}:{study_port}" + ) else: - self.request_url = f"http://{game_host}:{game_port}" + self.request_url = f"http{'s' if use_ssl else ''}://{game_host}:{game_port}" self.manager_id = random.choice(manager_ids) @@ -206,7 +205,7 @@ class PyGameGUI: self.switch_score_color: bool = False self.count_frames_score_label: int = 0 - self.fullscreen = False if self.show_debug_elements else True + self.fullscreen = do_study self.menu_state = MenuStates.Start self.manager: pygame_gui.UIManager @@ -2240,9 +2239,10 @@ def main( game_url: str, game_port: int, manager_ids: list[str], - CONNECT_WITH_STUDY_SERVER=False, USE_AAAMBOS_AGENT=False, debug=False, + do_study=False, + use_ssl=False, ): setup_logging() gui = PyGameGUI( @@ -2251,9 +2251,10 @@ def main( game_host=game_url, game_port=game_port, manager_ids=manager_ids, - CONNECT_WITH_STUDY_SERVER=CONNECT_WITH_STUDY_SERVER, USE_AAAMBOS_AGENT=USE_AAAMBOS_AGENT, debug=debug, + do_study=do_study, + use_ssl=use_ssl, ) gui.start_pygame() @@ -2265,11 +2266,7 @@ if __name__ == "__main__": epilog="For further information, " "see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html", ) - - url_and_port_arguments(parser) - disable_websocket_logging_arguments(parser) - add_list_of_manager_ids_arguments(parser) - add_gui_arguments(parser) + create_gui_parser(parser) args = parser.parse_args() main( args.study_url, @@ -2277,6 +2274,7 @@ if __name__ == "__main__": args.game_url, args.game_port, args.manager_ids, - CONNECT_WITH_STUDY_SERVER=True, - debug=args.do_study, + debug=args.debug, + do_study=args.do_study, + use_ssl=args.ssl, ) diff --git a/cooperative_cuisine/pygame_2d_vis/video_replay.py b/cooperative_cuisine/pygame_2d_vis/video_replay.py index 46521e12524e1c0c2b2074cc5b1f68486d354364..56ee373993d524a001271cb46bc2f455d28e7e47 100644 --- a/cooperative_cuisine/pygame_2d_vis/video_replay.py +++ b/cooperative_cuisine/pygame_2d_vis/video_replay.py @@ -7,29 +7,28 @@ based on the actions. Until now, we did not find any deviations from the json st # CLI Sequence of images replay from actions: ```bash -python video_replay.py -a ~/.local/state/cooperative_cuisine/log/ENV_NAME/actions.jsonl -e ~/.local/state/cooperative_cuisine/log/ENV_NAME/env_configs.jsonl -d -n 2 -p "0" +cooperative-cuisine replay -a ~/.local/state/cooperative_cuisine/log/ENV_NAME/actions.jsonl -e ~/.local/state/cooperative_cuisine/log/ENV_NAME/env_configs.jsonl -d -n 2 -p "0" ``` Sequence of images replay from json states: ```bash -python video_replay.py -j ~/.local/state/cooperative_cuisine/log/ENV_NAME/json_states.jsonl -d -p "0" +cooperative-cuisine replay -j ~/.local/state/cooperative_cuisine/log/ENV_NAME/json_states.jsonl -d -p "0" ``` The `display` (`-d`, `--display`) requires `opencv-python` (cv2) installed. (`pip install opencv-python`) Generate a video file from images (requires also `opencv-python`): ```bash -python video_replay.py --video ~/.local/state/cooperative_cuisine/log/ENV_NAME/DIR_NAME_WITH_IMAGES +cooperative-cuisine replay --video ~/.local/state/cooperative_cuisine/log/ENV_NAME/DIR_NAME_WITH_IMAGES ``` For additional CLI arguments: ```bash -python video_replay.py -h +cooperative-cuisine replay -h ``` # Code Documentation """ -import argparse import json import os import os.path @@ -42,8 +41,10 @@ import yaml from PIL import Image from tqdm import tqdm -from cooperative_cuisine import ROOT_DIR from cooperative_cuisine.action import Action +from cooperative_cuisine.argument_parser import ( + create_replay_parser, +) from cooperative_cuisine.environment import Environment from cooperative_cuisine.pygame_2d_vis.drawing import Visualizer from cooperative_cuisine.recording import FileRecorder @@ -297,127 +298,63 @@ def video_from_images(image_paths, video_name, fps): print("See:", video_name) -if __name__ == "__main__": - parser = ArgumentParser( - prog="Cooperative Cuisine Video Generation", - description="Generate videos from recorded data.", - epilog="For further information, see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html", - ) - parser.add_argument("-j", "--json_state", help="Json states file path", type=str) - parser.add_argument( - "-v", - "--visualization_config", - type=argparse.FileType("r", encoding="UTF-8"), - default=ROOT_DIR / "pygame_2d_vis" / "visualization.yaml", - ) - parser.add_argument( - "-o", - "--output", - type=str, - default="<json_state_name>", - ) - parser.add_argument( - "-p", - "--player_id", - type=str, - default=None, - help="Render view for specific player", - ) - parser.add_argument( - "-g", - "--grid_size", - type=int, - default=GRID_SIZE_DEFAULT, - help="Number pixel for one cell in the grid.", - ) - parser.add_argument( - "-a", - "--action_recording", - type=str, - default=None, - help="The path to the action recording", - ) - parser.add_argument( - "-e", - "--env_configs", - type=str, - default=None, - help="The path to the environment config logs", - ) - parser.add_argument( - "-s", - "--step_duration", - type=float, - default=1 / STEP_DURATION_DEFAULT, - help="Step duration in seconds between environment steps.", - ) - parser.add_argument( - "-f", - "--fps", - type=int, - default=FPS_DEFAULT, - help="Frames per second to render images from the environment", - ) - parser.add_argument( - "-d", "--display", action="store_true", help="Show generated images." - ) - parser.add_argument( - "-n", - "--number_player", - type=int, - default=NUMBER_PLAYERS_DEFAULT, - help="Number of player to visualize. Should be the same as played the game.", - ) - parser.add_argument( - "-b", - "--break_when_no_action", - action="store_true", - help="Stop rendering when no more actions are available.", - ) - parser.add_argument( - "--video", - "--video_source", - type=str, - help="Create a video from a folder full of images.", - ) - args = parser.parse_args() - with open(args.visualization_config, "r") as f: +def main(cli_args): + """ + Runs the video replay with the given command line arguments. + + Args: + cli_args: An object containing the command line arguments. + """ + with open(cli_args.visualization_config, "r") as f: viz_config = yaml.safe_load(f) - if args.json_state: + if cli_args.json_state: target_directory = ( - args.json_state.rsplit(".", maxsplit=1)[0] - if args.output == "<json_state_name>" - else args.output + cli_args.json_state.rsplit(".", maxsplit=1)[0] + if cli_args.output == "<json_state_name>" + else cli_args.output ) from_json_states( - args.json_state, + cli_args.json_state, viz_config, target_directory, - args.player_id, - args.display, - args.grid_size, + cli_args.player_id, + cli_args.display, + cli_args.grid_size, ) - elif args.video: + elif cli_args.video: target_directory = ( - args.video + ".mp4" if args.output == "<json_state_name>" else args.output + cli_args.video + ".mp4" + if cli_args.output == "<json_state_name>" + else cli_args.output ) - video_from_images(args.video, target_directory, args.fps) + video_from_images(cli_args.video, target_directory, cli_args.fps) else: target_directory = ( - args.action_recording.rsplit(".", maxsplit=1)[0] - if args.output == "<json_state_name>" - else args.output + cli_args.action_recording.rsplit(".", maxsplit=1)[0] + if cli_args.output == "<json_state_name>" + else cli_args.output ) simulate( - args.action_recording, - args.env_configs, + cli_args.action_recording, + cli_args.env_configs, viz_config, target_directory, - args.player_id, - args.step_duration, - args.fps, - args.display, - args.number_player, - args.break_when_no_action, - args.grid_size, + cli_args.player_id, + cli_args.step_duration, + cli_args.fps, + cli_args.display, + cli_args.number_player, + cli_args.break_when_no_action, + cli_args.grid_size, ) + + +if __name__ == "__main__": + parser = ArgumentParser( + prog="Cooperative Cuisine Video Generation", + description="Generate videos from recorded data.", + epilog="For further information, see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html", + ) + create_replay_parser(parser) + args = parser.parse_args() + main(args) diff --git a/cooperative_cuisine/study_server.py b/cooperative_cuisine/study_server.py index e136fa9545771a0b566cd4e5c7d3e7ccde9a6771..657c4c00aa6a3624cd963553a4a226e068b3314f 100644 --- a/cooperative_cuisine/study_server.py +++ b/cooperative_cuisine/study_server.py @@ -10,9 +10,9 @@ python game_server.py --manager-ids COPIED_UUID The environment starts when all players connected. """ - import argparse import asyncio +import json import logging import os import signal @@ -25,18 +25,16 @@ from typing import Tuple, Any import requests import uvicorn import yaml -from fastapi import FastAPI, HTTPException +from fastapi import FastAPI, HTTPException, Request from pydantic import BaseModel from cooperative_cuisine import ROOT_DIR +from cooperative_cuisine.argument_parser import create_study_server_parser from cooperative_cuisine.environment import EnvironmentConfig from cooperative_cuisine.game_server import CreateEnvironmentConfig, EnvironmentData from cooperative_cuisine.server_results import PlayerInfo, CreateEnvResult from cooperative_cuisine.utils import ( - url_and_port_arguments, - add_list_of_manager_ids_arguments, expand_path, - add_study_arguments, deep_update, UUID_CUTOFF, ) @@ -295,7 +293,7 @@ class Study: self.next_level() def get_connection( - self, participant_id: str + self, participant_id: str, participant_host: str ) -> Tuple[dict[str, PlayerInfo] | None, LevelInfo | None]: """Get the assigned connections to the game server for a participant. @@ -319,6 +317,22 @@ class Study: number_players=len(self.current_running_env["player_info"]), kitchen_size=self.current_running_env["kitchen_size"], ) + log_path = expand_path( + self.study_config["study_log_path"], + env_name=self.current_running_env["env_id"], + ) + os.makedirs(log_path, exist_ok=True) + with open(Path(log_path) / "study_log", "a") as log_file: + log_file.write( + json.dumps( + { + "env_id": self.current_running_env["env_id"], + "participant_ip": participant_host, + "level_info": level_info.dict(), + "player_info": player_info, + } + ) + ) return player_info, level_info else: raise HTTPException( @@ -403,7 +417,7 @@ class StudyManager: """Port of the game server where the studies are running their environments.""" self.game_server_url: str = "" """Combined URL of the game server where the studies are running their environments.""" - self.create_game_server_url() + self.create_game_server_url(use_ssl=False) self.server_manager_id: str = "" """Manager id of this manager which will be registered in the game server.""" @@ -467,7 +481,7 @@ class StudyManager: raise HTTPException(status_code=409, detail="Participant not in any study.") def get_participant_game_connection( - self, participant_id: str + self, participant_id: str, participant_host: str ) -> Tuple[dict[str, PlayerInfo], LevelInfo]: """Get the assigned connections to the game server for a participant. @@ -495,12 +509,15 @@ class StudyManager: if participant_id in self.participant_id_to_study_map.keys(): assigned_study = self.participant_id_to_study_map[participant_id] - player_info, level_info = assigned_study.get_connection(participant_id) + player_info, level_info = assigned_study.get_connection( + participant_id, participant_host + ) + return player_info, level_info else: raise HTTPException(status_code=409, detail="Participant not in any study.") - def set_game_server_url(self, game_host: str, game_port: int): + def set_game_server_url(self, game_host: str, game_port: int, use_ssl: bool): """Set the game server host address, port and combined url. These values are set this way because the fastapi requests act on top level of the python script. @@ -510,10 +527,12 @@ class StudyManager: """ self.game_host = game_host self.game_port = game_port - self.create_game_server_url() + self.create_game_server_url(use_ssl) - def create_game_server_url(self): - self.game_server_url = f"http://{self.game_host}:{self.game_port}" + def create_game_server_url(self, use_ssl: bool): + self.game_server_url = ( + f"http{'s' if use_ssl else ''}://{self.game_host}:{self.game_port}" + ) def set_manager_id(self, manager_id: str): """Set the manager id of the study server. This value is set this way because @@ -623,6 +642,7 @@ async def level_done(participant_id: str): @app.post("/get_game_connection/{participant_id}") async def get_game_connection( participant_id: str, + request: Request, ) -> dict[str, dict[str, PlayerInfo] | LevelInfo]: """Request to get the connection to the game server of a participant. @@ -634,7 +654,7 @@ async def get_game_connection( """ player_info, level_info = study_manager.get_participant_game_connection( - participant_id + participant_id, request.client.host if request.client is not None else "Test" ) return {"player_info": player_info, "level_info": level_info} @@ -664,8 +684,18 @@ async def disconnect_from_tutorial(participant_id: str): study_manager.end_tutorial(participant_id) -def main(study_host, study_port, game_host, game_port, manager_ids, study_config_path): - study_manager.set_game_server_url(game_host=game_host, game_port=game_port) +def main( + study_host, + study_port, + game_host, + game_port, + manager_ids, + study_config_path, + use_ssl, +): + study_manager.set_game_server_url( + game_host=game_host, game_port=game_port, use_ssl=use_ssl + ) study_manager.set_manager_id(manager_id=manager_ids[0]) study_manager.set_study_config(study_config_path=study_config_path) @@ -685,15 +715,8 @@ if __name__ == "__main__": epilog="For further information, " "see https://scs.pages.ub.uni-bielefeld.de/cocosy/overcooked-simulator/overcooked_simulator.html", ) - url_and_port_arguments( - parser=parser, - server_name="Study Server", - ) - add_list_of_manager_ids_arguments(parser=parser) - add_study_arguments(parser=parser) + create_study_server_parser(parser) args = parser.parse_args() - - game_server_url = f"https://{args.game_url}:{args.game_port}" main( args.study_url, args.study_port, @@ -701,4 +724,5 @@ if __name__ == "__main__": game_port=args.game_port, manager_ids=args.manager_ids, study_config_path=args.study_config, + use_ssl=args.ssl, ) diff --git a/cooperative_cuisine/utils.py b/cooperative_cuisine/utils.py index 88719d54d8fc2fc00cf67d464af0a8c764f9457e..601a1f3dc946b7901665e0d74f16cfc63a6df1df 100644 --- a/cooperative_cuisine/utils.py +++ b/cooperative_cuisine/utils.py @@ -9,7 +9,6 @@ import json import logging import os import sys -import uuid from collections import deque from datetime import datetime, timedelta from enum import Enum @@ -26,15 +25,6 @@ if TYPE_CHECKING: from cooperative_cuisine.counters import Counter from cooperative_cuisine.player import Player -DEFAULT_SERVER_URL = "localhost" -"""Default server URL of game and server study.""" - -DEFAULT_SERVER_PORT = 8080 -"""Default study server port.""" - -DEFAULT_GAME_PORT = 8000 -"""Default game server port.""" - UUID_CUTOFF = 8 """The cutoff length for UUIDs.""" @@ -297,131 +287,6 @@ def setup_logging(enable_websocket_logging=False): logging.getLogger("websockets.client").setLevel(logging.ERROR) -def url_and_port_arguments( - parser, - server_name="game server", - default_study_port=DEFAULT_SERVER_PORT, - default_game_port=DEFAULT_GAME_PORT, - default_server_url=DEFAULT_SERVER_URL, -): - """Adds arguments to the given parser for the URL and port configuration of a server. - - Args: - parser: The argument parser to add arguments to. - server_name: (Optional) The name of the server. Defaults to "game server". - default_study_port: (Optional) The default port number for the study URL. Defaults to 8080. - default_game_port: (Optional) The default port number for the game URL. Defaults to 8000. - default_server_url: (Optional) The default url for the server. Defaults to "localhost". - """ - parser.add_argument( - "-s", - "--study-url", - "--study-host", - type=str, - default=default_server_url, - help=f"Overcooked {server_name} study host url.", - ) - parser.add_argument( - "-sp", - "--study-port", - type=int, - default=default_study_port, - help=f"Port number for the {server_name}", - ) - parser.add_argument( - "-g", - "--game-url", - "--game-host", - type=str, - default=DEFAULT_SERVER_URL, - help=f"Overcooked {server_name} game server url.", - ) - parser.add_argument( - "-gp", - "--game-port", - type=int, - default=default_game_port, - help=f"Port number for the {server_name}", - ) - - -def disable_websocket_logging_arguments(parser): - """Disables the logging of WebSocket arguments in the provided parser. - - Args: - parser: The argument parser object (argparse.ArgumentParser) to which the - "--enable-websocket-logging" argument will be added. - - """ - parser.add_argument( - "--enable-websocket-logging" "", action="store_true", default=True - ) - - -def add_list_of_manager_ids_arguments(parser): - """This function adds the manager ids argument to the given argument parser. - - Args: - parser: An ArgumentParser object used to parse command line arguments. - - Returns: - None - """ - parser.add_argument( - "-m", - "--manager-ids", - nargs="+", - type=str, - default=[uuid.uuid4().hex], - help="List of manager IDs that can create environments.", - ) - - -def add_study_arguments(parser): - """This function adds the study configuration argument to the given argument parser. - - Args: - parser (argparse.ArgumentParser): The argument parser object. - - - Example: - ```python - import argparse - parser = argparse.ArgumentParser() - add_study_arguments(parser) - ``` - """ - parser.add_argument( - "--study-config", - type=str, - default=ROOT_DIR / "configs" / "study" / "study_config.yaml", - help="The config of the study.", - ) - - -def add_gui_arguments(parser): - """Adds the gui debug argument to the given argument parser. - If set, additional debug / admin elements are shown. - - Args: - parser (argparse.ArgumentParser): The argument parser object. - - - Example: - ```python - import argparse - parser = argparse.ArgumentParser() - add_gui_arguments(parser) - ``` - """ - parser.add_argument( - "--do-study", - default=True, - help="Disable additional debug / admin elements.", - action="store_false", - ) - - class NumpyAndDataclassEncoder(json.JSONEncoder): """Special json encoder for numpy types""" diff --git a/setup.py b/setup.py index 7fee18c16e6000f9b07ddd17a6b943cd692370b8..c56d169568dcecf132790c2cedbddb911e2e39e4 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ setup( ], description="The real-time overcooked simulation for a cognitive cooperative system", entry_points={ - "console_scripts": ["cooperative_cuisine = cooperative_cuisine.__main__:main"] + "console_scripts": ["cooperative-cuisine = cooperative_cuisine.__main__:main"] }, install_requires=requirements, license="MIT license", diff --git a/tests/test_pygame.py b/tests/test_pygame.py index 740601f4052972c069ec7fc26c7e832753489c2b..18e1fcf511bca3d5b6cb7b95a6791de73fb00ed7 100644 --- a/tests/test_pygame.py +++ b/tests/test_pygame.py @@ -1,9 +1,11 @@ import json import os +from argparse import ArgumentParser import yaml from cooperative_cuisine import ROOT_DIR +from cooperative_cuisine.argument_parser import create_screenshot_parser from cooperative_cuisine.pygame_2d_vis.drawing import ( calc_angle, Visualizer, @@ -25,7 +27,9 @@ def test_calc_angle(): def test_drawing(): - main([]) + parser = ArgumentParser() + create_screenshot_parser(parser) + main(parser.parse_args([])) assert os.path.exists(os.path.join(ROOT_DIR, "generated", "screenshot.jpg")) diff --git a/tests/test_utils.py b/tests/test_utils.py index 2802bb3a73f7deea7d1ee169adc0eb132b399096..7b507fb9941e51e41b7ac1e7bba490acf11651a4 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -4,18 +4,34 @@ from argparse import ArgumentParser import networkx import pytest +from cooperative_cuisine.argument_parser import ( + game_server_arguments, + study_server_arguments, + disable_websocket_logging_arguments, + add_list_of_manager_ids_arguments, + add_gui_arguments, + add_study_arguments, + ssl_argument, + create_study_server_parser, + create_game_server_parser, + create_gui_parser, + create_replay_parser, + create_normal_parser, + create_server_parser, +) from cooperative_cuisine.environment import Environment +from cooperative_cuisine.pygame_2d_vis.video_replay import ( + GRID_SIZE_DEFAULT, + STEP_DURATION_DEFAULT, + FPS_DEFAULT, + NUMBER_PLAYERS_DEFAULT, +) from cooperative_cuisine.state_representation import ( create_movement_graph, restrict_movement_graph, astar_heuristic, ) from cooperative_cuisine.utils import ( - url_and_port_arguments, - add_list_of_manager_ids_arguments, - disable_websocket_logging_arguments, - add_study_arguments, - add_gui_arguments, create_layout_with_counters, setup_logging, ) @@ -23,19 +39,20 @@ from tests.test_start import env_config_no_validation from tests.test_start import layout_empty_config, item_info -def test_parser_gen(): +def test_arguments(): parser = ArgumentParser() - url_and_port_arguments(parser) + game_server_arguments(parser) + study_server_arguments(parser) disable_websocket_logging_arguments(parser) add_list_of_manager_ids_arguments(parser) - add_study_arguments(parser) add_gui_arguments(parser) - + add_study_arguments(parser) + ssl_argument(parser) parser.parse_args( [ "-s", "localhost", - "-sp", + "-p", "8000", "-g", "localhost", @@ -49,6 +66,41 @@ def test_parser_gen(): ) +def test_parser_creation(): + parser = ArgumentParser() + create_game_server_parser(parser) + parser = ArgumentParser() + create_study_server_parser(parser) + parser = ArgumentParser() + create_gui_parser(parser) + parser = ArgumentParser() + create_replay_parser(parser) + print(parser) + grid_size_action = [ + a for a in parser._optionals._actions if "--grid-size" in a.option_strings + ][0] + assert grid_size_action.default == GRID_SIZE_DEFAULT + + number_player_action = [ + a for a in parser._optionals._actions if "--number-player" in a.option_strings + ][0] + assert number_player_action.default == NUMBER_PLAYERS_DEFAULT + fps_action = [a for a in parser._optionals._actions if "--fps" in a.option_strings][ + 0 + ] + assert fps_action.default == FPS_DEFAULT + step_duration_action = [ + a for a in parser._optionals._actions if "--step-duration" in a.option_strings + ][0] + assert step_duration_action.default == 1 / STEP_DURATION_DEFAULT + + parser = ArgumentParser() + create_server_parser(parser) + + parser = ArgumentParser() + create_normal_parser(parser) + + def test_layout_creation(): assert """###\n#_#\n###\n""" == create_layout_with_counters(3, 3) assert """###\n#_#\n#_#\n###\n""" == create_layout_with_counters(3, 4)