From e27d9130cf00d9f8304c1295fedbd5e25ffbd61d Mon Sep 17 00:00:00 2001 From: hrshtt Date: Sun, 27 Jul 2025 13:25:13 +0530 Subject: [PATCH 01/53] save and load from factorio --- fle/cluster/local/run-envs.sh | 132 ++++++++++++++++++++++++++++------ 1 file changed, 112 insertions(+), 20 deletions(-) diff --git a/fle/cluster/local/run-envs.sh b/fle/cluster/local/run-envs.sh index ae89d9b45..b03ec20e0 100755 --- a/fle/cluster/local/run-envs.sh +++ b/fle/cluster/local/run-envs.sh @@ -5,10 +5,11 @@ setup_platform() { ARCH=$(uname -m) OS=$(uname -s) if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then - export DOCKER_PLATFORM="linux/arm64" + export DOCKER_PLATFORM="linux/amd64" else export DOCKER_PLATFORM="linux/amd64" fi + export SAVES_PATH="../../../.fle/saves" # Detect OS for mods path if [[ "$OS" == *"MINGW"* ]] || [[ "$OS" == *"MSYS"* ]] || [[ "$OS" == *"CYGWIN"* ]]; then # Windows detected @@ -38,15 +39,42 @@ setup_compose_cmd() { fi } +# Function to check for saves and find the latest one +check_saves_and_get_latest() { + # Check if saves directory exists + if [ ! -d "$SAVES_PATH" ]; then + echo "Error: Saves directory not found at $SAVES_PATH" + exit 1 + fi + + # Add the specified number of factorio services + for i in $(seq 0 $(($NUM_INSTANCES - 1))); do + if ! ls "$SAVES_PATH/${i}"/*.zip 1> /dev/null 2>&1; then + echo "Error: No .zip save files found in $SAVES_PATH/${i} for instance $i" + exit 1 + fi + done + + # Find the latest save file based on modification time + LATEST_SAVE=$(ls -t "$SAVES_PATH"/*.zip | head -1) + LATEST_SAVE_NAME=$(basename "$LATEST_SAVE") + + echo "Found latest save: $LATEST_SAVE_NAME" + echo "Using save path: $SAVES_PATH" +} + # Generate the dynamic docker-compose.yml file generate_compose_file() { NUM_INSTANCES=${1:-1} SCENARIO=${2:-"default_lab_scenario"} + USE_LATEST_SAVE=${3:-false} - # Validate scenario - if [ "$SCENARIO" != "open_world" ] && [ "$SCENARIO" != "default_lab_scenario" ]; then - echo "Error: Scenario must be either 'open_world' or 'default_lab_scenario'." - exit 1 + # Validate scenario if not using latest save + if [ "$USE_LATEST_SAVE" = "false" ]; then + if [ "$SCENARIO" != "open_world" ] && [ "$SCENARIO" != "default_lab_scenario" ]; then + echo "Error: Scenario must be either 'open_world' or 'default_lab_scenario'." + exit 1 + fi fi # Validate input @@ -60,6 +88,18 @@ generate_compose_file() { exit 1 fi + # Determine the command based on whether to use latest save or scenario + if [ "$USE_LATEST_SAVE" = "true" ]; then + # Add the specified number of factorio services + for i in $(seq 0 $(($NUM_INSTANCES - 1))); do + mkdir -p ${SAVES_PATH}/${i} + done + + START_COMMAND="--start-server-load-latest" + else + START_COMMAND="--start-server-load-scenario ${SCENARIO}" + fi + # Create the docker-compose file cat > docker-compose.yml << EOF version: '3' @@ -76,7 +116,7 @@ EOF factorio_${i}: image: factorio platform: \${DOCKER_PLATFORM:-linux/amd64} - command: /opt/factorio/bin/x64/factorio --start-server-load-scenario ${SCENARIO} + command: /opt/factorio/bin/x64/factorio ${START_COMMAND} --port 34197 --server-settings /opt/factorio/config/server-settings.json --map-gen-settings /opt/factorio/config/map-gen-settings.json --map-settings /opt/factorio/config/map-settings.json --server-banlist /opt/factorio/config/server-banlist.json --rcon-port 27015 @@ -115,30 +155,51 @@ EOF - source: ../../data/_screenshots target: /opt/factorio/script-output type: bind + - source: ${SAVES_PATH}/${i} + target: /opt/factorio/saves + type: bind EOF done - echo "Generated docker-compose.yml with $NUM_INSTANCES Factorio instance(s) using scenario $SCENARIO" + if [ "$USE_LATEST_SAVE" = "true" ]; then + echo "Generated docker-compose.yml with $NUM_INSTANCES Factorio instance(s) using latest save" + else + echo "Generated docker-compose.yml with $NUM_INSTANCES Factorio instance(s) using scenario $SCENARIO" + fi } # Function to start Factorio cluster start_cluster() { NUM_INSTANCES=$1 SCENARIO=$2 + USE_LATEST_SAVE=$3 setup_platform setup_compose_cmd + # Check for saves if using latest save mode + if [ "$USE_LATEST_SAVE" = "true" ]; then + check_saves_and_get_latest + fi + # Generate the docker-compose file - generate_compose_file "$NUM_INSTANCES" "$SCENARIO" + generate_compose_file "$NUM_INSTANCES" "$SCENARIO" "$USE_LATEST_SAVE" # Run the docker-compose file - echo "Starting $NUM_INSTANCES Factorio instance(s) with scenario $SCENARIO..." + if [ "$USE_LATEST_SAVE" = "true" ]; then + echo "Starting $NUM_INSTANCES Factorio instance(s) with latest save..." + else + echo "Starting $NUM_INSTANCES Factorio instance(s) with scenario $SCENARIO..." + fi export NUM_INSTANCES # Make it available to docker-compose $COMPOSE_CMD -f docker-compose.yml up -d - echo "Factorio cluster started with $NUM_INSTANCES instance(s) using platform $DOCKER_PLATFORM and scenario $SCENARIO" + if [ "$USE_LATEST_SAVE" = "true" ]; then + echo "Factorio cluster started with $NUM_INSTANCES instance(s) using platform $DOCKER_PLATFORM and latest save" + else + echo "Factorio cluster started with $NUM_INSTANCES instance(s) using platform $DOCKER_PLATFORM and scenario $SCENARIO" + fi } # Function to stop Factorio cluster @@ -169,15 +230,23 @@ restart_cluster() { # Extract the number of instances CURRENT_INSTANCES=$(grep -c "factorio_" docker-compose.yml) - # Extract the scenario from the first instance - CURRENT_SCENARIO=$(grep -A1 "command:" docker-compose.yml | grep "start-server-load-scenario" | head -1 | sed -E 's/.*start-server-load-scenario ([^ ]+).*/\1/') - - if [ -z "$CURRENT_SCENARIO" ]; then - CURRENT_SCENARIO="default_lab_scenario" - echo "Warning: Could not determine current scenario, using default: $CURRENT_SCENARIO" + # Check if using latest save or scenario + if grep -q "start-server-load-latest" docker-compose.yml; then + CURRENT_MODE="latest_save" + echo "Found cluster using latest save mode" + else + CURRENT_MODE="scenario" + # Extract the scenario from the first instance + CURRENT_SCENARIO=$(grep -A1 "command:" docker-compose.yml | grep "start-server-load-scenario" | head -1 | sed -E 's/.*start-server-load-scenario ([^ ]+).*/\1/') + + if [ -z "$CURRENT_SCENARIO" ]; then + CURRENT_SCENARIO="default_lab_scenario" + echo "Warning: Could not determine current scenario, using default: $CURRENT_SCENARIO" + fi + echo "Found cluster with scenario: $CURRENT_SCENARIO" fi - echo "Found cluster with $CURRENT_INSTANCES instances using scenario: $CURRENT_SCENARIO" + echo "Found cluster with $CURRENT_INSTANCES instances" # Stop the current cluster echo "Stopping current cluster..." @@ -185,7 +254,11 @@ restart_cluster() { # Start with the same configuration echo "Restarting cluster..." - start_cluster "$CURRENT_INSTANCES" "$CURRENT_SCENARIO" + if [ "$CURRENT_MODE" = "latest_save" ]; then + start_cluster "$CURRENT_INSTANCES" "" "true" + else + start_cluster "$CURRENT_INSTANCES" "$CURRENT_SCENARIO" "false" + fi echo "Factorio cluster restarted successfully." } @@ -203,20 +276,29 @@ show_help() { echo "Options:" echo " -n NUMBER Number of Factorio instances to run (1-33, default: 1)" echo " -s SCENARIO Scenario to run (open_world or default_lab_scenario, default: default_lab_scenario)" + echo " -l Use latest save instead of scenario (loads most recent .zip save from ../../../.fle/saves/0..n)" echo "" echo "Examples:" echo " $0 Start 1 instance with default_lab_scenario" echo " $0 -n 5 Start 5 instances with default_lab_scenario" echo " $0 -n 3 -s open_world Start 3 instances with open_world" + echo " $0 -l Start 1 instance with latest save" + echo " $0 -n 5 -l Start 5 instances with latest save" echo " $0 start -n 10 -s open_world Start 10 instances with open_world" echo " $0 stop Stop all running instances" echo " $0 restart Restart the current cluster" + echo "" + echo "Note: When using -l (latest save), the system will:" + echo " 1. Check for .zip save files in ../../../.fle/saves/0..n" + echo " 2. Finds the most recently modified save file" + echo " 3. Start the server with --start-server-load-latest" } # Main script execution COMMAND="start" NUM_INSTANCES=1 SCENARIO="default_lab_scenario" +USE_LATEST_SAVE=false # Check if first arg is a command if [[ "$1" == "start" || "$1" == "stop" || "$1" == "restart" || "$1" == "help" ]]; then @@ -225,7 +307,7 @@ if [[ "$1" == "start" || "$1" == "stop" || "$1" == "restart" || "$1" == "help" ] fi # Parse options with getopts -while getopts ":n:s:h" opt; do +while getopts ":n:s:lh" opt; do case ${opt} in n ) if ! [[ "$OPTARG" =~ ^[0-9]+$ ]]; then @@ -241,6 +323,9 @@ while getopts ":n:s:h" opt; do fi SCENARIO=$OPTARG ;; + l ) + USE_LATEST_SAVE=true + ;; h ) show_help exit 0 @@ -259,10 +344,17 @@ while getopts ":n:s:h" opt; do done shift $((OPTIND -1)) +# Validate that scenario and latest save are not used together +if [ "$USE_LATEST_SAVE" = "true" ] && [ "$SCENARIO" != "default_lab_scenario" ]; then + echo "Error: Cannot use both -l (latest save) and -s (scenario) options together." + echo "When using -l, the scenario option is ignored as the server loads the latest save." + exit 1 +fi + # Execute the appropriate command case "$COMMAND" in start) - start_cluster "$NUM_INSTANCES" "$SCENARIO" + start_cluster "$NUM_INSTANCES" "$SCENARIO" "$USE_LATEST_SAVE" ;; stop) stop_cluster From e831c7f18fb5dcb7eec6849ea13735156acc5af5 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Sun, 27 Jul 2025 18:49:27 +0530 Subject: [PATCH 02/53] python port --- fle/cluster/local/run-envs.py | 243 ++++++++++++++++++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 fle/cluster/local/run-envs.py diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py new file mode 100644 index 000000000..f24b2fcf2 --- /dev/null +++ b/fle/cluster/local/run-envs.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +import os +import platform +import argparse +from pathlib import Path +import docker + +from enum import Enum + +ROOT_DIR = Path(__file__).resolve().parent.parent.parent.parent + +print(ROOT_DIR) + + +class Scenario(Enum): + OPEN_WORLD = "open_world" + DEFAULT_LAB_SCENARIO = "default_lab_scenario" + + +class PlatformConfig: + def __init__(self): + self.arch = platform.machine() + self.saves_path = ROOT_DIR / ".fle" / "saves" + self.os_name = platform.system() + self.fle_dir = ROOT_DIR + self.mods_path = self._detect_mods_path() + self.compose_path = ( + ROOT_DIR / "fle" / "cluster" / "local" / "docker-compose.yml" + ) + self.rcon_port = 27015 + self.udp_port = 34197 + self.rcon_password = "factorio" + self.map_gen_seed = 44340 + self.cluster_dir = ROOT_DIR / "fle" / "cluster" + self.scenario_dir = self.cluster_dir / "scenarios" + self.screenshots_dir = ROOT_DIR / "data" / "_screenshots" + + def _detect_mods_path(self) -> str: + if any(x in self.os_name for x in ("MINGW", "MSYS", "CYGWIN")): + path = os.getenv("APPDATA", "") + mods = Path(path) / "Factorio" / "mods" + if mods.exists(): + return str(mods) + return str( + Path(os.getenv("USERPROFILE", "")) + / "AppData" + / "Roaming" + / "Factorio" + / "mods" + ) + else: + return str( + Path.home() + / "Applications" + / "Factorio.app" + / "Contents" + / "Resources" + / "mods" + ) + + +class FactorioClusterManager: + def __init__( + self, + docker_platform: str, + config: PlatformConfig, + num_instances: int, + dry_run: bool = False, + ): + self.docker_platform = docker_platform + self.config = config + self.num = num_instances + self.client = docker.from_env() + self.dry_run = dry_run + + def start(self, scenario: str, use_latest: bool): + # ensure save subdirs exist + if use_latest: + for i in range(self.num): + (self.config.saves_path / str(i)).mkdir(parents=True, exist_ok=True) + + # render compose file + for i in range(self.num): + name = f"factorio_{i}" + ports = { + f"{self.config.udp_port}/udp": self.config.udp_port + i, + f"{self.config.rcon_port}/tcp": self.config.rcon_port + i, + } + command = [ + f"/opt/factorio/bin/x64/factorio", + self._get_start_command(use_latest), + *self._get_connection_flags(), + *self._get_server_settings(), + ] + if self.dry_run: + print(name) + print(ports) + print(" ".join(command)) + print(self._get_volumes(i)) + print(self.docker_platform) + print("-" * 100) + else: + self.client.containers.run( + "factorio", + command=command, + name=name, + ports=ports, + volumes=self._get_volumes(i), + detach=True, + platform=self.docker_platform, + restart_policy={"Name": "unless-stopped"}, + mem_limit="1024m", + ) + + def stop(self): + for container in self.client.containers.list(all=True): + if container.name.startswith("factorio_"): + container.stop() + container.remove() + + def restart(self, scenario: str, use_latest: bool): + self.stop() + self.start(scenario, use_latest) + + def _get_start_command(self, use_latest: bool) -> str: + return ( + f"--start-server-load-latest" + if use_latest + else f"--start-server-load-scenario {self.config.scenario}" + ) + + def _get_connection_flags(self) -> list[str]: + return [ + "--rcon-port", + str(self.config.rcon_port), + "--rcon-password", + self.config.rcon_password, + "--port", + str(self.config.udp_port), + ] + + def _get_server_settings(self) -> list[str]: + server_setting = ( + "--server-settings", + "/opt/factorio/config/server-settings.json", + ) + map_gen_settings = ( + "--map-gen-settings", + "/opt/factorio/config/map-gen-settings.json", + ) + map_settings = ( + "--map-settings", + "/opt/factorio/config/map-settings.json", + ) + mod_directory = ( + "--mod-directory", + "/opt/factorio/mods", + ) + map_gen_seed = ( + "--map-gen-seed", + str(self.config.map_gen_seed), + ) + return [ + *server_setting, + *map_gen_settings, + *map_settings, + *mod_directory, + *map_gen_seed, + ] + + def _get_volumes(self, instance_index: int) -> dict: + return { + self.config.mods_path: {"bind": "/opt/factorio/mods", "mode": "rw"}, + str(self.config.saves_path / str(instance_index)): { + "bind": "/opt/factorio/saves", + "mode": "rw", + }, + str((self.config.scenario_dir / self.config.scenario).resolve()): { + "bind": f"/opt/factorio/scenarios/{self.config.scenario}", + "mode": "rw", + }, + str((self.config.screenshots_dir).resolve()): { + "bind": "/opt/factorio/script-output", + "mode": "rw", + }, + } + + +def parse_args(): + p = argparse.ArgumentParser(description="Manage a local Factorio cluster") + p.add_argument( + "command", choices=["start", "stop", "restart"], nargs="?", default="start" + ) + p.add_argument("-n", type=int, default=1, help="Number of instances (1-33)") + p.add_argument( + "-s", + choices=Scenario, + default=Scenario.DEFAULT_LAB_SCENARIO.value, + help="Scenario to load", + ) + p.add_argument( + "-l", action="store_true", help="Use latest save instead of scenario" + ) + p.add_argument( + "--force-amd64", + action="store_true", + help="Force use of amd64 platform", + ) + p.add_argument( + "--dry-run", + action="store_true", + help="Dry run", + ) + return p.parse_args() + + +def main(): + args = parse_args() + config = PlatformConfig() + + docker_platform = ( + "linux/arm64" if config.arch in ("arm64", "aarch64") else "linux/amd64" + ) + if args.force_amd64: + docker_platform = "linux/amd64" + + if args.s == Scenario.OPEN_WORLD.value: + config.scenario = Scenario.OPEN_WORLD.value + elif args.s == Scenario.DEFAULT_LAB_SCENARIO.value: + config.scenario = Scenario.DEFAULT_LAB_SCENARIO.value + + mgr = FactorioClusterManager(docker_platform, config, args.n, args.dry_run) + + if args.command == "start": + mgr.start(args.s, args.l) + elif args.command == "stop": + mgr.stop() + elif args.command == "restart": + mgr.restart(args.s, args.l) + + +if __name__ == "__main__": + main() From 9fc3bbb9f8d0d1cc45b4196ac95874fe98e14663 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Sun, 27 Jul 2025 18:50:47 +0530 Subject: [PATCH 03/53] added environment --- fle/cluster/local/run-envs.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index f24b2fcf2..7835ac628 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -106,6 +106,7 @@ def start(self, scenario: str, use_latest: bool): name=name, ports=ports, volumes=self._get_volumes(i), + environment=self._get_environment(i, use_latest), detach=True, platform=self.docker_platform, restart_policy={"Name": "unless-stopped"}, @@ -185,6 +186,23 @@ def _get_volumes(self, instance_index: int) -> dict: }, } + def _get_environment(self, instance_index: int, use_latest: bool) -> dict: + """Get environment variables for the container.""" + env = { + "SAVES": "/opt/factorio/saves", + "CONFIG": "/opt/factorio/config", + "MODS": "/opt/factorio/mods", + "SCENARIOS": "/opt/factorio/scenarios", + "PORT": str(self.config.udp_port), + "RCON_PORT": str(self.config.rcon_port), + } + + # Only set SERVER_SCENARIO if not using latest save + if not use_latest: + env["SERVER_SCENARIO"] = self.config.scenario + + return env + def parse_args(): p = argparse.ArgumentParser(description="Manage a local Factorio cluster") From b951905e4011cb0ed50c5eff798dcbc2f8f3df5c Mon Sep 17 00:00:00 2001 From: hrshtt Date: Sun, 27 Jul 2025 19:29:04 +0530 Subject: [PATCH 04/53] consolidated image creation and scenario2map approaches --- fle/cluster/local/run-envs.py | 115 ++++++++++++++++------------------ 1 file changed, 53 insertions(+), 62 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index 7835ac628..95204bb45 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -5,6 +5,8 @@ from pathlib import Path import docker +import docker.errors + from enum import Enum ROOT_DIR = Path(__file__).resolve().parent.parent.parent.parent @@ -34,6 +36,8 @@ def __init__(self): self.cluster_dir = ROOT_DIR / "fle" / "cluster" self.scenario_dir = self.cluster_dir / "scenarios" self.screenshots_dir = ROOT_DIR / "data" / "_screenshots" + self.factorio_path = "/opt/factorio/bin/x64/factorio" + self.image_name = "factoriotools/factorio:1.1.110" def _detect_mods_path(self) -> str: if any(x in self.os_name for x in ("MINGW", "MSYS", "CYGWIN")): @@ -73,12 +77,52 @@ def __init__( self.client = docker.from_env() self.dry_run = dry_run - def start(self, scenario: str, use_latest: bool): + def _ensure_image(self): + """ + Ensure the Factorio Docker image is available locally; pull it if missing. + """ + try: + self.client.images.get(self.config.image_name) + except docker.errors.ImageNotFound: + print( + f"Docker image '{self.config.image_name}' not found locally. Pulling from Docker Hub..." + ) + self.client.images.pull(self.config.image_name, platform=self.docker_platform) + + def start(self, use_latest: bool): + # Make sure the Factorio image is present + self._ensure_image() # ensure save subdirs exist if use_latest: for i in range(self.num): (self.config.saves_path / str(i)).mkdir(parents=True, exist_ok=True) + # If running a fresh scenario (not use_latest) and no save exists, convert scenario to a save + if not use_latest: + # Ensure the first-instance save directory exists + first_save_dir = self.config.saves_path / "0" + first_save_dir.mkdir(parents=True, exist_ok=True) + + # Check for existing .zip saves + save_zips = list(first_save_dir.glob("*.zip")) + if not save_zips: + print(f"No save found for scenario '{self.config.scenario}'. Converting scenario to saved map...") + conversion = self.client.containers.run( + self.config.image_name, + name=f"factorio_convert_{self.config.scenario}", + volumes=self._get_volumes(0), + environment=self._get_environment(False), + entrypoint=["/opt/factorio/config/scenario2map.sh"], + command=[self.config.scenario], + detach=True, + platform=self.docker_platform, + auto_remove=True, + ) + conversion.wait() + print("Scenario conversion complete.") + # After conversion, switch to loading latest save + use_latest = True + # render compose file for i in range(self.num): name = f"factorio_{i}" @@ -86,27 +130,19 @@ def start(self, scenario: str, use_latest: bool): f"{self.config.udp_port}/udp": self.config.udp_port + i, f"{self.config.rcon_port}/tcp": self.config.rcon_port + i, } - command = [ - f"/opt/factorio/bin/x64/factorio", - self._get_start_command(use_latest), - *self._get_connection_flags(), - *self._get_server_settings(), - ] if self.dry_run: print(name) print(ports) - print(" ".join(command)) print(self._get_volumes(i)) print(self.docker_platform) print("-" * 100) else: self.client.containers.run( - "factorio", - command=command, + self.config.image_name, name=name, ports=ports, volumes=self._get_volumes(i), - environment=self._get_environment(i, use_latest), + environment=self._get_environment(use_latest), detach=True, platform=self.docker_platform, restart_policy={"Name": "unless-stopped"}, @@ -119,55 +155,9 @@ def stop(self): container.stop() container.remove() - def restart(self, scenario: str, use_latest: bool): + def restart(self, use_latest: bool): self.stop() - self.start(scenario, use_latest) - - def _get_start_command(self, use_latest: bool) -> str: - return ( - f"--start-server-load-latest" - if use_latest - else f"--start-server-load-scenario {self.config.scenario}" - ) - - def _get_connection_flags(self) -> list[str]: - return [ - "--rcon-port", - str(self.config.rcon_port), - "--rcon-password", - self.config.rcon_password, - "--port", - str(self.config.udp_port), - ] - - def _get_server_settings(self) -> list[str]: - server_setting = ( - "--server-settings", - "/opt/factorio/config/server-settings.json", - ) - map_gen_settings = ( - "--map-gen-settings", - "/opt/factorio/config/map-gen-settings.json", - ) - map_settings = ( - "--map-settings", - "/opt/factorio/config/map-settings.json", - ) - mod_directory = ( - "--mod-directory", - "/opt/factorio/mods", - ) - map_gen_seed = ( - "--map-gen-seed", - str(self.config.map_gen_seed), - ) - return [ - *server_setting, - *map_gen_settings, - *map_settings, - *mod_directory, - *map_gen_seed, - ] + self.start(use_latest) def _get_volumes(self, instance_index: int) -> dict: return { @@ -186,9 +176,10 @@ def _get_volumes(self, instance_index: int) -> dict: }, } - def _get_environment(self, instance_index: int, use_latest: bool) -> dict: + def _get_environment(self, use_latest: bool) -> dict: """Get environment variables for the container.""" env = { + "LOAD_LATEST_SAVE": "true" if use_latest else "false", "SAVES": "/opt/factorio/saves", "CONFIG": "/opt/factorio/config", "MODS": "/opt/factorio/mods", @@ -250,11 +241,11 @@ def main(): mgr = FactorioClusterManager(docker_platform, config, args.n, args.dry_run) if args.command == "start": - mgr.start(args.s, args.l) + mgr.start(args.l) elif args.command == "stop": mgr.stop() elif args.command == "restart": - mgr.restart(args.s, args.l) + mgr.restart(args.l) if __name__ == "__main__": From 6f1adec5dd685ab58dc7167acfed60747ae5c723 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Sun, 27 Jul 2025 19:52:30 +0530 Subject: [PATCH 05/53] linting fixes --- fle/cluster/local/run-envs.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index 95204bb45..54730a155 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -84,10 +84,10 @@ def _ensure_image(self): try: self.client.images.get(self.config.image_name) except docker.errors.ImageNotFound: - print( - f"Docker image '{self.config.image_name}' not found locally. Pulling from Docker Hub..." - ) + print(f"'{self.config.image_name}' Image not found locally.") + print("Pulling from Docker Hub...") self.client.images.pull(self.config.image_name, platform=self.docker_platform) + print("Image pulled successfully.") def start(self, use_latest: bool): # Make sure the Factorio image is present @@ -131,11 +131,12 @@ def start(self, use_latest: bool): f"{self.config.rcon_port}/tcp": self.config.rcon_port + i, } if self.dry_run: - print(name) - print(ports) - print(self._get_volumes(i)) - print(self.docker_platform) - print("-" * 100) + print(f"\nContainer name: {name}") + print(f"Port mappings: {ports}") + print(f"Volume mounts: {self._get_volumes(i)}") + print(f"Environment variables: {self._get_environment(use_latest)}") + print(f"Docker platform: {self.docker_platform}") + print("\n" + "=" * 80) else: self.client.containers.run( self.config.image_name, @@ -179,7 +180,7 @@ def _get_volumes(self, instance_index: int) -> dict: def _get_environment(self, use_latest: bool) -> dict: """Get environment variables for the container.""" env = { - "LOAD_LATEST_SAVE": "true" if use_latest else "false", + "LOAD_LATEST_SAVE": use_latest, "SAVES": "/opt/factorio/saves", "CONFIG": "/opt/factorio/config", "MODS": "/opt/factorio/mods", From dcf30c043dd23a64c5c3a59293bec2fd44047dbb Mon Sep 17 00:00:00 2001 From: hrshtt Date: Mon, 28 Jul 2025 03:20:30 +0530 Subject: [PATCH 06/53] defaulting to saves --- fle/cluster/local/run-envs.py | 193 +++++++++++++++++++++------------- 1 file changed, 121 insertions(+), 72 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index 54730a155..a392e68df 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -13,6 +13,12 @@ print(ROOT_DIR) +# Enable docker debug logging +import logging +logging.basicConfig(level=logging.DEBUG) +logging.getLogger('docker').setLevel(logging.DEBUG) + + class Scenario(Enum): OPEN_WORLD = "open_world" @@ -36,8 +42,9 @@ def __init__(self): self.cluster_dir = ROOT_DIR / "fle" / "cluster" self.scenario_dir = self.cluster_dir / "scenarios" self.screenshots_dir = ROOT_DIR / "data" / "_screenshots" - self.factorio_path = "/opt/factorio/bin/x64/factorio" + self.factorio_path = "/factorio/bin/x64/factorio" self.image_name = "factoriotools/factorio:1.1.110" + self.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value def _detect_mods_path(self) -> str: if any(x in self.os_name for x in ("MINGW", "MSYS", "CYGWIN")): @@ -89,112 +96,145 @@ def _ensure_image(self): self.client.images.pull(self.config.image_name, platform=self.docker_platform) print("Image pulled successfully.") - def start(self, use_latest: bool): + def start(self): # Make sure the Factorio image is present self._ensure_image() # ensure save subdirs exist - if use_latest: - for i in range(self.num): - (self.config.saves_path / str(i)).mkdir(parents=True, exist_ok=True) - - # If running a fresh scenario (not use_latest) and no save exists, convert scenario to a save - if not use_latest: - # Ensure the first-instance save directory exists - first_save_dir = self.config.saves_path / "0" - first_save_dir.mkdir(parents=True, exist_ok=True) - - # Check for existing .zip saves - save_zips = list(first_save_dir.glob("*.zip")) - if not save_zips: - print(f"No save found for scenario '{self.config.scenario}'. Converting scenario to saved map...") - conversion = self.client.containers.run( - self.config.image_name, - name=f"factorio_convert_{self.config.scenario}", - volumes=self._get_volumes(0), - environment=self._get_environment(False), - entrypoint=["/opt/factorio/config/scenario2map.sh"], - command=[self.config.scenario], - detach=True, - platform=self.docker_platform, - auto_remove=True, - ) - conversion.wait() - print("Scenario conversion complete.") - # After conversion, switch to loading latest save - use_latest = True + for i in range(self.num): + (self.config.saves_path / str(i)).mkdir(parents=True, exist_ok=True) # render compose file for i in range(self.num): name = f"factorio_{i}" - ports = { - f"{self.config.udp_port}/udp": self.config.udp_port + i, - f"{self.config.rcon_port}/tcp": self.config.rcon_port + i, - } + self.convert_scenario2map(name, i) if self.dry_run: print(f"\nContainer name: {name}") - print(f"Port mappings: {ports}") + print(f"Port mappings: {self._get_ports(i)}") print(f"Volume mounts: {self._get_volumes(i)}") - print(f"Environment variables: {self._get_environment(use_latest)}") + print(f"Environment variables: {self._get_environment()}") print(f"Docker platform: {self.docker_platform}") print("\n" + "=" * 80) else: self.client.containers.run( self.config.image_name, name=name, - ports=ports, + ports=self._get_ports(i), volumes=self._get_volumes(i), - environment=self._get_environment(use_latest), + environment=self._get_environment(), detach=True, platform=self.docker_platform, restart_policy={"Name": "unless-stopped"}, mem_limit="1024m", ) - def stop(self): + def stop(self, force: bool = False, timeout: int = 10): + """ + Stop and remove all factorio containers. + + Args: + force: If True, force kill containers instead of graceful stop + timeout: Timeout in seconds for graceful stop (ignored if force=True) + """ + containers_to_stop = [] + + # Find all factorio containers for container in self.client.containers.list(all=True): if container.name.startswith("factorio_"): - container.stop() - container.remove() - - def restart(self, use_latest: bool): + containers_to_stop.append(container) + + if not containers_to_stop: + print("No factorio containers found to stop.") + return + + print(f"Stopping {len(containers_to_stop)} factorio container(s)...") + + for container in containers_to_stop: + try: + print(f"Stopping container: {container.name}") + + if force: + # Force kill the container + container.kill() + print(f"Force killed: {container.name}") + container.remove() + print(f"Removed: {container.name}") + else: + # Graceful stop with timeout + container.stop(timeout=timeout) + print(f"Gracefully stopped: {container.name}") + + + except docker.errors.NotFound: + print(f"Container {container.name} not found (already removed)") + except docker.errors.APIError as e: + print(f"Error stopping {container.name}: {e}") + except Exception as e: + print(f"Unexpected error with {container.name}: {e}") + + print("Stop operation completed.") + + def restart(self): self.stop() - self.start(use_latest) + self.start() + + def convert_scenario2map(self, name: str, instance_index: int): + save_dir = self.config.saves_path / str(instance_index) + save_dir.mkdir(parents=True, exist_ok=True) + + # Check for existing .zip saves + save_zips = list(save_dir.glob("*.zip")) + print(f"before:instance_index: {instance_index}, save_zips: {save_zips}") + if not save_zips: + print(f"No save found for {name}") + print(f"Converting {self.config.scenario_name} to saved map...") + conversion = self.client.containers.run( + self.config.image_name, + name=name, + volumes=self._get_volumes(instance_index), + environment=self._get_environment(), + entrypoint=f"/scenario2map.sh", + command=[self.config.scenario_name], + detach=True, + platform=self.docker_platform, + auto_remove=True, + ) + conversion.wait() + save_zips = list(save_dir.glob("*.zip")) + print("Scenario converted successfully.") + print(f"after: instance_index: {instance_index}, save_zips: {save_zips}") + + def _get_ports(self, instance_index: int) -> dict: + return { + f"{self.config.udp_port}/udp": self.config.udp_port + instance_index, + f"{self.config.rcon_port}/tcp": self.config.rcon_port + instance_index, + } def _get_volumes(self, instance_index: int) -> dict: return { - self.config.mods_path: {"bind": "/opt/factorio/mods", "mode": "rw"}, + self.config.mods_path: {"bind": "/factorio/mods", "mode": "rw"}, str(self.config.saves_path / str(instance_index)): { - "bind": "/opt/factorio/saves", + "bind": "/factorio/saves", "mode": "rw", }, - str((self.config.scenario_dir / self.config.scenario).resolve()): { - "bind": f"/opt/factorio/scenarios/{self.config.scenario}", + str((self.config.scenario_dir / self.config.scenario_name).resolve()): { + "bind": f"/factorio/scenarios/{self.config.scenario_name}", "mode": "rw", }, str((self.config.screenshots_dir).resolve()): { - "bind": "/opt/factorio/script-output", + "bind": "/factorio/script-output", "mode": "rw", }, } - def _get_environment(self, use_latest: bool) -> dict: + def _get_environment(self) -> dict: """Get environment variables for the container.""" - env = { - "LOAD_LATEST_SAVE": use_latest, - "SAVES": "/opt/factorio/saves", - "CONFIG": "/opt/factorio/config", - "MODS": "/opt/factorio/mods", - "SCENARIOS": "/opt/factorio/scenarios", + return { + "LOAD_LATEST_SAVE": "true", # "true" string not python bool ! "PORT": str(self.config.udp_port), "RCON_PORT": str(self.config.rcon_port), + "SERVER_SCENARIO": self.config.scenario_name, } - # Only set SERVER_SCENARIO if not using latest save - if not use_latest: - env["SERVER_SCENARIO"] = self.config.scenario - - return env - def parse_args(): p = argparse.ArgumentParser(description="Manage a local Factorio cluster") @@ -204,13 +244,10 @@ def parse_args(): p.add_argument("-n", type=int, default=1, help="Number of instances (1-33)") p.add_argument( "-s", - choices=Scenario, + choices=[s.value for s in Scenario], default=Scenario.DEFAULT_LAB_SCENARIO.value, help="Scenario to load", ) - p.add_argument( - "-l", action="store_true", help="Use latest save instead of scenario" - ) p.add_argument( "--force-amd64", action="store_true", @@ -221,6 +258,17 @@ def parse_args(): action="store_true", help="Dry run", ) + p.add_argument( + "--force", + action="store_true", + help="Force kill containers instead of graceful stop", + ) + p.add_argument( + "--timeout", + type=int, + default=10, + help="Timeout in seconds for graceful stop (default: 10)", + ) return p.parse_args() @@ -235,18 +283,19 @@ def main(): docker_platform = "linux/amd64" if args.s == Scenario.OPEN_WORLD.value: - config.scenario = Scenario.OPEN_WORLD.value + config.scenario_name = Scenario.OPEN_WORLD.value elif args.s == Scenario.DEFAULT_LAB_SCENARIO.value: - config.scenario = Scenario.DEFAULT_LAB_SCENARIO.value + config.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value mgr = FactorioClusterManager(docker_platform, config, args.n, args.dry_run) if args.command == "start": - mgr.start(args.l) + mgr.start() elif args.command == "stop": - mgr.stop() + mgr.stop(force=args.force, timeout=args.timeout) elif args.command == "restart": - mgr.restart(args.l) + mgr.stop(force=args.force, timeout=args.timeout) + mgr.start() if __name__ == "__main__": From 4258838db7c91f2b1db4e6cafdeafa120225c678 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Mon, 28 Jul 2025 03:26:19 +0530 Subject: [PATCH 07/53] cleaner --- fle/cluster/local/run-envs.py | 87 ++++++++--------------------------- 1 file changed, 19 insertions(+), 68 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index a392e68df..ffbf3d0c2 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -11,14 +11,6 @@ ROOT_DIR = Path(__file__).resolve().parent.parent.parent.parent -print(ROOT_DIR) - -# Enable docker debug logging -import logging -logging.basicConfig(level=logging.DEBUG) -logging.getLogger('docker').setLevel(logging.DEBUG) - - class Scenario(Enum): OPEN_WORLD = "open_world" @@ -106,7 +98,6 @@ def start(self): # render compose file for i in range(self.num): name = f"factorio_{i}" - self.convert_scenario2map(name, i) if self.dry_run: print(f"\nContainer name: {name}") print(f"Port mappings: {self._get_ports(i)}") @@ -114,64 +105,26 @@ def start(self): print(f"Environment variables: {self._get_environment()}") print(f"Docker platform: {self.docker_platform}") print("\n" + "=" * 80) - else: - self.client.containers.run( - self.config.image_name, - name=name, - ports=self._get_ports(i), - volumes=self._get_volumes(i), - environment=self._get_environment(), - detach=True, - platform=self.docker_platform, - restart_policy={"Name": "unless-stopped"}, - mem_limit="1024m", - ) + return + self.convert_scenario2map(name, i) + self.client.containers.run( + self.config.image_name, + name=name, + ports=self._get_ports(i), + volumes=self._get_volumes(i), + environment=self._get_environment(), + detach=True, + platform=self.docker_platform, + restart_policy={"Name": "unless-stopped"}, + mem_limit="1024m", + ) - def stop(self, force: bool = False, timeout: int = 10): - """ - Stop and remove all factorio containers. - - Args: - force: If True, force kill containers instead of graceful stop - timeout: Timeout in seconds for graceful stop (ignored if force=True) - """ - containers_to_stop = [] - - # Find all factorio containers + + def stop(self): for container in self.client.containers.list(all=True): if container.name.startswith("factorio_"): - containers_to_stop.append(container) - - if not containers_to_stop: - print("No factorio containers found to stop.") - return - - print(f"Stopping {len(containers_to_stop)} factorio container(s)...") - - for container in containers_to_stop: - try: - print(f"Stopping container: {container.name}") - - if force: - # Force kill the container - container.kill() - print(f"Force killed: {container.name}") - container.remove() - print(f"Removed: {container.name}") - else: - # Graceful stop with timeout - container.stop(timeout=timeout) - print(f"Gracefully stopped: {container.name}") - - - except docker.errors.NotFound: - print(f"Container {container.name} not found (already removed)") - except docker.errors.APIError as e: - print(f"Error stopping {container.name}: {e}") - except Exception as e: - print(f"Unexpected error with {container.name}: {e}") - - print("Stop operation completed.") + container.stop() + container.remove() def restart(self): self.stop() @@ -183,7 +136,6 @@ def convert_scenario2map(self, name: str, instance_index: int): # Check for existing .zip saves save_zips = list(save_dir.glob("*.zip")) - print(f"before:instance_index: {instance_index}, save_zips: {save_zips}") if not save_zips: print(f"No save found for {name}") print(f"Converting {self.config.scenario_name} to saved map...") @@ -201,7 +153,6 @@ def convert_scenario2map(self, name: str, instance_index: int): conversion.wait() save_zips = list(save_dir.glob("*.zip")) print("Scenario converted successfully.") - print(f"after: instance_index: {instance_index}, save_zips: {save_zips}") def _get_ports(self, instance_index: int) -> dict: return { @@ -292,9 +243,9 @@ def main(): if args.command == "start": mgr.start() elif args.command == "stop": - mgr.stop(force=args.force, timeout=args.timeout) + mgr.stop() elif args.command == "restart": - mgr.stop(force=args.force, timeout=args.timeout) + mgr.stop() mgr.start() From 52754fb5e811dde9bea0d9739445f53cb9a2f6fe Mon Sep 17 00:00:00 2001 From: hrshtt Date: Mon, 28 Jul 2025 10:56:54 +0530 Subject: [PATCH 08/53] minor changes --- fle/cluster/local/run-envs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index ffbf3d0c2..e99d1fe43 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -119,7 +119,6 @@ def start(self): mem_limit="1024m", ) - def stop(self): for container in self.client.containers.list(all=True): if container.name.startswith("factorio_"): @@ -127,8 +126,9 @@ def stop(self): container.remove() def restart(self): - self.stop() - self.start() + for container in self.client.containers.list(all=True): + if container.name.startswith("factorio_"): + container.restart() def convert_scenario2map(self, name: str, instance_index: int): save_dir = self.config.saves_path / str(instance_index) @@ -141,7 +141,7 @@ def convert_scenario2map(self, name: str, instance_index: int): print(f"Converting {self.config.scenario_name} to saved map...") conversion = self.client.containers.run( self.config.image_name, - name=name, + name=f"conv_{name}", volumes=self._get_volumes(instance_index), environment=self._get_environment(), entrypoint=f"/scenario2map.sh", From 3e3ffdd22be6c65c50a2ae9be74056271427fa8b Mon Sep 17 00:00:00 2001 From: hrshtt Date: Mon, 28 Jul 2025 11:20:53 +0530 Subject: [PATCH 09/53] moved to aiodocker --- fle/cluster/local/run-envs.py | 226 +++++++++++++++++++--------------- 1 file changed, 125 insertions(+), 101 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index e99d1fe43..966ebb6a4 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -3,9 +3,9 @@ import platform import argparse from pathlib import Path -import docker - -import docker.errors +import asyncio +import aiodocker +from aiodocker.exceptions import DockerError from enum import Enum @@ -73,95 +73,26 @@ def __init__( self.docker_platform = docker_platform self.config = config self.num = num_instances - self.client = docker.from_env() self.dry_run = dry_run + self.docker = aiodocker.Docker() - def _ensure_image(self): - """ - Ensure the Factorio Docker image is available locally; pull it if missing. - """ + async def _ensure_image(self): try: - self.client.images.get(self.config.image_name) - except docker.errors.ImageNotFound: - print(f"'{self.config.image_name}' Image not found locally.") + await self.docker.images.inspect(self.config.image_name) + except DockerError: + print(f"'{self.config.image_name}' image not found locally.") print("Pulling from Docker Hub...") - self.client.images.pull(self.config.image_name, platform=self.docker_platform) + await self.docker.images.pull(self.config.image_name) print("Image pulled successfully.") - def start(self): - # Make sure the Factorio image is present - self._ensure_image() - # ensure save subdirs exist - for i in range(self.num): - (self.config.saves_path / str(i)).mkdir(parents=True, exist_ok=True) - - # render compose file - for i in range(self.num): - name = f"factorio_{i}" - if self.dry_run: - print(f"\nContainer name: {name}") - print(f"Port mappings: {self._get_ports(i)}") - print(f"Volume mounts: {self._get_volumes(i)}") - print(f"Environment variables: {self._get_environment()}") - print(f"Docker platform: {self.docker_platform}") - print("\n" + "=" * 80) - return - self.convert_scenario2map(name, i) - self.client.containers.run( - self.config.image_name, - name=name, - ports=self._get_ports(i), - volumes=self._get_volumes(i), - environment=self._get_environment(), - detach=True, - platform=self.docker_platform, - restart_policy={"Name": "unless-stopped"}, - mem_limit="1024m", - ) - - def stop(self): - for container in self.client.containers.list(all=True): - if container.name.startswith("factorio_"): - container.stop() - container.remove() - - def restart(self): - for container in self.client.containers.list(all=True): - if container.name.startswith("factorio_"): - container.restart() - - def convert_scenario2map(self, name: str, instance_index: int): - save_dir = self.config.saves_path / str(instance_index) - save_dir.mkdir(parents=True, exist_ok=True) - - # Check for existing .zip saves - save_zips = list(save_dir.glob("*.zip")) - if not save_zips: - print(f"No save found for {name}") - print(f"Converting {self.config.scenario_name} to saved map...") - conversion = self.client.containers.run( - self.config.image_name, - name=f"conv_{name}", - volumes=self._get_volumes(instance_index), - environment=self._get_environment(), - entrypoint=f"/scenario2map.sh", - command=[self.config.scenario_name], - detach=True, - platform=self.docker_platform, - auto_remove=True, - ) - conversion.wait() - save_zips = list(save_dir.glob("*.zip")) - print("Scenario converted successfully.") - def _get_ports(self, instance_index: int) -> dict: return { f"{self.config.udp_port}/udp": self.config.udp_port + instance_index, f"{self.config.rcon_port}/tcp": self.config.rcon_port + instance_index, } - def _get_volumes(self, instance_index: int) -> dict: - return { + def _get_volumes(self, instance_index: int) -> list: + vols = { self.config.mods_path: {"bind": "/factorio/mods", "mode": "rw"}, str(self.config.saves_path / str(instance_index)): { "bind": "/factorio/saves", @@ -171,20 +102,120 @@ def _get_volumes(self, instance_index: int) -> dict: "bind": f"/factorio/scenarios/{self.config.scenario_name}", "mode": "rw", }, - str((self.config.screenshots_dir).resolve()): { + str(self.config.screenshots_dir.resolve()): { "bind": "/factorio/script-output", "mode": "rw", }, } + # HostConfig.Binds expects ["host:container:mode", ...] + return [f"{host}:{b['bind']}:{b['mode']}" for host, b in vols.items()] - def _get_environment(self) -> dict: - """Get environment variables for the container.""" - return { - "LOAD_LATEST_SAVE": "true", # "true" string not python bool ! + def _get_environment(self) -> list: + env = { + "LOAD_LATEST_SAVE": "true", "PORT": str(self.config.udp_port), "RCON_PORT": str(self.config.rcon_port), "SERVER_SCENARIO": self.config.scenario_name, + "SAVES": "/opt/factorio/saves", + "CONFIG": "/opt/factorio/config", + "MODS": "/opt/factorio/mods", + "SCENARIOS": "/opt/factorio/scenarios", + } + # Docker API wants ["KEY=VALUE", ...] + return [f"{k}={v}" for k, v in env.items()] + + async def convert_scenario2map(self, name: str, instance_index: int): + save_dir = self.config.saves_path / str(instance_index) + save_dir.mkdir(parents=True, exist_ok=True) + + save_zips = list(save_dir.glob("*.zip")) + if not save_zips: + print(f"No save found for {name}") + print(f"Converting {self.config.scenario_name} to saved map...") + config = { + "Image": self.config.image_name, + "Env": self._get_environment(), + "HostConfig": {"Binds": self._get_volumes(instance_index)}, + "Entrypoint": ["/scenario2map.sh"], + "Cmd": [self.config.scenario_name], + "Platform": self.docker_platform, + } + container = await self.docker.containers.run( + config=config, name=f"conv_{name}" + ) + await container.wait() + await container.delete() + print("Scenario converted successfully.") + + async def start(self): + await self._ensure_image() + for i in range(self.num): + (self.config.saves_path / str(i)).mkdir(parents=True, exist_ok=True) + + if self.dry_run: + for i in range(self.num): + name = f"factorio_{i}" + print(f"\nContainer name: {name}") + print(f"Port mappings: {self._get_ports(i)}") + print(f"Volume mounts: {self._get_volumes(i)}") + print(f"Environment variables: {self._get_environment()}") + print(f"Docker platform: {self.docker_platform}") + print("\n" + "=" * 80) + return + + # Launch all instances concurrently + tasks = [self._start_instance(i) for i in range(self.num)] + await asyncio.gather(*tasks) + + async def _start_instance(self, instance_index: int): + name = f"factorio_{instance_index}" + await self.convert_scenario2map(name, instance_index) + config = { + "Image": self.config.image_name, + "Env": self._get_environment(), + "HostConfig": { + "PortBindings": { + f"{self.config.udp_port}/udp": [ + {"HostPort": str(self.config.udp_port + instance_index)} + ], + f"{self.config.rcon_port}/tcp": [ + {"HostPort": str(self.config.rcon_port + instance_index)} + ], + }, + "Binds": self._get_volumes(instance_index), + "RestartPolicy": {"Name": "unless-stopped"}, + "Memory": 1024 * 1024 * 1024, + }, + "Platform": self.docker_platform, } + await self.docker.containers.run(config=config, name=name) + + async def stop(self): + # Stop & remove any container whose name starts with "factorio_" + ctrs = await self.docker.containers.list(all=True) + # Prepare concurrent stop/delete tasks + tasks = [] + for ctr in ctrs: + info = await ctr.show() + nm = info.get("Name", "").lstrip("/") + if nm.startswith("factorio_"): + tasks.append(self._stop_and_delete(ctr)) + # Run all stops/deletes concurrently + await asyncio.gather(*tasks) + + async def _stop_and_delete(self, ctr): + await ctr.stop() + await ctr.delete() + + async def restart(self): + ctrs = await self.docker.containers.list(all=True) + tasks = [] + for ctr in ctrs: + info = await ctr.show() + nm = info.get("Name", "").lstrip("/") + if nm.startswith("factorio_"): + tasks.append(ctr.restart()) + await asyncio.gather(*tasks) def parse_args(): @@ -200,15 +231,9 @@ def parse_args(): help="Scenario to load", ) p.add_argument( - "--force-amd64", - action="store_true", - help="Force use of amd64 platform", - ) - p.add_argument( - "--dry-run", - action="store_true", - help="Dry run", + "--force-amd64", action="store_true", help="Force use of amd64 platform" ) + p.add_argument("--dry-run", action="store_true", help="Dry run") p.add_argument( "--force", action="store_true", @@ -223,7 +248,7 @@ def parse_args(): return p.parse_args() -def main(): +async def main(): args = parse_args() config = PlatformConfig() @@ -232,22 +257,21 @@ def main(): ) if args.force_amd64: docker_platform = "linux/amd64" - if args.s == Scenario.OPEN_WORLD.value: config.scenario_name = Scenario.OPEN_WORLD.value elif args.s == Scenario.DEFAULT_LAB_SCENARIO.value: config.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value mgr = FactorioClusterManager(docker_platform, config, args.n, args.dry_run) - if args.command == "start": - mgr.start() + await mgr.start() elif args.command == "stop": - mgr.stop() + await mgr.stop() elif args.command == "restart": - mgr.stop() - mgr.start() + await mgr.restart() + + await mgr.docker.close() if __name__ == "__main__": - main() + asyncio.run(main()) From 92b955d83aa008b82389ffe050260370274b9d8d Mon Sep 17 00:00:00 2001 From: hrshtt Date: Mon, 28 Jul 2025 12:53:58 +0530 Subject: [PATCH 10/53] more async --- fle/cluster/local/run-envs.py | 114 ++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index 966ebb6a4..95024f980 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -124,28 +124,39 @@ def _get_environment(self) -> list: # Docker API wants ["KEY=VALUE", ...] return [f"{k}={v}" for k, v in env.items()] - async def convert_scenario2map(self, name: str, instance_index: int): - save_dir = self.config.saves_path / str(instance_index) - save_dir.mkdir(parents=True, exist_ok=True) - - save_zips = list(save_dir.glob("*.zip")) - if not save_zips: - print(f"No save found for {name}") - print(f"Converting {self.config.scenario_name} to saved map...") - config = { - "Image": self.config.image_name, - "Env": self._get_environment(), - "HostConfig": {"Binds": self._get_volumes(instance_index)}, - "Entrypoint": ["/scenario2map.sh"], - "Cmd": [self.config.scenario_name], - "Platform": self.docker_platform, - } - container = await self.docker.containers.run( - config=config, name=f"conv_{name}" - ) - await container.wait() - await container.delete() - print("Scenario converted successfully.") + async def get_scenario2map_ctr(self, instance_index: int): + config = { + "Image": self.config.image_name, + "Env": self._get_environment(), + "HostConfig": {"Binds": self._get_volumes(instance_index)}, + "Entrypoint": ["/scenario2map.sh"], + "Cmd": [self.config.scenario_name], + "Platform": self.docker_platform, + } + return await self.docker.containers.run( + config=config, name=f"conv_factorio_{instance_index}" + ) + + async def get_server_ctr(self, instance_index: int): + config = { + "Image": self.config.image_name, + "Env": self._get_environment(), + "HostConfig": { + "PortBindings": { + f"{self.config.udp_port}/udp": [ + {"HostPort": str(self.config.udp_port + instance_index)} + ], + f"{self.config.rcon_port}/tcp": [ + {"HostPort": str(self.config.rcon_port + instance_index)} + ], + }, + "Binds": self._get_volumes(instance_index), + "RestartPolicy": {"Name": "unless-stopped"}, + "Memory": 1024 * 1024 * 1024, + }, + "Platform": self.docker_platform, + } + return await self.docker.containers.create_or_replace(config=config, name=f"factorio_{instance_index}") async def start(self): await self._ensure_image() @@ -164,48 +175,43 @@ async def start(self): return # Launch all instances concurrently - tasks = [self._start_instance(i) for i in range(self.num)] - await asyncio.gather(*tasks) + containers = await self.get_containers_to_run() - async def _start_instance(self, instance_index: int): - name = f"factorio_{instance_index}" - await self.convert_scenario2map(name, instance_index) - config = { - "Image": self.config.image_name, - "Env": self._get_environment(), - "HostConfig": { - "PortBindings": { - f"{self.config.udp_port}/udp": [ - {"HostPort": str(self.config.udp_port + instance_index)} - ], - f"{self.config.rcon_port}/tcp": [ - {"HostPort": str(self.config.rcon_port + instance_index)} - ], - }, - "Binds": self._get_volumes(instance_index), - "RestartPolicy": {"Name": "unless-stopped"}, - "Memory": 1024 * 1024 * 1024, - }, - "Platform": self.docker_platform, - } - await self.docker.containers.run(config=config, name=name) + scenario2map_ctrs = containers[0] + server_ctrs = containers[1] + + await asyncio.gather(*[item.wait() for item in scenario2map_ctrs]) + await asyncio.gather(*[item.delete() for item in scenario2map_ctrs]) + + await asyncio.gather(*[item.start() for item in server_ctrs]) + + def check_save_exists(self, instance_index: int): + save_dir = self.config.saves_path / str(instance_index) + save_dir.mkdir(parents=True, exist_ok=True) + + save_zips = list(save_dir.glob("*.zip")) + return len(save_zips) > 0 + + async def get_containers_to_run(self): + scenario2map_ctrs = [await self.get_scenario2map_ctr(i) for i in range(self.num) if not self.check_save_exists(i)] + server_ctrs = [await self.get_server_ctr(i) for i in range(self.num)] + return scenario2map_ctrs, server_ctrs async def stop(self): # Stop & remove any container whose name starts with "factorio_" ctrs = await self.docker.containers.list(all=True) # Prepare concurrent stop/delete tasks - tasks = [] + tasks_stop = [] + tasks_delete = [] for ctr in ctrs: info = await ctr.show() nm = info.get("Name", "").lstrip("/") if nm.startswith("factorio_"): - tasks.append(self._stop_and_delete(ctr)) + tasks_stop.append(ctr.stop()) + tasks_delete.append(ctr.delete()) # Run all stops/deletes concurrently - await asyncio.gather(*tasks) - - async def _stop_and_delete(self, ctr): - await ctr.stop() - await ctr.delete() + await asyncio.gather(*tasks_stop) + await asyncio.gather(*tasks_delete) async def restart(self): ctrs = await self.docker.containers.list(all=True) @@ -221,7 +227,7 @@ async def restart(self): def parse_args(): p = argparse.ArgumentParser(description="Manage a local Factorio cluster") p.add_argument( - "command", choices=["start", "stop", "restart"], nargs="?", default="start" + "command", choices=["start", "stop", "restart", "clean"], nargs="?", default="start" ) p.add_argument("-n", type=int, default=1, help="Number of instances (1-33)") p.add_argument( From 11dd335789f8222473a113ea543a2da1dc1571a8 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Mon, 28 Jul 2025 13:11:23 +0530 Subject: [PATCH 11/53] even more async --- fle/cluster/local/run-envs.py | 41 ++++++++++++++--------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index 95024f980..21b83fdc4 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -157,6 +157,17 @@ async def get_server_ctr(self, instance_index: int): "Platform": self.docker_platform, } return await self.docker.containers.create_or_replace(config=config, name=f"factorio_{instance_index}") + + async def get_containers_to_run(self): + # Create tasks for concurrent execution + scenario2map_tasks = [self.get_scenario2map_ctr(i) for i in range(self.num) if not self.check_save_exists(i)] + server_tasks = [self.get_server_ctr(i) for i in range(self.num)] + + # Execute tasks concurrently + scenario2map_ctrs = await asyncio.gather(*scenario2map_tasks) + server_ctrs = await asyncio.gather(*server_tasks) + + return scenario2map_ctrs, server_ctrs async def start(self): await self._ensure_image() @@ -176,13 +187,11 @@ async def start(self): # Launch all instances concurrently containers = await self.get_containers_to_run() - scenario2map_ctrs = containers[0] server_ctrs = containers[1] await asyncio.gather(*[item.wait() for item in scenario2map_ctrs]) await asyncio.gather(*[item.delete() for item in scenario2map_ctrs]) - await asyncio.gather(*[item.start() for item in server_ctrs]) def check_save_exists(self, instance_index: int): @@ -191,36 +200,18 @@ def check_save_exists(self, instance_index: int): save_zips = list(save_dir.glob("*.zip")) return len(save_zips) > 0 - - async def get_containers_to_run(self): - scenario2map_ctrs = [await self.get_scenario2map_ctr(i) for i in range(self.num) if not self.check_save_exists(i)] - server_ctrs = [await self.get_server_ctr(i) for i in range(self.num)] - return scenario2map_ctrs, server_ctrs async def stop(self): # Stop & remove any container whose name starts with "factorio_" - ctrs = await self.docker.containers.list(all=True) - # Prepare concurrent stop/delete tasks - tasks_stop = [] - tasks_delete = [] - for ctr in ctrs: - info = await ctr.show() - nm = info.get("Name", "").lstrip("/") - if nm.startswith("factorio_"): - tasks_stop.append(ctr.stop()) - tasks_delete.append(ctr.delete()) - # Run all stops/deletes concurrently + ctrs = await self.docker.containers.list(filters={"name": ["/factorio_"]}) + tasks_stop = [ctr.stop() for ctr in ctrs] + tasks_delete = [ctr.delete() for ctr in ctrs] await asyncio.gather(*tasks_stop) await asyncio.gather(*tasks_delete) async def restart(self): - ctrs = await self.docker.containers.list(all=True) - tasks = [] - for ctr in ctrs: - info = await ctr.show() - nm = info.get("Name", "").lstrip("/") - if nm.startswith("factorio_"): - tasks.append(ctr.restart()) + ctrs = await self.docker.containers.list(filters={"name": ["/factorio_"]}) + tasks = [ctr.restart() for ctr in ctrs] await asyncio.gather(*tasks) From 778b8991f4670e1f68cfb259e4fddb0793718eff Mon Sep 17 00:00:00 2001 From: hrshtt Date: Mon, 28 Jul 2025 13:20:50 +0530 Subject: [PATCH 12/53] clean up --- fle/cluster/local/run-envs.py | 74 +++++++++++++++++------------------ 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index 21b83fdc4..85509527b 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -20,22 +20,15 @@ class Scenario(Enum): class PlatformConfig: def __init__(self): self.arch = platform.machine() - self.saves_path = ROOT_DIR / ".fle" / "saves" self.os_name = platform.system() - self.fle_dir = ROOT_DIR - self.mods_path = self._detect_mods_path() - self.compose_path = ( - ROOT_DIR / "fle" / "cluster" / "local" / "docker-compose.yml" - ) - self.rcon_port = 27015 - self.udp_port = 34197 - self.rcon_password = "factorio" - self.map_gen_seed = 44340 + self.saves_path = ROOT_DIR / ".fle" / "saves" self.cluster_dir = ROOT_DIR / "fle" / "cluster" self.scenario_dir = self.cluster_dir / "scenarios" self.screenshots_dir = ROOT_DIR / "data" / "_screenshots" - self.factorio_path = "/factorio/bin/x64/factorio" + self.mods_path = self._detect_mods_path() self.image_name = "factoriotools/factorio:1.1.110" + self.rcon_port = 27015 + self.udp_port = 34197 self.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value def _detect_mods_path(self) -> str: @@ -86,10 +79,15 @@ async def _ensure_image(self): print("Image pulled successfully.") def _get_ports(self, instance_index: int) -> dict: - return { - f"{self.config.udp_port}/udp": self.config.udp_port + instance_index, - f"{self.config.rcon_port}/tcp": self.config.rcon_port + instance_index, + ports = { + f"{self.config.udp_port}/udp": [ + {"HostPort": str(self.config.udp_port + instance_index)} + ], + f"{self.config.rcon_port}/tcp": [ + {"HostPort": str(self.config.rcon_port + instance_index)} + ], } + return ports def _get_volumes(self, instance_index: int) -> list: vols = { @@ -124,6 +122,13 @@ def _get_environment(self) -> list: # Docker API wants ["KEY=VALUE", ...] return [f"{k}={v}" for k, v in env.items()] + def check_save_exists(self, instance_index: int): + save_dir = self.config.saves_path / str(instance_index) + save_dir.mkdir(parents=True, exist_ok=True) + + save_zips = list(save_dir.glob("*.zip")) + return len(save_zips) > 0 + async def get_scenario2map_ctr(self, instance_index: int): config = { "Image": self.config.image_name, @@ -136,37 +141,34 @@ async def get_scenario2map_ctr(self, instance_index: int): return await self.docker.containers.run( config=config, name=f"conv_factorio_{instance_index}" ) - + async def get_server_ctr(self, instance_index: int): config = { "Image": self.config.image_name, "Env": self._get_environment(), "HostConfig": { - "PortBindings": { - f"{self.config.udp_port}/udp": [ - {"HostPort": str(self.config.udp_port + instance_index)} - ], - f"{self.config.rcon_port}/tcp": [ - {"HostPort": str(self.config.rcon_port + instance_index)} - ], - }, + "PortBindings": self._get_ports(instance_index), "Binds": self._get_volumes(instance_index), "RestartPolicy": {"Name": "unless-stopped"}, "Memory": 1024 * 1024 * 1024, }, "Platform": self.docker_platform, } - return await self.docker.containers.create_or_replace(config=config, name=f"factorio_{instance_index}") - + return await self.docker.containers.create_or_replace( + config=config, name=f"factorio_{instance_index}" + ) + async def get_containers_to_run(self): - # Create tasks for concurrent execution - scenario2map_tasks = [self.get_scenario2map_ctr(i) for i in range(self.num) if not self.check_save_exists(i)] + scenario2map_tasks = [ + self.get_scenario2map_ctr(i) + for i in range(self.num) + if not self.check_save_exists(i) + ] server_tasks = [self.get_server_ctr(i) for i in range(self.num)] - - # Execute tasks concurrently + scenario2map_ctrs = await asyncio.gather(*scenario2map_tasks) server_ctrs = await asyncio.gather(*server_tasks) - + return scenario2map_ctrs, server_ctrs async def start(self): @@ -193,13 +195,6 @@ async def start(self): await asyncio.gather(*[item.wait() for item in scenario2map_ctrs]) await asyncio.gather(*[item.delete() for item in scenario2map_ctrs]) await asyncio.gather(*[item.start() for item in server_ctrs]) - - def check_save_exists(self, instance_index: int): - save_dir = self.config.saves_path / str(instance_index) - save_dir.mkdir(parents=True, exist_ok=True) - - save_zips = list(save_dir.glob("*.zip")) - return len(save_zips) > 0 async def stop(self): # Stop & remove any container whose name starts with "factorio_" @@ -218,7 +213,10 @@ async def restart(self): def parse_args(): p = argparse.ArgumentParser(description="Manage a local Factorio cluster") p.add_argument( - "command", choices=["start", "stop", "restart", "clean"], nargs="?", default="start" + "command", + choices=["start", "stop", "restart", "clean"], + nargs="?", + default="start", ) p.add_argument("-n", type=int, default=1, help="Number of instances (1-33)") p.add_argument( From 44d952977e2e58e32c3c0b14723a2605dd32bb92 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Mon, 28 Jul 2025 16:53:36 +0530 Subject: [PATCH 13/53] fixed rcon issue --- fle/cluster/local/run-envs.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/local/run-envs.py index 85509527b..050627151 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/local/run-envs.py @@ -30,6 +30,7 @@ def __init__(self): self.rcon_port = 27015 self.udp_port = 34197 self.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value + self.server_config_dir = Path(__file__).parent.resolve() / "config" def _detect_mods_path(self) -> str: if any(x in self.os_name for x in ("MINGW", "MSYS", "CYGWIN")): @@ -75,7 +76,9 @@ async def _ensure_image(self): except DockerError: print(f"'{self.config.image_name}' image not found locally.") print("Pulling from Docker Hub...") - await self.docker.images.pull(self.config.image_name) + await self.docker.images.pull( + self.config.image_name, platform=self.docker_platform + ) print("Image pulled successfully.") def _get_ports(self, instance_index: int) -> dict: @@ -89,7 +92,7 @@ def _get_ports(self, instance_index: int) -> dict: } return ports - def _get_volumes(self, instance_index: int) -> list: + def _get_volumes(self, instance_index: int, for_server: bool = True) -> list: vols = { self.config.mods_path: {"bind": "/factorio/mods", "mode": "rw"}, str(self.config.saves_path / str(instance_index)): { @@ -105,6 +108,11 @@ def _get_volumes(self, instance_index: int) -> list: "mode": "rw", }, } + if for_server: + vols[str(self.config.server_config_dir.resolve())] = { + "bind": "/factorio/config", + "mode": "rw", + } # HostConfig.Binds expects ["host:container:mode", ...] return [f"{host}:{b['bind']}:{b['mode']}" for host, b in vols.items()] @@ -114,10 +122,7 @@ def _get_environment(self) -> list: "PORT": str(self.config.udp_port), "RCON_PORT": str(self.config.rcon_port), "SERVER_SCENARIO": self.config.scenario_name, - "SAVES": "/opt/factorio/saves", - "CONFIG": "/opt/factorio/config", - "MODS": "/opt/factorio/mods", - "SCENARIOS": "/opt/factorio/scenarios", + "DLC_SPACE_AGE": "false", } # Docker API wants ["KEY=VALUE", ...] return [f"{k}={v}" for k, v in env.items()] @@ -133,7 +138,7 @@ async def get_scenario2map_ctr(self, instance_index: int): config = { "Image": self.config.image_name, "Env": self._get_environment(), - "HostConfig": {"Binds": self._get_volumes(instance_index)}, + "HostConfig": {"Binds": self._get_volumes(instance_index, for_server=False)}, "Entrypoint": ["/scenario2map.sh"], "Cmd": [self.config.scenario_name], "Platform": self.docker_platform, From 05506fdc8d596b29d11928ac81f893fd5fe9d752 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Mon, 28 Jul 2025 16:57:01 +0530 Subject: [PATCH 14/53] new location for factorio-server structure --- .../config/map-gen-settings.json | 78 +++ .../factorio-server/config/map-settings.json | 120 +++++ fle/cluster/factorio-server/config/rconpw | 1 + .../config/server-settings.json | 72 +++ .../{local => factorio-server}/run-envs.py | 3 +- .../default_lab_scenario/blueprint.zip | Bin 0 -> 215969 bytes .../default_lab_scenario/control.lua | 446 ++++++++++++++++++ .../default_lab_scenario/control2.lua | 6 + .../default_lab_scenario/description.json | 5 + .../default_lab_scenario/freeplay.lua | 299 ++++++++++++ .../scenarios/default_lab_scenario/info.json | 1 + .../locale/en/freeplay.cfg | 4 + .../locale/en/level-01-locale.cfg | 71 +++ .../default_lab_scenario/paperclips.lua | 81 ++++ .../default_lab_scenario/planet1.png | Bin 0 -> 505684 bytes .../scenarios/default_lab_scenario/script.dat | Bin 0 -> 1441 bytes .../scenarios/open_world/README.md | 1 + 17 files changed, 1186 insertions(+), 2 deletions(-) create mode 100644 fle/cluster/factorio-server/config/map-gen-settings.json create mode 100644 fle/cluster/factorio-server/config/map-settings.json create mode 100644 fle/cluster/factorio-server/config/rconpw create mode 100644 fle/cluster/factorio-server/config/server-settings.json rename fle/cluster/{local => factorio-server}/run-envs.py (98%) create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/blueprint.zip create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/control2.lua create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/description.json create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/freeplay.lua create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/info.json create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/freeplay.cfg create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/paperclips.lua create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/planet1.png create mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/script.dat create mode 100644 fle/cluster/factorio-server/scenarios/open_world/README.md diff --git a/fle/cluster/factorio-server/config/map-gen-settings.json b/fle/cluster/factorio-server/config/map-gen-settings.json new file mode 100644 index 000000000..7a2b41281 --- /dev/null +++ b/fle/cluster/factorio-server/config/map-gen-settings.json @@ -0,0 +1,78 @@ +{ + "_terrain_segmentation_comment": "The inverse of 'water scale' in the map generator GUI.", + "terrain_segmentation": 1, + + "_water_comment": + [ + "The equivalent to 'water coverage' in the map generator GUI. Higher coverage means more water in larger oceans.", + "Water level = 10 * log2(this value)" + ], + "water": 1, + + "_comment_width+height": "Width and height of map, in tiles; 0 means infinite", + "width": 0, + "height": 0, + + "_starting_area_comment": "Multiplier for 'biter free zone radius'", + "starting_area": 1, + + "peaceful_mode": false, + "autoplace_controls": + { + "coal": {"frequency": 1, "size": 1, "richness": 1}, + "stone": {"frequency": 1, "size": 1, "richness": 1}, + "copper-ore": {"frequency": 1, "size": 1,"richness": 1}, + "iron-ore": {"frequency": 1, "size": 1, "richness": 1}, + "uranium-ore": {"frequency": 1, "size": 1, "richness": 1}, + "crude-oil": {"frequency": 1, "size": 1, "richness": 1}, + "trees": {"frequency": 1, "size": 1, "richness": 1}, + "enemy-base": {"frequency": 1, "size": 1, "richness": 1} + }, + + "cliff_settings": + { + "_name_comment": "Name of the cliff prototype", + "name": "cliff", + + "_cliff_elevation_0_comment": "Elevation of first row of cliffs", + "cliff_elevation_0": 10, + + "_cliff_elevation_interval_comment": + [ + "Elevation difference between successive rows of cliffs.", + "This is inversely proportional to 'frequency' in the map generation GUI. Specifically, when set from the GUI the value is 40 / frequency." + ], + "cliff_elevation_interval": 40, + + "_richness_comment": "Called 'cliff continuity' in the map generator GUI. 0 will result in no cliffs, 10 will make all cliff rows completely solid", + "richness": 1 + }, + + "_property_expression_names_comment": + [ + "Overrides for property value generators (map type)", + "Leave 'elevation' blank to get 'normal' terrain.", + "Use 'elevation': '0_16-elevation' to reproduce terrain from 0.16.", + "Use 'elevation': '0_17-island' to get an island.", + "Moisture and terrain type are also controlled via this.", + "'control-setting:moisture:frequency:multiplier' is the inverse of the 'moisture scale' in the map generator GUI.", + "'control-setting:moisture:bias' is the 'moisture bias' in the map generator GUI.", + "'control-setting:aux:frequency:multiplier' is the inverse of the 'terrain type scale' in the map generator GUI.", + "'control-setting:aux:bias' is the 'terrain type bias' in the map generator GUI." + ], + "property_expression_names": + { + "control-setting:moisture:frequency:multiplier": "1", + "control-setting:moisture:bias": "0", + "control-setting:aux:frequency:multiplier": "1", + "control-setting:aux:bias": "0" + }, + + "starting_points": + [ + { "x": 0, "y": 0} + ], + + "_seed_comment": "Use null for a random seed, number for a specific seed.", + "seed": null +} diff --git a/fle/cluster/factorio-server/config/map-settings.json b/fle/cluster/factorio-server/config/map-settings.json new file mode 100644 index 000000000..62798d92d --- /dev/null +++ b/fle/cluster/factorio-server/config/map-settings.json @@ -0,0 +1,120 @@ +{ + "difficulty_settings": + { + "recipe_difficulty": 0, + "technology_difficulty": 0, + "technology_price_multiplier": 1, + "research_queue_setting": "after-victory" + }, + "pollution": + { + "enabled": true, + "_comment_min_to_diffuse_1": "these are values for 60 ticks (1 simulated second)", + "_comment_min_to_diffuse_2": "amount that is diffused to neighboring chunk", + "diffusion_ratio": 0.02, + "min_to_diffuse": 15, + "ageing": 1, + "expected_max_per_chunk": 150, + "min_to_show_per_chunk": 50, + "min_pollution_to_damage_trees": 60, + "pollution_with_max_forest_damage": 150, + "pollution_per_tree_damage": 50, + "pollution_restored_per_tree_damage": 10, + "max_pollution_to_restore_trees": 20, + "enemy_attack_pollution_consumption_modifier": 1 + }, + "enemy_evolution": + { + "enabled": true, + "time_factor": 0.000004, + "destroy_factor": 0.002, + "pollution_factor": 0.0000009 + }, + "enemy_expansion": + { + "enabled": true, + "min_base_spacing": 3, + "max_expansion_distance": 7, + "friendly_base_influence_radius": 2, + "enemy_building_influence_radius": 2, + "building_coefficient": 0.1, + "other_base_coefficient": 2.0, + "neighbouring_chunk_coefficient": 0.5, + "neighbouring_base_chunk_coefficient": 0.4, + "max_colliding_tiles_coefficient": 0.9, + "settler_group_min_size": 5, + "settler_group_max_size": 20, + "min_expansion_cooldown": 14400, + "max_expansion_cooldown": 216000 + }, + "unit_group": + { + "min_group_gathering_time": 3600, + "max_group_gathering_time": 36000, + "max_wait_time_for_late_members": 7200, + "max_group_radius": 30.0, + "min_group_radius": 5.0, + "max_member_speedup_when_behind": 1.4, + "max_member_slowdown_when_ahead": 0.6, + "max_group_slowdown_factor": 0.3, + "max_group_member_fallback_factor": 3, + "member_disown_distance": 10, + "tick_tolerance_when_member_arrives": 60, + "max_gathering_unit_groups": 30, + "max_unit_group_size": 200 + }, + "steering": + { + "default": + { + "radius": 1.2, + "separation_force": 0.005, + "separation_factor": 1.2, + "force_unit_fuzzy_goto_behavior": false + }, + "moving": + { + "radius": 3, + "separation_force": 0.01, + "separation_factor": 3, + "force_unit_fuzzy_goto_behavior": false + } + }, + "path_finder": + { + "fwd2bwd_ratio": 5, + "goal_pressure_ratio": 2, + "max_steps_worked_per_tick": 100, + "max_work_done_per_tick": 8000, + "use_path_cache": true, + "short_cache_size": 5, + "long_cache_size": 25, + "short_cache_min_cacheable_distance": 10, + "short_cache_min_algo_steps_to_cache": 50, + "long_cache_min_cacheable_distance": 30, + "cache_max_connect_to_cache_steps_multiplier": 100, + "cache_accept_path_start_distance_ratio": 0.2, + "cache_accept_path_end_distance_ratio": 0.15, + "negative_cache_accept_path_start_distance_ratio": 0.3, + "negative_cache_accept_path_end_distance_ratio": 0.3, + "cache_path_start_distance_rating_multiplier": 10, + "cache_path_end_distance_rating_multiplier": 20, + "stale_enemy_with_same_destination_collision_penalty": 30, + "ignore_moving_enemy_collision_distance": 5, + "enemy_with_different_destination_collision_penalty": 30, + "general_entity_collision_penalty": 10, + "general_entity_subsequent_collision_penalty": 3, + "extended_collision_penalty": 3, + "max_clients_to_accept_any_new_request": 10, + "max_clients_to_accept_short_new_request": 100, + "direct_distance_to_consider_short_request": 100, + "short_request_max_steps": 1000, + "short_request_ratio": 0.5, + "min_steps_to_check_path_find_termination": 2000, + "start_to_goal_cost_multiplier_to_terminate_path_find": 500.0, + "overload_levels": [0, 100, 500], + "overload_multipliers": [2, 3, 4], + "negative_path_cache_delay_interval": 20 + }, + "max_failed_behavior_count": 3 +} diff --git a/fle/cluster/factorio-server/config/rconpw b/fle/cluster/factorio-server/config/rconpw new file mode 100644 index 000000000..765009706 --- /dev/null +++ b/fle/cluster/factorio-server/config/rconpw @@ -0,0 +1 @@ +factorio diff --git a/fle/cluster/factorio-server/config/server-settings.json b/fle/cluster/factorio-server/config/server-settings.json new file mode 100644 index 000000000..bf8641150 --- /dev/null +++ b/fle/cluster/factorio-server/config/server-settings.json @@ -0,0 +1,72 @@ +{ + "name": "Name of the game as it will appear in the game listing", + "description": "Description of the game that will appear in the listing", + "tags": ["game", "tags"], + + "_comment_max_players": "Maximum number of players allowed, admins can join even a full server. 0 means unlimited.", + "max_players": 0, + + "_comment_visibility": ["public: Game will be published on the official Factorio matching server", + "lan: Game will be broadcast on LAN"], + "visibility": + { + "public": false, + "lan": true + }, + + "_comment_credentials": "Your factorio.com login credentials. Required for games with visibility public", + "username": "", + "password": "", + + "_comment_token": "Authentication token. May be used instead of 'password' above.", + "token": "", + + "game_password": "", + + "_comment_require_user_verification": "When set to true, the server will only allow clients that have a valid Factorio.com account", + "require_user_verification": false, + + "_comment_max_upload_in_kilobytes_per_second" : "optional, default value is 0. 0 means unlimited.", + "max_upload_in_kilobytes_per_second": 0, + + "_comment_max_upload_slots" : "optional, default value is 5. 0 means unlimited.", + "max_upload_slots": 5, + + "_comment_minimum_latency_in_ticks": "optional one tick is 16ms in default speed, default value is 0. 0 means no minimum.", + "minimum_latency_in_ticks": 0, + + "_comment_max_heartbeats_per_second": "Network tick rate. Maximum rate game updates packets are sent at before bundling them together. Minimum value is 6, maximum value is 240.", + "max_heartbeats_per_second": 60, + + "_comment_ignore_player_limit_for_returning_players": "Players that played on this map already can join even when the max player limit was reached.", + "ignore_player_limit_for_returning_players": false, + + "_comment_allow_commands": "possible values are, true, false and admins-only", + "allow_commands": "true", + + "_comment_autosave_interval": "Autosave interval in minutes", + "autosave_interval": 10, + + "_comment_autosave_slots": "server autosave slots, it is cycled through when the server autosaves.", + "autosave_slots": 0, + + "_comment_afk_autokick_interval": "How many minutes until someone is kicked when doing nothing, 0 for never.", + "afk_autokick_interval": 0, + + "_comment_auto_pause": "Whether should the server be paused when no players are present.", + "auto_pause": false, + + "only_admins_can_pause_the_game": true, + + "_comment_autosave_only_on_server": "Whether autosaves should be saved only on server or also on all connected clients. Default is true.", + "autosave_only_on_server": true, + + "_comment_non_blocking_saving": "Highly experimental feature, enable only at your own risk of losing your saves. On UNIX systems, server will fork itself to create an autosave. Autosaving on connected Windows clients will be disabled regardless of autosave_only_on_server option.", + "non_blocking_saving": false, + + "_comment_segment_sizes": "Long network messages are split into segments that are sent over multiple ticks. Their size depends on the number of peers currently connected. Increasing the segment size will increase upload bandwidth requirement for the server and download bandwidth requirement for clients. This setting only affects server outbound messages. Changing these settings can have a negative impact on connection stability for some clients.", + "minimum_segment_size": 25, + "minimum_segment_size_peer_count": 20, + "maximum_segment_size": 100, + "maximum_segment_size_peer_count": 10 +} diff --git a/fle/cluster/local/run-envs.py b/fle/cluster/factorio-server/run-envs.py similarity index 98% rename from fle/cluster/local/run-envs.py rename to fle/cluster/factorio-server/run-envs.py index 050627151..375ee82d3 100644 --- a/fle/cluster/local/run-envs.py +++ b/fle/cluster/factorio-server/run-envs.py @@ -22,14 +22,13 @@ def __init__(self): self.arch = platform.machine() self.os_name = platform.system() self.saves_path = ROOT_DIR / ".fle" / "saves" - self.cluster_dir = ROOT_DIR / "fle" / "cluster" - self.scenario_dir = self.cluster_dir / "scenarios" self.screenshots_dir = ROOT_DIR / "data" / "_screenshots" self.mods_path = self._detect_mods_path() self.image_name = "factoriotools/factorio:1.1.110" self.rcon_port = 27015 self.udp_port = 34197 self.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value + self.scenario_dir = Path(__file__).parent.resolve() / "scenarios" self.server_config_dir = Path(__file__).parent.resolve() / "config" def _detect_mods_path(self) -> str: diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/blueprint.zip b/fle/cluster/factorio-server/scenarios/default_lab_scenario/blueprint.zip new file mode 100644 index 0000000000000000000000000000000000000000..e603e83157500bd58d9179383dfdfae913250b94 GIT binary patch literal 215969 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
  • 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%0DsMtiB(J>)()%m5H{WrvJ~FmCg)x-K?+GaD_m! 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%0DNAYwh!`pA|m1KDSQY>q3REd9Q(vu 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 z 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 literal 0 HcmV?d00001 diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua b/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua new file mode 100644 index 000000000..8e30a5fed --- /dev/null +++ b/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua @@ -0,0 +1,446 @@ +require "util" +--local intro_screen = require("intro_screen") +require "story" + +global.paths = global.paths or {} + +local think = function(thought) + game.players[1].print({"","[img=entity/character][color=orange]",{"engineer-title"},": [/color]",{"think-"..thought}}) +end + +function check_for_player_death(event) + if event.name == defines.events.on_player_died then + game.set_game_state({game_finished=true, player_won=false, can_continue=false}) + end +end + +local function on_configuration_changed() + game.reload_script() +end + +local on_player_created = function(event) + + local player = game.players[1] + + player.force.enable_research() + game.forces.player.disable_all_prototypes() + game.forces.player.reset_technology_effects() + -- game.forces.player.reset() + game.forces.player.research_queue_enabled = true + game.map_settings.enemy_expansion.enabled = false + game.forces.enemy.evolution_factor = 0 + game.map_settings.enemy_evolution.enabled = false + --game.disable_tips_and_tricks() + -- game.players[1].force.disable_all_prototypes() + global.storage_tank = game.get_entity_by_tag("storage-tank") + local entities = + { + global.storage_tank + } + for index, entity in pairs(entities) do + entity.minable = false + entity.destructible = false + end + global.intro_entities = entities + + + global.car1 = game.get_entity_by_tag("car1") + local entities2 = + { + global.car1 + } + for index, entity in pairs(entities2) do + entity.minable = false + end + global.intro_entities2 = entities2 + + + local recipe_list = player.force.recipes + -- game.players[1].clear_recipe_notifications() + recipe_list["chemical-plant"].enabled = false + + local technology_list = game.forces.player.technologies + technology_list["automation"].researched = true + technology_list["stone-wall"].researched = true + + technology_list["optics"].enabled = true + technology_list["gun-turret"].researched = true + technology_list["logistics"].enabled = true + technology_list["military"].enabled = true + technology_list["logistic-science-pack"].researched = true + technology_list["steel-processing"].enabled = true + technology_list["electronics"].researched = true + technology_list["fast-inserter"].enabled = true + technology_list["automation-2"].enabled = true + technology_list["electric-energy-distribution-1"].enabled = true + technology_list["engine"].enabled = true + technology_list["fluid-handling"].enabled = true + technology_list["oil-processing"].researched = true + technology_list["logistics"].enabled = true + technology_list["heavy-armor"].enabled = true + technology_list["steel-axe"].enabled = true + technology_list["military-2"].enabled = true + technology_list["chemical-science-pack"].enabled = true + technology_list["military-science-pack"].enabled = true + technology_list["logistics-2"].enabled = true + technology_list["advanced-material-processing"].enabled = true + technology_list["solar-energy"].enabled = true + technology_list["laser"].enabled = false + technology_list["flamethrower"].enabled = true + technology_list["flammables"].researched = true + technology_list["modules"].enabled = false + technology_list["speed-module"].enabled = false + technology_list["effectivity-module"].enabled = false + technology_list["productivity-module"].enabled = false + technology_list["sulfur-processing"].researched = true + technology_list["toolbelt"].enabled = true + technology_list["advanced-electronics"].researched = true + technology_list["explosives"].enabled = true + technology_list["land-mine"].enabled = true + technology_list["electric-energy-accumulators"].enabled = true + technology_list["laser-turret"].enabled = false + technology_list["tank"].enabled = false + technology_list["circuit-network"].enabled = true + technology_list["night-vision-equipment"].enabled = false + technology_list["solar-panel-equipment"].enabled = false + technology_list["energy-shield-equipment"].enabled = false + technology_list["battery-equipment"].enabled = false + technology_list["modular-armor"].enabled = false + technology_list["military-3"].enabled = true + technology_list["railway"].enabled = true + technology_list["automated-rail-transportation"].enabled = false + technology_list["rail-signals"].enabled = true + technology_list["gate"].enabled = true + technology_list["rocketry"].enabled = false + technology_list["battery"].researched = true + technology_list["plastics"].researched = true + technology_list["fluid-wagon"].enabled = true + technology_list["stack-inserter"].enabled = true + + + --blocked tech + technology_list["automation-3"].enabled = false + technology_list["automobilism"].enabled = false -- only from start + technology_list["explosive-rocketry"].enabled = false + technology_list["braking-force-2"].enabled = false + technology_list["cliff-explosives"].enabled = false + technology_list["construction-robotics"].enabled = false + technology_list["energy-weapons-damage-3"].enabled = false + technology_list["exoskeleton-equipment"].enabled = false + technology_list["landfill"].enabled = false + technology_list["utility-science-pack"].enabled = false + technology_list["production-science-pack"].enabled = false + technology_list["advanced-material-processing-2"].enabled = false + technology_list["personal-roboport-equipment"].enabled = false + technology_list["personal-laser-defense-equipment"].enabled = false + technology_list["nuclear-fuel-reprocessing"].enabled = false + technology_list["nuclear-power"].enabled = false + technology_list["military-4"].enabled = false + technology_list["destroyer"].enabled = false + technology_list["defender"].enabled = false + technology_list["discharge-defense-equipment"].enabled = false + technology_list["distractor"].enabled = false + technology_list["artillery"].enabled = false + technology_list["atomic-bomb"].enabled = false + technology_list["robotics"].enabled = false + technology_list["automation-3"].enabled = false + technology_list["rocket-control-unit"].enabled = false + technology_list["logistic-system"].enabled = false + technology_list["logistic-robotics"].enabled = false + technology_list["electric-engine"].enabled = false + technology_list["uranium-processing"].enabled = false + technology_list["spidertron"].enabled = false + technology_list["kovarex-enrichment-process"].enabled = false + technology_list["lubricant"].enabled = false + technology_list["low-density-structure"].enabled = false + technology_list["rocket-fuel"].enabled = false + + + + --blocked tech upgrades + technology_list["braking-force-1"].enabled = true + technology_list["energy-weapons-damage-1"].enabled = false + technology_list["energy-weapons-damage-2"].enabled = false + technology_list["energy-weapons-damage-3"].enabled = false + technology_list["inserter-capacity-bonus-1"].enabled = true + technology_list["inserter-capacity-bonus-2"].enabled = true + technology_list["inserter-capacity-bonus-3"].enabled = true + technology_list["laser-shooting-speed-1"].enabled = false + technology_list["laser-shooting-speed-2"].enabled = false + technology_list["laser-shooting-speed-3"].enabled = false + technology_list["stronger-explosives-1"].enabled = true + technology_list["stronger-explosives-2"].enabled = false + technology_list["stronger-explosives-3"].enabled = false + technology_list["physical-projectile-damage-1"].enabled = true + technology_list["physical-projectile-damage-2"].enabled = true + technology_list["physical-projectile-damage-3"].enabled = true + technology_list["physical-projectile-damage-4"].enabled = true + technology_list["physical-projectile-damage-5"].enabled = false + technology_list["refined-flammables-1"].enabled = true + technology_list["refined-flammables-2"].enabled = true + technology_list["refined-flammables-3"].enabled = false + technology_list["research-speed-1"].enabled = true + technology_list["research-speed-2"].enabled = true + technology_list["research-speed-3"].enabled = true + technology_list["research-speed-4"].enabled = false + technology_list["mining-productivity-1"].enabled = true + technology_list["mining-productivity-2"].enabled = false + technology_list["weapon-shooting-speed-1"].enabled = true + technology_list["weapon-shooting-speed-2"].enabled = true + technology_list["weapon-shooting-speed-3"].enabled = true + technology_list["weapon-shooting-speed-4"].enabled = false + technology_list["worker-robots-speed-1"].enabled = false + technology_list["worker-robots-speed-2"].enabled = false + technology_list["worker-robots-storage-1"].enabled = false + technology_list["worker-robots-storage-2"].enabled = false + technology_list["artillery-shell-range-1"].enabled = false + technology_list["artillery-shell-speed-1"].enabled = false + technology_list["follower-robot-count-1"].enabled = false + technology_list["follower-robot-count-2"].enabled = false + + + +end + + +script.on_event(defines.events.on_script_path_request_finished, function(event) + local request_data = global.path_requests[event.id] + if not request_data then + log("No request data found for ID: " .. event.id) + return + end + + local player = game.get_player(request_data) + if not player then + log("Player not found for request ID: " .. event.id) + return + end + + if event.path then + -- Path found successfully + player.print("Path found with " .. #event.path .. " waypoints") + global.paths[event.id] = event.path + log("Path found for request ID: " .. event.id) + elseif event.try_again_later then + player.print("Pathfinder is busy, try again later") + global.paths[event.id] = "busy" + log("Pathfinder busy for request ID: " .. event.id) + else + player.print("Path not found" .. serpent.block(event)) + global.paths[event.id] = "not_found" + log("Path not found for request ID: " .. event.id) + + end +end) + + +local story_table = +{ + { + { + init = function(event, story) + game.players[1].teleport({0, 0}) + game.print("Welcome players") + set_goal({"Enjoy the scenery for 5 seconds"}) + end, + condition = story_elapsed_check(5) + }, + { + action = function() + local character = game.players[1] + character.insert{name = "iron-gear-wheel", count = 47} + character.insert{name = "pistol", count = 1} + character.insert{name = "firearm-magazine", count = 1} + character.insert{name = "electronic-circuit", count = 90} + character.insert{name = "copper-cable", count = 38} + character.insert{name = "iron-plate", count = 10} + character.insert{name = "steam-engine", count = 1} + set_info() + end + }, + { + init = function(event, story) + game.print("Collect 10 iron ore") + set_goal({"goal-iron-ore"}) + end, + condition = function(event, story) + return game.players[1].get_item_count("iron-ore") >= 10 + end, + action = function(event, story) + game.print("Well done") + set_goal({"completed-iron-ore"}) + end + }, + { + init = function(event, story) + game.print("Build a stone furnace") + set_goal({"goal-build-furnace"}) + end, + condition = function(event, story) + local has_furnace = game.players[1].get_item_count("stone-furnace") >= 1 + local furnace_on_ground = game.players[1].surface.count_entities_filtered{position = game.players[1].position, radius=100, name = "stone-furnace"} >= 1 + return has_furnace or furnace_on_ground + end, + action = function(event, story) + game.print("Great! Now place the furnace on the ground.") + set_goal({"goal-place-furnace"}) + end + }, + { + init = function(event, story) + game.print("Smelt the iron ore in the furnace and collect 10 iron plates") + set_goal({"goal-smelt-and-collect-10-iron-plates"}) + end, + condition = function(event, story) + return game.players[1].get_item_count("iron-plate") >= 10 + end, + action = function(event, story) + game.print("Well done") + set_goal({"completed-iron-plate"}) + end + }, + { + init = function(event, story) + game.print("Collect 10 copper ore") + set_goal({"goal-copper-ore"}) + end, + condition = function(event, story) + return game.players[1].get_item_count("copper-ore") >= 10 + end, + action = function(event, story) + game.print("Well done") + set_goal({"completed-copper-ore"}) + end + }, + { + init = function(event, story) + game.print("Smelt the copper ore in the furnace and collect 10 copper plates") + set_goal({"goal-smelt-and-collect-10-copper-plates"}) + end, + condition = function(event, story) + return game.players[1].get_item_count("copper-plate") >= 10 + end, + action = function(event, story) + game.print("Well done") + set_goal({"completed-smelt-and-collect-10-copper-plates"}) + end + }, + { + init = function(event, story) + game.print("Craft a burner mining drill") + set_goal({"goal-craft-burner-mining-drill"}) + end, + condition = function(event, story) + return game.players[1].get_item_count("burner-mining-drill") >= 1 + end, + action = function(event, story) + game.print("Great! Now place the burner mining drill on the iron ore.") + set_goal({"completed-craft-burner-mining-drill"}) + end + }, + { + init = function(event, story) + game.print("Place the burner mining drill on the iron ore") + set_goal({"goal-place-burner-mining-drill"}) + end, + condition = function(event, story) + return game.players[1].get_item_count("burner-mining-drill") == 0 + end, + action = function(event, story) + game.print("Well done") + set_goal({"completed-place-burner-mining-drill"}) + end + }, + { + init = function(event, story) + game.print("Build an assembling machine") + set_goal({"goal-build-assembling-machine"}) + end, + condition = function(event, story) + return game.players[1].get_item_count("assembling-machine-1") >= 1 + end, + action = function(event, story) + game.print("Great! Now place the assembling machine on the ground.") + set_goal({"completed-place-assembling-machine"}) + end + }, + { + init = function(event, story) + game.print("Automate the production of iron gears") + set_goal({"goal-automate-iron-gears"}) + end, + condition = function(event, story) + return game.players[1].get_item_count("iron-gear-wheel") >= 100 + end, + action = function(event, story) + game.print("Well done") + set_goal({"completed-iron-gears"}) + end + }, + { + init = function(event, story) + game.print("Build a transport belt") + set_goal({ "goal-build-transport-belt" }) + end, + condition = function(event, story) + return game.players[1].get_item_count("transport-belt") >= 1 + end, + action = function(event, story) + game.print("Great! Now place the transport belt on the ground.") + set_goal({ "goal-place-transport-belt" }) + end + } + } +} +story_init_helpers(story_table) + + +local init = function() + global.story = story_init() + + game.map_settings.enemy_expansion.enabled = false + game.forces.enemy.evolution_factor = 0 + game.map_settings.enemy_evolution.enabled = false + --game.disable_tips_and_tricks() + -- game.players[1].force.disable_all_prototypes() +end + + +local story_events = +{ + defines.events.on_tick, + defines.events.on_entity_died, + defines.events.on_built_entity, + defines.events.on_player_mined_item, + defines.events.on_player_mined_entity, + defines.events.on_sector_scanned, + defines.events.on_entity_died, + defines.events.on_entity_damaged, + defines.events.on_player_died +} + +local function on_configuration_changed() + --game.reload_script() + init() +end + +script.on_event(story_events, function(event) + if #game.players > 0 and game.players[1].character then + check_for_player_death(event) + story_update(global.story, event) + end +end) + +script.on_event(defines.events.on_player_created, function(event) + on_player_created(event) +end) + +script.on_event(defines.events.on_player_crafted_item, check_automate_science_packs_advice) +script.on_event(defines.events.on_player_created, on_player_created) +script.on_event(defines.events.on_unit_group_finished_gathering, on_unit_group_finished_gathering) +--script.on_event(defines.events.on_gui_click, intro_screen.on_gui_click) +script.on_init(on_configuration_changed) +script.on_configuration_changed(on_configuration_changed) + + + diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/control2.lua b/fle/cluster/factorio-server/scenarios/default_lab_scenario/control2.lua new file mode 100644 index 000000000..60adec886 --- /dev/null +++ b/fle/cluster/factorio-server/scenarios/default_lab_scenario/control2.lua @@ -0,0 +1,6 @@ +local handler = require("event_handler") +handler.add_lib(require("freeplay")) +--handler.add_lib(require("silo-script")) +--handler.add_lib(require("paperclips")) +--require("story") +--require "story" diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/description.json b/fle/cluster/factorio-server/scenarios/default_lab_scenario/description.json new file mode 100644 index 000000000..d217d9b78 --- /dev/null +++ b/fle/cluster/factorio-server/scenarios/default_lab_scenario/description.json @@ -0,0 +1,5 @@ +{ + "order": "a", + "multiplayer-compatible": true, + "is-main-game": true +} diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/freeplay.lua b/fle/cluster/factorio-server/scenarios/default_lab_scenario/freeplay.lua new file mode 100644 index 000000000..5afa12dfe --- /dev/null +++ b/fle/cluster/factorio-server/scenarios/default_lab_scenario/freeplay.lua @@ -0,0 +1,299 @@ +local util = require("util") +local crash_site = require("crash-site") +require('story') + +local created_items = function() + return + { + ["iron-plate"] = 8, + ["wood"] = 1, + ["pistol"] = 1, + ["firearm-magazine"] = 10, + ["burner-mining-drill"] = 1, + ["stone-furnace"] = 1 + } +end + +local respawn_items = function() + return + { + ["pistol"] = 1, + ["firearm-magazine"] = 10 + } +end + +local ship_items = function() + return + { + ["firearm-magazine"] = 8 + } +end + +local debris_items = function() + return + { + ["iron-plate"] = 8 + } +end + +local ship_parts = function() + return crash_site.default_ship_parts() +end + +local chart_starting_area = function() + local r = global.chart_distance or 200 + local force = game.forces.player + local surface = game.surfaces[1] + local origin = force.get_spawn_position(surface) + force.chart(surface, {{origin.x - r, origin.y - r}, {origin.x + r, origin.y + r}}) +end + + +local on_player_created = function(event) + local player = game.get_player(event.player_index) + util.insert_safe(player, global.created_items) + + if not global.init_ran then + + --This is so that other mods and scripts have a chance to do remote calls before we do things like charting the starting area, creating the crash site, etc. + global.init_ran = true + + chart_starting_area() + + if not global.disable_crashsite then + local surface = player.surface + surface.daytime = 0.7 + crash_site.create_crash_site(surface, {-5,-6}, util.copy(global.crashed_ship_items), util.copy(global.crashed_debris_items), util.copy(global.crashed_ship_parts)) + util.remove_safe(player, global.crashed_ship_items) + util.remove_safe(player, global.crashed_debris_items) + player.get_main_inventory().sort_and_merge() + if player.character then + player.character.destructible = false + end + global.crash_site_cutscene_active = true + crash_site.create_cutscene(player, {-5, -4}) + return + end + + end + + if not global.skip_intro then + if game.is_multiplayer() then + player.print(global.custom_intro_message or {"msg-intro"}) + else + game.show_message_dialog{text = global.custom_intro_message or {"msg-intro"}} + end + end + +end + +local on_player_respawned = function(event) + local player = game.get_player(event.player_index) + util.insert_safe(player, global.respawn_items) +end + +local on_tick = function(event) + story_update(global.story, event) + game.print("tick!") +end + +local on_cutscene_waypoint_reached = function(event) + if not global.crash_site_cutscene_active then return end + if not crash_site.is_crash_site_cutscene(event) then return end + + local player = game.get_player(event.player_index) + + player.exit_cutscene() + + if not global.skip_intro then + if game.is_multiplayer() then + player.print(global.custom_intro_message or {"msg-intro"}) + else + game.show_message_dialog{text = global.custom_intro_message or {"msg-intro"}} + end + end +end + +local skip_crash_site_cutscene = function(event) + if not global.crash_site_cutscene_active then return end + if event.player_index ~= 1 then return end + local player = game.get_player(event.player_index) + if player.controller_type == defines.controllers.cutscene then + player.exit_cutscene() + end +end + +local on_cutscene_cancelled = function(event) + if not global.crash_site_cutscene_active then return end + if event.player_index ~= 1 then return end + global.crash_site_cutscene_active = nil + local player = game.get_player(event.player_index) + if player.gui.screen.skip_cutscene_label then + player.gui.screen.skip_cutscene_label.destroy() + end + if player.character then + player.character.destructible = true + end + player.zoom = 1.5 +end + +local on_player_display_refresh = function(event) + crash_site.on_player_display_refresh(event) +end + +local freeplay_interface = +{ + get_created_items = function() + return global.created_items + end, + set_created_items = function(map) + global.created_items = map or error("Remote call parameter to freeplay set created items can't be nil.") + end, + get_respawn_items = function() + return global.respawn_items + end, + set_respawn_items = function(map) + global.respawn_items = map or error("Remote call parameter to freeplay set respawn items can't be nil.") + end, + set_skip_intro = function(bool) + global.skip_intro = bool + end, + get_skip_intro = function() + return global.skip_intro + end, + set_custom_intro_message = function(message) + global.custom_intro_message = message + end, + get_custom_intro_message = function() + return global.custom_intro_message + end, + set_chart_distance = function(value) + global.chart_distance = tonumber(value) or error("Remote call parameter to freeplay set chart distance must be a number") + end, + get_disable_crashsite = function() + return global.disable_crashsite + end, + set_disable_crashsite = function(bool) + global.disable_crashsite = bool + end, + get_init_ran = function() + return global.init_ran + end, + get_ship_items = function() + return global.crashed_ship_items + end, + set_ship_items = function(map) + global.crashed_ship_items = map or error("Remote call parameter to freeplay set created items can't be nil.") + end, + get_debris_items = function() + return global.crashed_debris_items + end, + set_debris_items = function(map) + global.crashed_debris_items = map or error("Remote call parameter to freeplay set respawn items can't be nil.") + end, + get_ship_parts = function() + return global.crashed_ship_parts + end, + set_ship_parts = function(parts) + global.crashed_ship_parts = parts or error("Remote call parameter to freeplay set ship parts can't be nil.") + end +} + +if not remote.interfaces["freeplay"] then + remote.add_interface("freeplay", freeplay_interface) +end + +local is_debug = function() + local surface = game.surfaces.nauvis + local map_gen_settings = surface.map_gen_settings + return map_gen_settings.width == 50 and map_gen_settings.height == 50 +end + +local freeplay = {} + +freeplay.events = +{ + [defines.events.on_player_created] = on_player_created, + [defines.events.on_player_respawned] = on_player_respawned, + [defines.events.on_cutscene_waypoint_reached] = on_cutscene_waypoint_reached, + ["crash-site-skip-cutscene"] = skip_crash_site_cutscene, + [defines.events.on_player_display_resolution_changed] = on_player_display_refresh, + [defines.events.on_player_display_scale_changed] = on_player_display_refresh, + [defines.events.on_cutscene_cancelled] = on_cutscene_cancelled, + [defines.events.on_tick] = on_tick +} + +freeplay.on_configuration_changed = function() + global.created_items = global.created_items or created_items() + global.respawn_items = global.respawn_items or respawn_items() + global.crashed_ship_items = global.crashed_ship_items or ship_items() + global.crashed_debris_items = global.crashed_debris_items or debris_items() + global.crashed_ship_parts = global.crashed_ship_parts or ship_parts() + + if not global.init_ran then + -- migrating old saves. + global.init_ran = #game.players > 0 + end +end + +global.story_table = +{ + { + { + init = function(event, story) + game.print("Welcome players") + set_goal({"Enjoy the scenery for 5 seconds"}) + end, + condition = story_elapsed_check(5) + }, + { + init = function(event, story) + game.print("Enough relaxation, time to automate!") + set_goal("Automate it!") + --game.players[1].set_goal_description("Automate!", true) + set_info{text = "Automating things is a lot of fun, trust me"} + for k, player in pairs (game.players) do + player.insert("iron-plate") + end + end, + update = function(event, story) + if event.name == defines.events.on_player_crafted_item then + game.players[event.player_index].print({"", "You crafted a ", game.item_prototypes[event.item_stack.name].localised_name}) + end + end, + condition = function(event) + return event.name == defines.events.on_built_entity + end, + action = function(event) + game.print({"", game.players[event.player_index].name, " built a ", event.created_entity.localised_name}) + end + }, + { + init = function() + set_goal("Thats it guys!") + set_info() + end, + condition = story_elapsed_check(5) + } + } +} +story_init_helpers(global.story_table) + +freeplay.on_init = function() + global.story = story_init() + global.created_items = created_items() + global.respawn_items = respawn_items() + global.crashed_ship_items = ship_items() + global.crashed_debris_items = debris_items() + global.crashed_ship_parts = ship_parts() + + if is_debug() then + global.skip_intro = true + global.disable_crashsite = true + end + +end + +story_init_helpers(story_table) + +return freeplay diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/info.json b/fle/cluster/factorio-server/scenarios/default_lab_scenario/info.json new file mode 100644 index 000000000..19765bd50 --- /dev/null +++ b/fle/cluster/factorio-server/scenarios/default_lab_scenario/info.json @@ -0,0 +1 @@ +null diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/freeplay.cfg b/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/freeplay.cfg new file mode 100644 index 000000000..5f466bfce --- /dev/null +++ b/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/freeplay.cfg @@ -0,0 +1,4 @@ +msg-intro=This is the Factorio Freeplay. Your task is to launch a rocket into space. You will need to research advanced technologies in order to unlock the rocket silo. Start small, work your way up with automation, and don't forget to protect yourself from the natives. +scenario-name=Freeplay +description=Your task is to launch a rocket into space. Start from nothing, work your way up with automation, and don't forget to protect yourself from the natives.\n[font=default-bold]This is the intended way of playing Factorio.[/font] + diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg b/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg new file mode 100644 index 000000000..2aec677fb --- /dev/null +++ b/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg @@ -0,0 +1,71 @@ +description=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. \n\n[font=default-bold]Mission Parameters:[/font] \n- New Research: Tank\n- Enemies do not evolve or expand\n +engineer-title=Engineer +msg-intro=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. +scenario-name=Mission 01 + +# Goal Parameters Configuration File +# Maps goal parameters to human-readable names + +goal-iron-ore=Collect 10 iron ore +goal-build-furnace=Build a stone furnace +goal-place-furnace=Place the stone furnace on the ground +goal-smelt-iron=Place 10 iron ore in the furnace and start smelting +goal-smelt-and-collect-10-iron-plates=Smelt and collect 10 iron plates from the furnace +goal-smelt-and-collect-10-copper-plates=Smelt and collect 10 copper plates from the furnace +completed-smelt-and-collect-10-copper-plates=Successfully smelted copper ore into copper plates +goal-completed-iron-plate=Successfully smelted iron ore into iron plates +goal-expand-factory=Expand your factory +goal-setup-assembling-machine=Set up the assembling machine to produce iron gear wheels +goal-supply-iron-plates=Supply the assembling machine with iron plates + + +goal-base=- Go to the base\n- Get into a parked vehicle +goal-build-tank=- Build a tank +goal-construct-tank=- Place tank on the ground +goal-ressearch-tank=Tank is now available in the tech tree \n- Research tank +goal-ressearch-military=- Research military +goal-restore-oil=- Restore oil production \n- Repair pumpjacks: __1__ / __2__ +goal-connect-oil=- Repair and connect pipes to the base\n- Oil must flow through the pipeline \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Kill 3 small worms nearby the pipeline +goal-destroy-spawners=- Destroy __1__ / __2__ biter nests +goal-destroy-spawners2=- Destroy __1__ / __2__ biter nests \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Roadkill the nests by tank +goal-grenades=- Construct and keep at least __1__/__2__ grenades in your inventory +goal-chemical=Chemical plant is now available for the construction \n- Build chemical plants: __1__ / __2__ +goal-train-station=Automated rail transportation is now available in the tech tree \n- Research automated rail transportation \n- Build __1__ / __2__ train stops \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Secure supply line from abandoned bases in the west +msg-base-1=[color=orange]OUTER BASE:[/color] Anyone survived the crash? If you can hear me, our base is located [font=default-bold]NORTHEAST[/font] from your position. +msg-base-2=[color=orange]OUTER BASE:[/color] It was a blast! Those bugs can shoot the plasma really high! Too bad they just hit you. +msg-base-3=[color=orange]OUTER BASE:[/color] I can' t send people to you, our base is under attack! If you arrive unharmed, get in the [font=default-bold]VEHICLE[/font] parked inside! Then I'll send you further instructions. +msg-base-4=[color=orange]OUTER BASE:[/color] The bugs destroyed our pumpjacks and pipelines. We have no fuel supply for our flamethrower turrets. +msg-base-5=[color=orange]OUTER BASE:[/color] The oil facilities lie on [font=default-bold]NORTH[/font]. Can you take a look at this? +msg-base-6=[color=orange]OUTER BASE:[/color] In the [font=default-bold]VEHICLE IS THE EQUIPMENT[/font] for repairing the pumpjacks and pipes. +msg-base-7=[color=orange]OUTER BASE:[/color] I love the smell of running gasoline. Great job! +msg-base-8=[color=orange]OUTER BASE:[/color] I have good news and bad news. The good news is, I've assigned some robots to repair your ship. And the bad news... We need you to solve an insect problem. I think that's fair, right? +msg-base-9=[color=orange]OUTER BASE:[/color] If you want to survive in the wasteland, you should get some better equipment. Use our [font=default-bold]LABORATORIES[/font] to research weapons! \n\nIf I were you, I'd be all over it, because bugs never sleep! +msg-base-10=[color=orange]OUTER BASE:[/color] Well done! It's time to finally get rid of the bugs! As soon as you're ready, head out into the wasteland. +msg-base-11=[color=orange]OUTER BASE:[/color] [font=default-bold]DESTROY[/font] their [font=default-bold]NESTS[/font] around the base. We're not gonna let some insect terrorize us! +msg-base-12=[color=orange]OUTER BASE:[/color] Dead bug, good bug! Finally there will be some peace, but I don't want to jinx it. \n\nI would be quite happy if you could help us construct a box of [font=default-bold]GRENADES[/font]. It's in your interest too. \n\nDon't forget to research advance millitary first. +msg-base-12.1=[color=orange]OUTER BASE:[/color] Just don't throw those grenades under your feet. I've had a couple of individuals here who've... But. What a waste of words. +msg-base-12.2=[color=orange]OUTER BASE:[/color] At least you know how to make explosives. Believe me, bugs really don't like when things explode under their legs. Unfortunately, they're smarter than they look and flattened our [font=default-bold]CHEMICAL PLANT[/font] in the last attack. Will you be so kind and construct a new one? +msg-base-13=[color=orange]OUTER BASE:[/color] You're the one who crashed on this planet years ago, right? You have really bad luck for landing, ha ha. \n\nI think I can trust you. It's classified, but screw it. We're kind of researching new weapons here. At the moment, we're developing a multi-functional [font=default-bold]TANK[/font]. +msg-base-14=[color=orange]OUTER BASE:[/color] However, we are slowly running out of material here. The base is normally supplied by the train line from [font=default-bold]WESTERN STATIONS[/font]. But we lost contact a month ago. +msg-base-15=[color=orange]OUTER BASE:[/color] The bugs probably got them. Either way, it is essential to [font=default-bold]RESTORE SUPPLY LINE[/font] to the base. Make sure we have a link between us and the stations so we can continue with our research. +msg-base-16=[color=orange]OUTER BASE:[/color] Research done, now we need to [font=default-bold]CONSTRUCT THE TANK[/font]. Your automation is more efficient than ours. We'd be happy to have you take care of it. +msg-base-17=[color=orange]OUTER BASE:[/color] We need to do some tests and our good insect friends will be "happy" to help us. [font=default-bold]FILL THE TANK[/font] with the ammo of your choice and [font=default-bold]DESTROY[/font] other insect hatcheries. +msg-base-18=[color=orange]OUTER BASE:[/color] Well done! The test met our expectations. Thank you for your assistance! \n\nWe managed to fix your ship. It's not as good as new, but it'll fly to the nearest dock. Good luck, and if you accidentally fly by, I'll make sure you get to crash... I mean, land safely here. Ha, ha! +think-fall-ship=Ouch, that was a hard landing. What happened? +think-ship-members=Oh, no! My crew is dead... +think-find-base=Okay, those tracks should lead to the base, I should follow them. +think-assist=Great, now I'm a plumber... I'd better get that fuel supply going again, or it's gonna get pretty stuffy in here. +think-assist2=I should handle that vehicle with care. It would be pretty sucky if I had to run around on foot. +think-base=I should go back to base and automate it a little better. +think-base2=There are some coal and iron deposits on the northwestern tip that could be exploited. +think-weapon=Hmm, the planet was only recently discovered and already corporations are doing weapons research here? Suspicious... +think-tank=I have to admit, having a tracked armored monster by my side is quite reassuring. +think-car=With the vehicle, the disposal of nests will be easier. +think-grenade=I've never been a fan of grenades, but I don't have much choice. +think-done=Train stops are done, now we'll focus on researching the tank. + +[intro-screen] +header=Mission 01 - Return +continue=START MISSION +intro=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. \n\n[font=default-bold]Mission Objectives:[/font]\n- Find the Outer Base \n\n\n[font=default-bold]Mission Intel:[/font]\n- Follow the objectives to get new researches.\n- Scouting your surroundings can be worthwhile. +param=[font=default-bold]Mission Parameters:[/font]\n- New Research: Tank\n\nOn Difficulty Easy:\n- Enemies do not evolve or expand\n- Increased starting items & ammo\n\nOn Difficulty Normal:\n- Enemies do not evolve or expand\n- Decreased starting items & ammo\n\nOn Difficulty Hard:\n- Increased starting evolution factor\n- Enemies evolve and expand \ No newline at end of file diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/paperclips.lua b/fle/cluster/factorio-server/scenarios/default_lab_scenario/paperclips.lua new file mode 100644 index 000000000..99edfa4f4 --- /dev/null +++ b/fle/cluster/factorio-server/scenarios/default_lab_scenario/paperclips.lua @@ -0,0 +1,81 @@ +require("story") + +local paperclips = {} +--Init the story script +paperclips.on_init = function() + global.story = story_init(player(1)) +end + +--Register the update to all events +local on_event = function(event) + story_update(global.story, event) +end + +--Can also register to specific events, if you want to do multiple things with that event +local on_tick = function(event) + if global.story == nil then + global.story = story_init(player(1)) + end + story_update(global.story, event) + game.print("tick") + --Other things on tick can go here. +end + +paperclips.events = +{ + [defines.events.on_player_created] = on_event, + [defines.events.on_player_respawned] = on_event, + [defines.events.on_cutscene_waypoint_reached] = on_event, + ["crash-site-skip-cutscene"] = on_event, + [defines.events.on_player_display_resolution_changed] = on_event, + [defines.events.on_player_display_scale_changed] = on_event, + [defines.events.on_cutscene_cancelled] = on_event, + [defines.events.on_tick] = on_tick +} + + +story_table = +{ + { + { + init = function(event, story) + game.print("Welcome players") + set_goal({"Enjoy the scenery for 5 seconds"}) + end, + condition = story_elapsed_check(5) + }, + { + init = function(event, story) + game.print("Enough relaxation, time to automate!") + set_goal("Automate it!") + --game.players[1].set_goal_description("Automate!", true) + set_info{text = "Automating things is a lot of fun, trust me"} + for k, player in pairs (game.players) do + player.insert("iron-plate") + end + end, + update = function(event, story) + if event.name == defines.events.on_player_crafted_item then + game.players[event.player_index].print({"", "You crafted a ", game.item_prototypes[event.item_stack.name].localised_name}) + end + end, + condition = function(event) + return event.name == defines.events.on_built_entity + end, + action = function(event) + game.print({"", game.players[event.player_index].name, " built a ", event.created_entity.localised_name}) + end + }, + { + init = function() + set_goal("Thats it guys!") + set_info() + end, + condition = story_elapsed_check(5) + } + } +} + +story_init_helpers(story_table) + +return paperclips \ No newline at end of file diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/planet1.png b/fle/cluster/factorio-server/scenarios/default_lab_scenario/planet1.png new file mode 100644 index 0000000000000000000000000000000000000000..0af9eb0eb675c5b114e8438708cea9edec97b866 GIT binary patch literal 505684 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

    enO%K;9r;rR;Je~>hN4e76XN%zxNdt~GR zdSal)^|cLvDbR?vc5~C*P~4k+a5xHpa9tbMWxEo zXLeqtDF}g8j*sM)ZWGLNfy1c#& zRL8q6297HVE14?g~I?P%X#upi>+q#Yw2w46(GU3ZWAl$ zbAw4EmEqH+m-?3J2%1%{mx1?}cR4h(sI%7KJAtBIDH4I`rZt1wv#JJoBNtIswPrP- z-@;4Nt;5Mb z;Q79}T@b(gKLxB=o{s;u1yDzY_dOpIFysQ`h^x8gzG*CAKnxbTDNc6>;<@;jOB0jk z{_#~Qz{~Q-=jwO32W5Z3j!axY>kt80iuyC<`#(1{-ydR)2IVh?=KdTa0KLHK>kYtQ z9B0E)T*Hz)1NL{c;>YiG{Ey=hhkq7&tW$IC`bWJVMCL?ecq`)P7|4Mp6u6czTan6;U?ww)XyD;cZi}c98<7m%htSGa{^3~WGr(@ zmt}SBy9q7US5DqYzp1F(s!Dz(x+~vH>qX0ows2V0$+X~Yn%RPIvLLO&a*JdHUGJyYFnBfzfg8JAxZe&=+s;OKXj6dkF2iBcqNNJHArxu_|(}B~ktcq2O zf>{Klg0K!Kd79$nwfbbe+5l))pz%g=4r>SK zV)u{(EEE{1s{vC8pqIMQj6f*=OsPK}qv;rCW&WE608+l$0B9Z7T0D{e|1OXi*ZMy; z07Ky))?@ruTZ{Aj*3a$5Fw@`=0Yth+>V4|(f4S!dU^Z77fF7+1f%+de{b1A4|A_t_ zjq-+)kIFx__A3E>?|Czy!Un(g>?c1^r>OjEr?2-185w|I(fW_akUNYvM*YBV=^YNo~eO4PyC8Q8!?Yed17Obe2Of-ykH1GWG>FH`q6wiRLB2VIE z-!4Hl?87E>z$;>vboWS-s|BKL?YK_f`?g4nOB141>?C(8?JKkfvl^ExlD^xF?<$Q-m*>-KIVA zp;{G^oOw$Oa0+Z0C5_P_4X|?Vc}Udv%IMg0DT@86`w({)Bm~kaF7A`p6dp|F$qBA zfXCD)1N-ssrT_@d-)sQN0}OvW6X@kk!q(>VI0aA!V9FmPE{543ha!M8ccSAeKsxmP zGWO-v{@#T9;Nq09h=p?Ia&dDmg+!RjDyS*#c z%hh(dT;{b~lytYrq!b|t1gjE-^nzSmG;Nh!y!G4=bJuUO_;kBSGkF=ZgnC&p2Nb{> zzBy|ve6yhbrhuA*kw@^2MVBRvw8F1D^aFY__IGr<2dGHGQ^Tb+op&sXmiwBa80nvW zbO(RzlDc_ZSYhJ07Vqn%d;HY%+LW%UjF`0`d!T>~TNi2#FTnrBa_s_(lv1)9tb!;# zzhX9%ydulA$a#TEV&>=V@{&P+S1Zq>XP#j`iH(vsRp?oH#*CV)iZcAaAVw~7B>G7K zR6iA|jhrGcWaBt+^cgIO>EtauZ4of8%KfdKDy3&4vlLs{91K9?t3p6(nTI}?SC9S2 zH@tpbExo)*_RrGpwOC)&T>YL>i`X)Bn(XPIISDA_@ zKf8?5bVu(edy%(i@4LCqrT^^7Bh2_K`cVykQ z%go?{ZanDj_H7F*q~>)KoO91C^GsR@{c|?_C(JVtd@7j_-GAN!HlY%>4X4U^;d!Rl zhG~N=L-I6n)Yu-5io-BG*GYjq=J96 zhuy&d=n?>2{E-2$6$|)wdW$u#V`5GwK@J4q;K%@|Dj?*@0E~gpz2gW#_e^W;AKm}4 z`a_P@UweLS0j74|g8{(h5C>rQn?HT_yMX=lJ*DnnhhTTG0}$N^G}ixq^t~SqB?N3B z4XHsuO838o4cNr(_3f^_b7ix8!uie3$qkoPEyU)%!D6@TrP06N-}Ki@f9APyx4M!Q zefZ>-UeN2CyC<`|U0E*AdYU%%syKOcLqpJd&o<{___ng$f6|v>9NsqWxoppGZv3`& zgPIX9yeYCAq#3>t9B|xrN!4u7^~0^7swRF;Rr?NJBc$H@Sw^JO)*PJUHg8v*E14vC zQ^(+l3FTF?j0K@vZisT}KirlKrT}?=oiDe0+E$U#?w6dN#J$M{3EK6#5=q;w8kePY zmuDh!!#sDQkl2(^0`x}~?Ye4=ycNhfGN%cIXa#El=QT2PK^IQzgbP3nE{gd9SAzps zWn2Rp2w9fNyz<&Q&n`n1#KXrU#OIZTAOO@JV12e7!Qlu>xt(;5-6WQ23=w}0ESjbn zV8SMwCIhoH9jDgNgsBmszD$qjTpz^BkJLHMU(57)`6XRcc=PP=an27kU(o`Gc>xgZ z;5787XI~C75Che)z+us`yI}?L=-*EP00X|!0Q?*`k5Rx^mH~&}e>|K+3ZTb-PXYhu zta0?O`Nb*raE6{{=LiJUqko*|24FxuRWfuG0gUEw>iZdXhuj~VPW+!k`;Zsa|5%{U zT0j9DK|!1=rPrI<>zytI4x-b4Y`?{lUwTb2_l4 zeCzJ^QQKD{j=#N=OMlnzn&n$h-&U0^pSP&I@r$=K2@%ZF#X^X|9neRwM!lB5}i&qVlWX@9{@= z#pBKp-KDG_ywx{zl3VV{D$n5UYhreZo8XHI`pKQqIkp4HG@SkMG4U*Z>4 z3~EkW75m15EnHV_lD1XjW>j$G|3$OTU>@vd-iRQ}p~Vgfj55wrWE0duU9TeCaN;Ja zO@-ZOW?h+;4fZkKoGEXbIl0U0R2Hghu=R3FVFhNEoBK-9VNE!1J3}e3^_gN0DHBl` zK2*HIZ&u;*bxp6Yb^g{-UvY-0;a}qhYxZk|Z8mlaL%|1)*W<3B|F{JDrBeXh*Yhyc z`ys)9U;rTHKQ;iSIbnbk3aFR)$M!M^Xz3gP#}_aF-22P;QDp$|^sMhpH-`4VAULi9 zL@FXW7y#Yzk3ab#0?-iYol5=>0f46c9HM`~c>waie_Zc7(*6&RkFlv2dxZk3Gfn#$ zHUGod2K86B4lH1#|5E|*$KU(W55D=$Z+ugg{V?U{$+e#8aC;ZV^zy+fW0CzR%-PLn zXPEiJD7}8*GG}$pMfvcPZ+#dpg=+-YJ{+z{HkW5tmnUDF@jE7&8}HW-KeXI@KzvA$ z(#S|fPKfKSw^u%0BKJ0%FeV6ec}aS?-iYcfNi4yHEPG0s>*l++?QTE4Gw3S3phhD| zqUMDX)fhzDMiF@GEEf#?zyexZCzL5{fDu7~2!up%x*)#mCTA0)AiaWa^=I(5z+B%A!$VC-IsB z={fZY6OuNK^b+NP^9Ct7WBEKGH3vQ~NuE*X?orxA$oLJ1SW>2zzRm)Mfp`nu9H@@k2>p8o z;ES(`08DIB-#)x%O)nks0QAg1WiVbc04VxRA64oPu||vVxpZ9Q;E)2$gCPPsjsW!1 zsYeE2T=zrw{!Sx5A!Fmu;Q3L;KQ8%CzJGH5V^c5GeX;^L$2Pn+IeZ-P938*jAV*^W z!5sYEAF%A-r+)mkA1cxh+&=A5qUbl!&fVsL-;+4#H>Zwb^@g-B`+m8CXAG}BMDW}A zYTJq2V?MR!99*9D`NNxcOOMc+m#FluKKZftzV*FvnVhFy)$iQ(haN63Ni^U2L{=7E z2Kjuk+gxOk+=X?$kEuyP_oeXk{iO&VToPW;;N)WF0g^8$aeGesmWi6thv)99O&1+w zYhQLnL%86ih3k@+&WJ!hZgKKFL#(#Ra?nf0QvqNWkZE%B;V)m2nERwHR{)@dTGf+1qBOj}I? zp{y~X_bHr}O?9g#No&Q7$#(3Q63opTRo?1sfU1|2ryxQ+fJ z|A~u>9XP&_L(Ok;|5~*E{|NU6_dZ$%o%(;~7sDP8WdpSD*8(dtd4J#q;Qs;uz)GJ# zMB;zf_Y_(l$LBkM{NXJvzqa9L7rey6!Lxfv2za?*e8LHp4J-C8=?b2> zf|8Qen{t=$!tHL&GFgPXw-M^plyc6+oN`u@-Hu+I+yu0h30yV|L(;mUp@a*%D>!sL zxeDtlqlAQRl94tc3fXgs0F{RH?kZV-a-EX%sw`=O_Z10(7*UBjVWL4Oz^E%r9(pO| z^_rz-v8P;Ct$!uqk`aTGXCiumG$aZ;1g5*`Si{1+14-sp1K^c zm=+XQfY;dZsKL!1LvWq*XcCT;mkO^GBjU==r5AFqSUC;$cH*dZD?6tI-K0!Fm zphP7nJ6hz-6=Lsy{;~hD2lY9lW$04s)&7#IuK9~HO}1gel9qIR<5PbA-jiS_^VZ)N zJSH_=Gr^eX32WtoGylOOdU68HAT1<3Hjw@V{2v$E7!pP$5)tNe(fLb1UnhIN$Qu`a zI$8R4@77)&Rt0R$YU?`GtrE^$fjdZFKB-y?)_?>gqY(BWD6A?;LPm3ou)3mo&0Hk=Rl=fv7aAv(45qSA_Hoj5}{;*Q9z?;R2=Few_!=I}rvGv4kM8~d z!W4K!xb%(^HBbq^#4~-{uhzIS0w%rt^b(j3o_r- z(zJ)Se7GUk*Slvw`0fwT z)EoQpvPK+mceC5Q^#*+bW#?b`vZPHc$>j&0%*sv7 z@Ibn|3&9gg$x;@(WtTOCCO)}xiv-%bm*~RZUX~BmBK0Uy>-@1!7LwwN5LC~F8@iX9 z*zF4n{a+)FU#|V4?);eWM5?ME5J1&az+qk7{gSz)NPLEOyRK{q+q>||O>4QAM2bRi zP8oASNm}op?rT}OXNeTO5WYy%*C8VCTjthgBio!bIUx=gEQg_xy<7PEF3b&{+B_8% zT_t%_6wpP{?)Qbr7lk82DGO*S=~_hm;0{D)`PL$W2G<1(!F)8#%qz&w1LkMdm2?pR zEE1*39At@xWD5!91*br9QSH2e6M|EBlrpwp200`LIACTeY6qrcB77Lv3^J%pafQqD zmMjClfMrUz-@FN zm#PAKhycdXKZm&=$F=|OYxSS#J(J5mj{KEAL)6_L4etNz(-XR`+&`W;8STM={SVQU z0kw_XUp+@eaW!`3eIB}mZc0B#{B|8@?CR!+>Yd-eEv4{Jw_xoo{!~8U%Zr#dA)Rk7 zXDKOtkpABF02KV5hh%2mK#c-&f$`{%dVD; zmu^qDo%{6h)l;{7+-^U*O*n5EopX^an=Xb<5@;%=K;ci{db63yAa6L?UnzhuT>Et{ zOP@4?Bi?7Q39FB^%i8z%EoJT!;Xm`i`(rd>ez%kz-rkdyARQ8NmnLxj9qpdFt8hu1 z5K!VK=9M%#r~^x0$Sz4(-sUWUCFyIK(M0B%WCBS>mwdjh+6vJ*Cn*hyM}-gs6~uzo zIcWPNVNNS5OR~cB_WLTW)6|jtZ5HBWArzIt^cZVfVHyPmwqNfnfd9(HqsC|Q^vGYvW11rbglJ$sgr7aHnR1dhEKtH9@ zFGvMNKk6mahXb3S(uYj#-iQV2MZnc=!~n3VGpM~1-S|E(9efWma0b-%e-;5it6nw$ z$CM~J68Fc$5HRUUA1@Pu)b>I*pdJHg{e04R+rh*9&S(#8)!3<4ID2jYvV~p)CHlgI zIhFwZA$JD@;6eR$*B|6~=;!lxe@*0_yuY62aqRr@P?bDf4?Mq7MhgJt*L!uGV8E$n zptyfu(drM;jYf*|bFUA>aQEb{5i44ddNU8tF6b>dgbnchAMMF)UXIla6f;pTjE{=QFtpec86b01yrNK7L^NJqKwyKvK?`WJ7{9-M67DfmI4Wt3g|6h}YQJ7xk;ne_^Ib@jFqKr)oc4qWx0k#`&QNe(u&dMMIVgF8k}zHZ zzaz*dIGGbpo0`UP!n!r_A3k(AbN8oMz zwP&@vTMC-f6DcouuAq`T@m4T<<)!XUU4Sq$_I%xFD&K!c?NLO|`12gf_RPmCI8ry&zQuLa{VkX*PStn1FB1!8<7mS=4D( zgId%WEsPVd;dem}7@B~LJjYQ1&>#t0yvik!7I&@G9_9oCG&FF?k*)&8&cZ=UJ%sV< zRlH{s-6@bBhx67y9v5gG&f4L-vVRJrH-ni*0)`$TJupyha}9;f)DHe|JPt7690%-z z7k~V}Y5@NF4-A0k95BEy{&hkDrx{8wf`FzT1ANQ_(4)W1qq;xz-I)S{PdVf8uR{dj z=$R+z3&oYTH&XXT97h0S+wbwrf4Ki|4NZMmnQw~y4+cOf?{P=JcIg`D$2NL?2+n@- z^xvcQjqctV>P_2a<$`_>Otp|;%Z;*$$+w_7)EdD58vrT;DUQ-1UP z5~&*BoIiNmiL7GTQX>j3Z{@|wdmdf)E*Srx{Y1E_wwn;npCpuX|LK}>ht)JJ$>Y!O zKfda~`*q#xIE;PYEuq=lvxG&QAZ$lEyx*_=a=X4-il_U?5QMplqEPugp+eH0hi6Ne z6~RK%LKA61SuEXHDR>xmur-XsI&DM2mcJQC_pgg6MSJqE@iOe?uryVDmmR;^B56*c>~(thb$7VEIUuCigH4H%j5{*lgxJ7 zmiUFwi*=R}H59Ny_)&MP0+3e}+*8p(4XmzUY0;WF3{O|k%9yJ^NR}tiDci|%#sm1( zDzUB1VO@~`0YQoS_T=2%_L&kZbka;xN0EGsQGXjPd7>xvI zAP^571pG%40ARpZrvRpPzyQB6cJSODS|?sg0q}e&1<>UGBj(>s8Gz^EeGusNP7e`) zDt{>MkMo`9_(ZlCfKboS;tkY)9sD0={%ZF>cJx5?r@$Xh`{4O?EI$!@f$nSlR_Z^Z z{)7JGpl{&{_XoJ(kO3&;G8q6>{fGL~I|4e+*DfS`a=lqyTrQk@F$Vqr0EEH2`;;u= z^Xt0@n?88-C?I>{WjB<(^o-`zX}q&AB``T0*i-BWVjc)a`g z_LHk+*rF$>4Cm!8-h_&xyWB9*Z|=F_p1>xoNJ6ew%hkj6zHt-6c7zkody=@6)?}`# ze;h4{tR$3@62887OCrQ>i8?+7_ULk-JnsB(ed<%uP#>8AV~mq-y(O{ht7h7qQ#Yn8 zS*}6=jYM)EuHN-Ywv^?QohZ-CNAY=|8zS8ALr$eZKt_D;NFZ?ey|6{2MU9u>pYjQ^C!m#4Rm9&-COkaH3T2pO)kGzJ5RG|8d#>$I#SK|6fl1 z0rYF1Kc@URK2vIXn6YI`r`fh^~GCv7k;=cB6=QPoy3=y^ybldoCtUA3li_^nz-i{ zkJneL&CT_-pS2%1?5&Y^dkRL z7GOTDAf$E^^&bbN0mDL=niV{a41)!LA^49B0Hl0H1Yo{^0Zx&?ceDW*+y9RZfDHiE zKE?nB_ix)k`KJ+pY8}HbjKMv4fNDPK{5SD;@Aj{UDPVKW{#Wcj$T9I>tG|x@ zb?Of}hW?Kef5-vtQ*pk!0qq~O9XoZd@8D9Kh@%c5g&B@31WhAFG3@6K#NU1KCu?KvC;L@jBxJSPkhJX+67Dz^f>2)cFbK=AIPD#{?=B4e zr$X>8Nw|O-653pHc+T?;jitN}`-t-85CAz3uu5{3Mv zu~5CJ0U3sr`ApC`V)QDViP|MyVoBC@jMdC){MKj)>ug8lzT1heYCBSZ?UN+H7?K+7 z!79&l;Dy}X&5c~bbl679Id5xde3L@agv|2F$wV&pZAuNBrH)GYvqYFxnlme_nzW)> zab^p{BMo^ zW8VL>+h^D3pY8ANtD4x|eD~S&_k_UZ?_;!Q>0$oo^>**_a`~b6y|qVlfBS*$>6$m9 zC;jH6KYi41F4MM70LNcF-Y@^g+O@qn4p-qcZD*1uP16mz-ykz4Y1*b47*iXkrJ?Ak z=(5W$F0P|4?SuF*48;f0aX>`yMZ_BjKJ20oBBG**$|6393yS+7C@cD=559>1g5dW{ z+NQf+SN%lXRT~C=mZI%>tG4ghsQ>`qc=FE~P*+E?b2X1>hk7 zFd_Ue7XaW|eT5cKYK8rW{&7n|~biN98-S^M$U9U$_5NK`--0+fGj z0eKdl!WJ$pdVuW_|293pu>Gv6`P;PUE9duDsQ;@909)bmmokql9LRZb7ppM;TOvT! z1K`;+qFZ+IU$p#~15)n@w+!8>-$JvG<-FZ?^Vms^TTg@Qg&R6tY`^X8$4$X7;sox# zjI{dPbpiN4FfrG|G$1AtGrS0LFo#7xSxtu*j^WKF7f0j%07*ia`SC~;rGBsHTTuXc z@=SvOfXT_gV2{rSauCPh`~)bw5oAKE5ghrpcr>Iv)d)r-@!YQ|a z#|`E`7sw*hFxw^+g=}i(!sRFrj6)`nzzQx~hqgV@AZZIN6(V^1k?mVj*3qOHusg@1 z;Ja8}Bgqux{a~?mpaE>6AWw&HrH&b1*IeU6HJVW!t4S&uxQ694y|$@E;1D?ukVT^w z>6BDhrMlD9O^gQ`Xch-YUq&$5d<~Ja~dhkyQZ54-G${=3#uN5(KSd-POahS z5atNT0boHgQZy{$U|MxH2EXQ&eZ#a@ngzb%7W_RZSR^S`cme;B5jL1b=m3Nl zB;%@J1s4S2ZTrBYU&jBS0Cv3GR}}!!gnK-|PwrxYCB5+h2=-BV0L6Ma1ZWW24^~C} zLFRv33GjnvIqA5wrvRF}um-+O^OF_*u)8t;KPdx%0{FwONdD~#e#-{kTkn61`wTt0u-)_|KdmC4> zKsouX#|!tcV<39}=CPr+;S960{vek_nHd}p28;W3$&DmHbr~awf_=;;VU{H;KSMbU zliC+2Zyw#dOH&$W+ctnX+0PhGm|TdNE_p%%Z=FA#%jbh&5gdEiFbIz-)J5Q|_6Ylf~wreu&J zLx6z@2^>$cTCAdzi6W5!rPByAMN17JNOn(%4Xc>e@iov1;4y21wS6)d7+Mgh@ z>nQl@Sb+U8V-a~@|IfXE7Bm39%Bk4c71h5aHYT>QsD`UX2i3{??-jtucQ9b30QR-m zk2eZpclP0GLeNV*fO6y)4iyH_X&2nziu?=6kJ_9&MF#MYeC>Gv1lYu`^Z`D;OH(ep zW#ezS{>2N{pSS_A|E27g(*L;%t-T-&c`lpu9}V$HPhjcVH^9NI<5c1%eI`NB@X%#L+N!I6b^K zpmaOZy%3CIC;)}WABO*xxd3gW`KudZAX8)-c?mcma&!tzE(3okO;diIsl?UCkBLGj zRW!;7t;EW*)B}i*M;x$uR2O-M$RiyKU^O57@IWVniqDr0SJw|sT2ew{RFNvQA_7?}( ze!tF9a)Wdec2rF-IKiOsI1qPrQ!1>}Wqx1-{1=<|pELk~K!3jgEfj=SDTsf&ijJY= zb-1k$D!TkIPn!qqCLe(ZyQko|J2LV!-Y zp#Qh_%72>6Ee1%;e*tprWdR`lg%d!+ovVW?8TePXy+{4oEQtRW`>)VHtZIf|A;(qL zALm;IfY-|~U-^OUNwtc5aR2dbzgF3Qj157zuVD1<5oazwdV2rkcN;BpYSp--K5YsI}A>*#r7{)pm6Q3 z3qas_B(tI|2}YXmLW0m*M$}Hy`-rTeIz-+-xjTpaA8S6_p6BqadtTO`%`$84x8TNe z$PIwW!}H=aOC)8DT?nF3HUtybPm=YFhwOVH$BRdzqBEjM2zpj3`OpnXCdrZo;G9Lo z;(-MaN;V@tIx%(op4&)ztR^v}ld$9gwMR;jyK>{;;9wqzmowmYeweLQ4bC?r4+&(P zETovw%RGy*JOiP>V`eE6g-nOjBi5_c@}UxD23UWf_0vXE*YXIg1fHQI#^EfLGnwEA zy7st5N!@u}<08grI})M`MpB_aLY;=JYYc}J;9SsK_-_S*aVU{iK@OP&+-&ik%6ezPk> zb~=#%iu+%1f57|0YsdP*t}6Mf8hY-;nxOur>4)@h4|;OzKB4B|WB)ZLvN^TzPgLBt*($w&9$ItQG<&duDlJ5o3rE$^J0A5FPN zHn^I9aUQ`M2Pg+#Px10!Ao^x)0CD21m#~tBygev7$#^n2;^qQx>yn%iavrF)9LPI> z)p%HIW%$v#5G;?Ekv4-F1>EUbf~HMC4F9 zHVX}q_yFT!`eDRPmSOR{0B68R5f7l0Sg?GUWq|b_vWkMhCbNso5-z4(m_(W4g^49G zjw9Ge^UXVM09te0=ZvYZWH9G2=7Nw&32&v)DT?Np1ksoL(6h3zgVaN4H31Zupe3`G z?hPXynnY5QS*W7Vzn4IbcnZbLCC3yf_VA0{L%Lb#RF9dPlpe`T&6%dG=D1PEqaoX=@H5W7W3xxj$amv>#n2dDt1x?$^lt~E)i5c1J=2O2^1oJcg;nrW)0(V%6(j5Kn8 z&BB_G>AOzDQX@hJRtXY7z=3j9xKaOUV37hisK0^%lqC;kH?+3%mx>a2iwHhx`;C2x{#R8$pWEy8hmDABWx==C zCE5=spsM((X#T~aR01Vipj;&YxO4x%jLtu#^v}IMkIzO=h4^gV3hv%}vpu+d9Lpol zo@?%%zL*3#g#2s@?BCXvAn=pblS?HT z+QUTg6Mve)%4eAXu5c!SypLdjV~ICEwV@)6p{Ve}6lpz)4Gl&1*=Yi&BM(F+nb}AK zKtBKz1fw6{Hp4e2!v#pH5a4|lWh{uv20w(~B7Nj$Qn9ikeCiDpCfHxAiGF!k75h8Ij^1RJCu$08qq?X|jslX)GL0&uqe z`9I78d~!F|gW6H}a843_FC`NS@ox|^)xH8~Hp=P$Tjzo9(pyXc@jr3@@m1ef0K~;9 z=l+p~sO^dn^qb~)s?Hy<|34>_yHJz=lJCDA!PsMe6@*aTWyE#;qfRZpv?dN)G1gwh#V$Q=uO9f?fi=rPQgVFFg* zEV%wI@`X$$_^jwM^8Ea6Ea0bPpz&P1NB|`$(-q9TznWSc{Q1NOJJ`p&Q#kgSm_dr! z4BovhSiFF{@F<`nfItE?P+)}-SjgZbAt%rqD}1V4@`+9ohlDAVvD5YX`6HJO9pvnM ziL~|rEh*->%p&bVz)&V1zGxxRxMBk=!WU$k$F%%tm_a})f+@zH~;cus`;Ti&BHq$L{9WPv*mWF zHb-?n(Q5tRu;qc6K+_Q_1i(AAAbt%b2r^{ep)(pa-IZvEWD`P8V^bQXAL|OJS^4F|X|_%HPZ++G(Hp^Y*i-@UN^Ma$1Q6u@#2=i||Gadt=Wp z!n(5%SRq+oCP4jVF_}(%V%~iy3DEkdiIs@J16Xc7^fLg20uVt!3V;K70jz?UV-f+s zl(1$s#md(B&=m9lpokgt(R4E4qDWwDQIhn?5DZhXfD|&yhmiM4azMDVsWBo@Ve5G(`bj$C25ZB%~?CGi;FT+#u35qe(SQ z$}`O$erOq~uy|eV*7Y@t0gXuW26Cen8xTTm+17vUQZyM0uG7E)C4|@w$hbi_+O7fQ zVT|xyJv8OWF;z`$8*n-)l{FnTrby~QQ;}xKt(u4L0jFPwtFK9xZZv-h?Qvw3VY^O4 zr9fbC&^Dbz=v1#pO?KvR1d;Ic{xz#G`g|Emo_ikQE`0E$a=`fYTnT^eAADd2B`V?zR)eGH(S zhjM@;nfr&oue+=UgSH|5oA4j9PsaLgqyIum_qo5N@!t6K6|z@3|5#h`$Kh2q@Uf}| z*rxg4hWdYS>wGR8iD>zo2Lq>-56%~74lp}6n8yKQjEDM z{P4rK9ybA+^Rr9e33%Uz=gyPKJ&}Jit<{8n-=0ZqEVE>LGf73$NgJ)sY3r02v@Pr{<f=xu)y7jqh4n< zOBjFz1DgUt;sk7m29+Z+vHdR~Uh4`0~yNF+2+J9_?_y65O(klkQ zo&wmifQmg1rE%YM2_lC|{J*C!n;$+r;g5U0#MQdY-3K4KcH26?W+k9{vb$bG>Zj`$v-RwzzgkPJ zkYg4@Ib=k_+tFH(e8;=uWBq*meH!u^3~ z)Id;V9YrVnl*F@W)pPWmsnvqK1GNi(!J^$jQytPIluoIXr)tKPs(#dgEC)1jLuiC1 z>G%oR$BT3usJ+@Bh~7)|ssAkWrX$@2u~79?LwikUoiq&I_6jW-fiRkz4guJgZD{`& z51<17{(%BmR<~-@j?YuuMgX;a9ssQU-zxz0eZ#J{h5okfK0mIJc&6SaEQY6*0w8*= zj;-wbTQ9i(-49ok!1k}mg71$(T#)~r_*b1ii&YW-HfDUM0IJv?3!kfkfbE9zL9#1# zK*=m9$^T!*nBQ}B;eYzkAAEF>H-8|T5oGmVF9^)#ad6t@k4L)B1rI)fNqUppCnND_ z>RX06xc!;9tsNQyEA|W~lNsQDAmC14++5yFQTxjneovxV(DVm&%_BjtEDHo)?@hBA z%sJ#1($0Gb3!-U4weJjXV5uS9zGbaAvqBC<&^OQxpPid+QcJ>KU*^7Ro10G0RQ z^;@e|LXra@`|%dx7T_mCR1rhqu9Suh&%kAn%q~eb0!fGUwk>!O$nhKQY2+v*5rH%w znfwLAZbX-lAleGcn-Z#(1u+1Z*@y+1K~TeHX=n-t_`JwIrgX>~Mw1)xRv57?G6!u@ zk(4PSe{di81i`!0as-Fg!Kzd!V0tF4cUl@U49mLO`pPd38rm-*Y;jYayXO%_dKgT& zNFl?2d1xKY(>W(pJCV)?ZHWR>6)PPm~0zpp9YOZ zwG1NB5lG6BM(C=b6L6fFtIG!c2?~TkfiV9Z6B9~OSUZm(XN}16c2cfK^#1SkMiYFZMJNs=sqvBmsACm0$3nZ|^?j`F9wo4Y z#$QG5zx4mNgFSY+|7`+*Y$b=MQUGM_b@(anZ>>Lkhn@W=!9>;n7wdaO0igBAZ?Dgn zL45v$#qC(&9_Xz)JoLe5M#1gCJ$A3o&KiwaKE3zw_~FP_cm;49mj_4p@8+rM+!BvX zJ7E#5D^or>650N0wKC)>QhcU$VE}PwY~EB`F!n6aIzVib3tth;^V|M#{rtsholT9Y z;!)1xa<;l5^d2RYV6TT`pAq5P1Nckq1Trm~COc(CSyTY~uV$>ypuhlmMSJKcSPbzR z@O&crgdPa_cYrzae$fI%;W$BaNCuA;pgW)&6jfdh7`>Gx1#N(_kO<%`(C)a=L=KS+ zbO8EK1p`@4aF)Ou0=E&0fwUyZu;5*I4x=8vSp=VMxmOVWaEEm>XiPSs>3xMHi#2=yFG*RLL%j-lE*oO1_M zfM~3ravJ^>84!dVo&0xhnHHtuE%^SVABgT~IF!!7zO1P#o+3GbYE@`q(RLW>p-#1E za1>rg0aQaoyG0nV=@pD!r|cA7LS=9WcG{x(`U;>L{qy%A;MQ^%C}>3+E((Dl?fd1x z4Xl?zz*e(V0o_(b|JimG=#~f|#vklw0ZMR#C^w`4c4NP~dxW6x{A@Gix6JwzS%viO z%m0S|M~=AMu9pXVeX8;nD)6ry`tywV6dU2+RsVkZ%ZUFUKc1h*9&uAmvlfpFZ<)*-u|FxI7we&FjF zz&Q3~xV~AfZj#8BeNN$&$OwX*)fmNKSC_!Vi(la4=6f|wD93v*PIq9 zVQ(FtKk6A#3j!Mls+unyWDgpM{lmPahO(nJ0YTKjvZT8Bd7{JCx>POa@SAu@8|3p^ zBU%vVPYhu+x~)d5H10*!AcfNQ#=c~4_NuPe{y{MiLI-u*7fgdPE6%GYUMCySZnbw~ z#GwibEC2*Rq6P56R;i-@Mgc7Uu>zoL74VNgA6I)QAYDvoe2EHNyqb-@jzC2C+ob5< zsY(yZD1soGbwX@{3fL)t7Ws3Ed5A@#qY!`c>i&3_3~c?p@W_j_f63aZvUnu|P+$Pt zZR5%pXV^+;aGMycg8ygbQ|J`@vBI0cZkt~G9@dw?@~z*3{eQh!q6+r@_36EP?s0Cm z^Zek!)5H7M!RX$p7zwdFo$}ILcHLnTBCw6eM}1y4St}Rq{^g4-6N6bVL+XF!!y}iI z{*%iosDSI$6tTFX$iVp@zOR35OFomVU%Wt+oy-!M_t|w1MS`+@Ib2=7Sj{e1p|nm` zE65236U)k$wv3u2}}p=By=-(a%cofo8Rf?$k)d()8G}T`r;E*`$ugDMS}5&ax{}_ znN6zYLZz@&TkYoY!G|azZjcTiDw=s8w8#Q}_Z^uOJPZu4*c$rXIeJiVe3S z`gMVT&IShT>^g>S%jRG!p#%PV41lg+z)AsZW+hjt|AGQReYI)~N>*c^3D{_qC_uBZ zPXsFZ{8(!ZPg@tx+55VZU)ndDpa{MiHWD?`2! zb=;`9z0bA^U_05Tl7FlX4p0>Ql{db-V;11}3&{Vs9zXu#hn|9aIT##X$!;*Yx6Gg3 z@4_bi@ak-C-yVwG=;(cpG93uu|0MpJw@i5yOecZ^3XVLQT-q4t=gS$-_b=?}WKD{E zp8Wa6&9vt~x!`9_UhxnNh{HFJ`Kf&5PggyA1)Km@dPp=t;_K|_h$QBEygiw%btBqR>fZ!;Os0OG>~eNoUth&S*&$8tG|@cR{}-5o-8t(}^l0uM+P1^Ea* z45^8boN}fXUAa!vSX|v|H(OU7hoNpA(3%57U+>;J1XnLtBL=`f0KaA{K6f35GgbUW zQDk_gh%qP>Zcy$6b$N%XagGWKpKKLN)G5qWSp_3 zLE2Nguw)x^jY{GO$Un40M3+)fjtOr{rm20q>1h{MNSHOS`cnEt6fP9g)hXFwP zyFdW2+qDGP9GV+e6ZQxDM4%1HaoYf_OQwF|>tII-{NzE|_@ffs-E1G?zi)*6V-V%7 z53m9HtHE9f_shQEjG;J8ZzRhzpPm^zlZbAkl$Rnf|kPmM^L*mfq z{WKQyn#0;+27&@y1Ruy?F0ydrvyikO3=P5;V0fEnPn@o$f#XYh{IekkI)f-Uc#BLF zaBL=x&rIVmU(8#z{GecZEpv+EIHS`wiQJjMY0N*`hsF72HuIV(jU{0KcE>LRD0k)^gCU`xyNL34q-@vOy zZ|7fxDe?g1u39TQ1*wD?;5m4}+wTfiINx5O6SN&BNJHQma@5JxLwF53;TMr-Rs2z} z6$U{5H+}$#o{LmK$(2F6{OW(o0#qKs*_bldzJPT}p&G-8}2UyCgBSRLGkpRqkHJl*OXIVa(_7i6Ph5qmfo_w)h$%btY zvjjarp3J1tiJ%BJ!1UqJ7l9Zeqqkn!qR3BHFJ8QuW(^MVEw7m8)}ANBLWCXJ0O6MEDtBa@>jlS zu}}{RVw0&42VUn;W*lb7KzPUp;;~HN8DN74kof^hmj@;Z?jI^2{>rtDtIDqcUgLbUf zS4iv*W5vMa>aP2-soaZAQVTq)G0K5kDzn-mt@AEM05M|`+0B!0S@vI+;X04W)Oj$ln53BycTvnG-eiO-TY9)9m4O@RpH3?oTA zDE#<-0Q%S*tl|l!E4JvsndpTPpta*cOH-{rFhIwFcRH^xjfTN#ZjPKAEAmZQX|k-P zONP13n?WoC{__|I?m0Kl*~ow=zKE{kj$s;+E~iaBHr(zYHvsNawA^J|&BMd_(pBO5 zHw=*TLA6wQ7AZ=YRB7N+XF9@p^ z`XkJpVrAb{G?41ii%leqph%_H`_R&WZ4U59GGSFv!nY61#(p_&I=#Q!QO z8qy%_@-O;U1h&- zULi%c&Y$$Y`Qm2voJ7+jVC;RE_ZblM;l)W7vNv|h3`K;?4KyXl0T~vaBqE?rK4wkI;bCcc z@OKD}$hP~k!uQ0!MIGi^>lWrDFF*g`x;6(sMKJ@R!9|QhrL1XkAPkazWSR%w5k&y~ zxoHqL;0`;`!N8Y2h$jV>WjuF`2P|=w1qKQ(a1Nm!+K?SLrcr83 zE6SOsTM>m{S=V(#zJ(;9?$v)eXoH1fEbVkcL0}GsN#~S1l{B|RRwSg za7_PkVF3LvR70(75`b+-3?judkeL7311LqkQUHWfSq%oS){YiXbpN6ELBNvuzmEYF zjsLrFTd?haD`0?y^A886*u9(v1pDVz1%Un^l?OoV|96R;@5?{pdR&zw{+cF&6`LUhxt3GeCEKE7C*hl;na+t8giy2A%+mHU7rNN2ial)7%+Ed?N zCs`o(j(q>c&1|SD@Qrh^=L;b8%QC;t_!&&P3~b-OS!F03vZt27vLv?m*92}zLwOwA z2==koD+oTMAA$|b?k55ZLXgEW@~8*4F$#~~$4^)S1KuRFreY*(AC!M#Xj75U=SeMZ zsW%ij{uzJ7F+wJ>iNy~EBoT6+g#sJQm-8>?RAcB1(NZw#Inz+GR3P(#6NUv2pI_z9 z{EJI>{-MWie#KqKnvolktsN$s&c>_<5jXRQ!GM)L$df#jbed+t5 zrg;wNzwJgK4A4z88&Imm0*hyKNVG#)En=y)R+>Wpz`!d)S8GsZuS-oU1+kbKriVMr z4=qp*#Vk{NxUR6^fLq(Bq3HEXQ~|c) zf{1^i0KNQSbLBJgcmqT=r)tPb5P=mXn;ByDldW#Tl~sv!Td9pyNR94}q<> zc*N>*ebieEZ^3)k4Le>HP^und6KgfkR(P5`+RE z5sA#0BO6M>n7R_(|(MYFfx zjB1{m+0XPaLYq7(bXxJUDH{=E7%fq7g=t+Uf~^%A zKsCv}Q9=Wrwo8_2)ex~A2B<~kOfCEel{5-Q1`?V_C9%In19d3bL$4hMY#o4NDsWL9 zShxVM%`U1GKm`N7>;Y6mf&P9ug#y@T0^+(H|Gy6ck_($miRb$}IR){}jf8PNh4gX3dQYnS+5(?1w z-~l|w=>O%_(`9^iboxH~q^D(XW+%PLskmSOXVF|7U47y{cl*c~#YlNiP6nBb=w7g= z3NP9`rv3SsGtmB@+)OSWDb2c&vo73VR}g16eggTQAO_%MH|B*H?ZH@PCI?E)55x=6 zWH<$Qf;b%VH*1Me#6H`zuNe1uwCc)Me|mBut)5&w0-fN}U$ZwCm!XqL-pWrce8$eeQ!JW=ixa^h<9yiA zkoz11BriWif%eica}Gg}sTew$+MZuAlEMff;tJieBKH7XaOPoF|(F5@UNxdALZ zq)$CNlc5Y8aQ#`_=Z51kP{w#mJxfT967R%_1#fcuYEo9sF-p@2VVlQc##`Ty48D+&M} z!1oU;1px7f{D13HdkSDH+7-zE3@;pC#s-QP5PrO#ZBW5oS`fRU%|8TxL;E4$1YQA- zU&H<@U;Elue*U$uzyW~&4+!AF@jLIo?|tIY?UQIYl6(Dee0(-|cx2Qp!x0|b=fpAV z3ij}*$D;!P^60(UI)M)_xZ~OE=J}J$;mzgr!q#OMEMG=&WeC`0#=l@dp|xd>BkX@z zYFiOz*=z#bAmA@D$H`7mKOpe^7teWq=H!e&lcrN{Z6j~MPi+x4pLqQQ$eShD0Uo?W zlt6rcgdOs5+B@poz1#OjNADE-Cw&(0-r%?4Zl2#T{-0;Q|KxqilP6QpH`8e-IiLfB zN+>a)@$;e_j9eu%0SAnQ;PBQGMH^4^EBCN{aFDwP-2;xxa?f;4CX*50c+eYCrY2cj zH?CZFxilR&KmTI=5SCaG@Erv)jrv2e+fhWftDs~mI2{ZOS!NnW@`tuRf$uQ9B9MCuH*m|1W709RX-f7`8>5P>1&I4ke$L;P6J zApbaQNx&%et0MnT?T%#xP*nauBQ$`Yf8{G*``(xDJpS#|?<^OK`PseOCnqOo?-~x= z5zikzWQ57+_{~RI?3uA5;4OJ9+x^~vVf%f!{-is{)B?%&$!fitJnF5UJXuK=$HqJy ze^G>&J?ZuR%M)h^4n2%@k)*SG6G56y-}l5%W;4KsnRN4rT|Jo?*^G<}SX*g^G(ONd zR$u6A*`Gl_ zyu8e;=f6*s%eDU;(Z%mgNwHMLCwY8c0=jN;f*?;s$_h@oz$Hzm-8@Isf}XB*$UoTn z2dgiy0Yd6^SHi@mw0&o_#EhoOQo>zoT-Sk2L7LD3g$dMeJHb0 zG(Q^Y7}hg7b3KF^kvR!j%@_sd?IT^_Rn5HDd*n$G%J1}yA2>&*`p(D!c8PaT*Q4un z#MRA1-l{p^{iio)-4@7uA~E2(M8>#(BLB4dFH3~G1r)gP1xPONe^dY!3|L_RQh0QpJ* z?5Fs56+GzLxyACowCnRznE$FlXu%1@ty0{-Tgp5ZPRXYU@K3g9g#Z7&uYB!W-}>^G zpMK}JSA#qA^ZV~{kM2Eu_)$6Lr15~ak50zH+fKsx^WH?#MfY?R^v)KmDN^y{A%u50 zV@Sh48`3#sFK)iDl5Y5ri(>)yULFLp4LaYEKV%u8{XDF{IAt@r|Ak9X{{2VKeSd}% zJ{eDEFCf~?kWW^8G+{EypIOM|51}D=yx}L}4U&Oi3=+^U^vDoDVKPN5Kf9T+{_5i7 zeeZkt&bwpa5GUh{{`3uRru|I$^B3L|Yv@Mwi@d}D0_ZZ9st~*ol~%cLM#V=ZhM0KWi$GFF9c}T$`GJcDF8wQg!~sHK)dvB zsw6_A>}7maj5*z-0(WnfQ$Oo_9>5=pqW?+(e5WM%V^{Hi;RXP{e{X^L6L%kCUNHVj zKHxJ@07|`p*RSy?k^3Fp2dg9&lAlv8Mi8kU#u4R36|f3ybEb~q8IlRsbD6D4CT zaWEG6o)OH)lM5T}LS}P}hy;BMxq~qmdymjAjCaQuLkR?b(MGZGGa0piY>3a5sH{U;g_h|4?lHdFuVR&@%h=Iar zJb&?#6V_t0^!G2;-|&^2>GSDxMFJuY$A4&Wpv?{J<9J|N5ziHqsOKgIQpB!nbPR}n zo+EmL5;%Aq=N+%XGb)IJR_0{m5yliOyL666-R1RiP6~J;%E-y+KQZP^F6Rt);K6;? zn0sdM(c5N?Ky=LMus^Pad=J5g8o{wqLE)ofP3O9Y!uugwTBgziL6=X7pfMajV-#?f zttLywp!Qj*>S&hk7;rswv%ztiu4tg!F^VFk5YW8SeUO`!3BMmuLYhe$ne|82AbOB& z^4}U60JtE;x7RGM2FXly0t3`qG{m0d_6yujBW=f;L@ail{c8AlL;w3hAUfgySSc{g zFv3{`h9fcqB{q;kFKC;(;i$Z&zUuh@2dh>hfg}LfAOSDykJncK`!L`iH`$OK2I!D5 z=jF-24y6BO51?!S-e~SA0C@job-IMW-1zt&23RBk%6W%~`YR9MJB0eX9|EBG7a|E+ z-B$qk+IjgK;2v9X{jqIifVHXfc?QAXrU7tO$bSL!ZL0mq4xnGr@)zrSU;g>GzxT`U z{v7PTFJ9lebN2ZDeRp*9+0()BlnEY==7ZqmZTEU^k2B;GUK#qmzH9qy5OPDn)S@eO z;=Z_EU0(L2DS!Y&zUdFS1rzY>QNJe)gWycGK^5!`1Al19Jjagh%QYZDTkP=w{l&?8 z#h3sQpFDw_+fxCYAKypnjyU;z2KLREOJ;D@kqDd(R|NS>eDZ_R)QrRS@yXhUHn{9h z?EcZG?;W4K^Yq~uHAbIC=<(Ab|1b!}=Wjb=-c0$={pYAm&i*|6^B2|^Pae%wMQTK> zYD*qel`U{7J2Dz(7ym%>x#2EyZr%XoS8wMap5P>Rzj%J_sqdIcTTpr+>zj(M_l+82?a=YK6!ZYl z5i=dU-!`Ys^*Pt%yp|3uA<}9ZsqO{Nr&?hx^&pZBJuy^x0-h2U9e*XOOG6Dr4G25E z83RJs3X@lo_#4Gs@dmj|?@V|#Ei2L?#ga}%Rw4F8&1l63Nk1{2uDS@QDLFVV1A*#) zB*1iYGe5L&0C4y}cmRL__d&oN4-nTx+RL7(pV&DCjhBf)Me`qM0DM&npi|-%LSR`)gl@u)eGSN)a!hzRk>jBJf3$Zv*>Pi30cnn-9P@ zs3LuQqGbDguaN&=L-tYhgRURnM(*zyKm5wKz6S;H_|~1f=bybd8r!1}oDAfn)6pnc zjDmZ;vSq9P2& z$Xo&poH+_~pee=EhZ?=!b+r1N+=YRhN z`>mA!g?Eg+H?^if|CB#l{+|I1CbP@uU+`~wh}Eg6ZLn~$1}WG-OjDK(Rn=goSbXIZD3@?!z>T< zpG-KG&B$mtW~8+kUXpVvk565YjQNNaIXx2bn=T=HMSi#t2grnPI z0id9goD65f>CMV#{Hfdo_XV6cTV$=CB7(1C*<@~?DY#r+p7b7}i-#?hKS1V-c0UG6i=!Ie0-ooQ(j1)*CEe5MT56>Prwet@ z@fsT4)YS-jK-FrVPUM&vXuXu{VxTxd7jzsDgzLIWB$ zfJ4r`vPXzo=z)~>yy%}+1*rc@1+*akgcwZnf&ZHa@M;6lKW+*EDh#k}0$6|mTPhDx zj73ktf4-=auRu}&guwha;;yW>akdJlo@8pruIrBF-Lb(5vEw980>vR|6A}WnY(t0b zV@d*{5uiatRX~WPM2rv!#3o1)Bh5u45CT+nfDvLT0-+Hg26}-6Do`OIxZq~QkKlRj zlwseOxA7Wh(og-)_MGROgO6So6C(bH;U_i!Wcw2ZfWF^Q0Domc0mP|)7=drlQ@{hxhFIfTP7NkECdc1`(A2|Iq*4wpit?sHfDBqR|J;v-9$M_PwvYjFD8nP*=PeslAVDg&M81)S{6p+p%|^aDoj`lE&)UwF zESi1D=7jSY!RJ}lrfC=GzZ=-JDj9;G1^9)~qJ{b?^ zBWtND)4Ru%DOhZvlWu=|9p`{uTHQbG*d2@@I=Hp9hgJwSvVDQpMS=hR)~!7j2|*>#OFZ$L&X1K z6v`*#nm4j1S^sFpD7i=w05BCt1`Bh(^oK%AF8p|8aOhAthhL#tI3X_+F@-{UgowBZ zBgW9<&;BzjNu(><;0*ku6-CLv;sH>9G#xlL`1Ym89*I6y+#%!=1&lg?+J9aC(fD_5 zG1xFtQ`B!SF+lYyfdHb}KT!C#MHf*>i=DUeI&*w+m~MT zV|WBV{_z6&hx}t*g8mPG{AtKP0)7ZR3X9P13&*?nk@P>o50t>2yLY~Div0i14w@}I zQG&GdZby*Hx$JRK)nTk)`bxbZ!?8sk$@`+QyfH#M-Sa#>Z%@(i^SmOQK_Q4h0*7*X z+3r*T|GDipqW3ayDCs&rleakJ2u5=ml{?`0jo{@^&ZkYjyE`4Dj}PJU0N`)8G9dOb zp$Tw6-~jB;r{E41sxm0P=m^{eRX@jyEA;LVgcC4}Us2qBnb+?R-u6W9di|vvm2Bmv zd~-L`Fpr0YhiFk*T(%!dkVo9&mPTZGZY=373Kr8omhF>U^W9&Sq!CK zpeg8js&@~rtRW2$T-jfR*q?Tx2bMeb6?L0Ud=X-iWqhcu8S0dVedshp#QmPA8$?D~ zE3J|~GQwYsfPlNR*)?7)2it6~-sX+Tf1!C`q;0Gxxxr3yeO5}!g2UiI9kKo}E zEkKFuVHGN74dWR9#7lJ|$6j-A-w(;ikhn%z(SS5~4&D*NOU&Re1pk!t53vH*;%va* z!vK%;650N~SKdM~g(B>IRWa3ALX&IznPStoZse&>)d*`9df@M`CY_ph@GUC#;i zY@;^hMZ3e(`(3_R;W4)d)D}so_gQ|50sH()rVmMmQST!OFm4%O2Q_$WMJmA@25zNL zsFxcOr-!O7)y9^aZPgkm0OqFi>G}B-34k_VwOzYR%JieI!L1SQ<_WI{MRp+VHh=>Z zAitTS&q}mIb3g?`s=|22^;Zk{;}vV~AC!l~;$X0GvsZbAwRd^`KR3@WM%57r0T23|n&>?B_`$Hz=R~pGD4X!}u3p{S z3Q8^un_ub%JtDjIzTw*ddq9N0d#JP5_d0r~y98$QHT(1$iU1C_XhtuwJem3f{-Es` z7LQ702?DMI4A29XQGJc3oh~2fEZnECcA5oe+zt0A)>N4albB`9^9@ z3`IwgQ=Y%1SU}^6G*T+z_8UrQ!D)a!0}mJ)z-cPvwj2u~KzsPQNFuC6ssBY0kOl#X z68{U^zvSQyKbOn&@!Mz=?otZL2&x*9Gl;$#PZ&_D;bUc?{#n4UXp2kg@8AH0Xh+b1 zKh+J%KYA|pk6-{$Y@fPR0Oay3E(Hv1-OEeh^K-@Aps82 zvk$-abI3m=9`gV2-kp>0Ljl}Jd!aiIiTtDQC(QrDJ9jYu>-6A>m%Y^9cXyw4v*aj+ z#$=pp%nITa+YP#Q(5VL{o~tyn7)wWvQ`bnj zUm*2ZzuV>6ZpUzKWC?UVM3xTZ0Mp=Ab&XBnA&o6n!c48%Ea##6e26AP3?yauD8+Vl z+ZI`k7JV(B@g1Y;@m)S(ONhk~}bmUA`OhV#J&lE)qp|@$>iaD z&9G1!1c>D;8Uz*qxhit6Z&0$e0t<}wo!f$PZ>a$TL8NYU%L zr7ag_LqPh$WYT3$mQ5@M5S|rSI`n|yTQUoHKk3V!Mo-F!&mFTi+)W1MX@@vpDg z%lKoBfqQ?TG!_6r%m@5~dlC2VulOj+T3oZZ3>`q~7Gdqx%U}BUQw?Gi@Cz%8?}7tB z%zwoGy_)<>zv;F2E*O6B>*GG4vFtBWfW-Fy_*FmueVqW-B})LgJ_tLB{O^7Vi2waN zcTR}-e;fw?{@r`uCuaZdy?YVj0la&~Zk=(ZayA;V6LeVk{)9Okpc&GZ<(E1u=_>)->3|5p z=aSePD)fU6>a!_Gmalo`GGhv~m@>9(1CD~wEw>PI!y>OL%PVZDL|1fQP1(JrP^;!e z!abB&lJk>k%V#7~E@UyfZ<&T>C6@^H7a}xTVqHB4g#g;12ar!U{gjeQQx-_i2@~di zX-!m@z*#}mpH@@4=x835gpUx)3bE3pX*spzL$bke)l$F#mXeWJq=*RMRMnsya2mmY z5lse13NA$W2MWNnSSSFTSlC7Pm4&$dL+0I3|6UA$m5!@}m;W(A|LbqX9RT8!IOQLo zlDm+6vXK=J`@%6=skjFaIg$y0>8J(Z3TA<05&1t#Auq}Ri4y?w zFA4yF{h{vfvi27nx_-gdFH-qmdOWH-a{6(AsFM@`PA*ISfb|m?5GsI_2F1QW%oWDN z2p9nWk!1fvK>qjc-$$YE$({SRKYr)l9h`svn17w+pyTG& zUzRC!8ysH-iO1D}1oEuQ^N3$<_{7%`26xzUTQ2uAqvG@t)0)$n8b~#AD~rOKuv-FN z;JqWaMoWY@o7+362yG5I#Ob^V@?fvSRLinxgjL$$IAO|9807W)BVja^fuB`JJU6Nq z@`j0se%+<@5PZL4p@1526>ciiRwJ$@r9s$je&tbzfXj z;Nu&HM@ExcdJ4ZgEkjCu5;FKJDE>DcA0N;iCyh^&(Lbbl2cBEfJUGXSw?WaiH`@HFUQ0m zQ~=@x`T#$Vn}5gNf3$uYjs0Bo_Wi>t|8V+A>kqX5d-$aY+rMn+j|Cb4WFF@P2Z)~j z!6%>-kpGLNA9z1W_`&+a;GdiT@`n;Q`93gz6#bnL-p^;hdi0i$fBfiLujY1(rBY@0 z6*o0n;QEuARANedNaJ-Yby0#BU<-A-+%t-xot6JU^C0q;M@P1>0WT@`|E&y_$SkcFmS9Zx|aD72BBM_Dg?geF@3fdx2Z zv<;4KJomb^o?PSMi4-!!>^exv#cZQrV>*r*kct0Q$j7gOj{Z*Q0Lv9Lr;+E6kqMk4 zAm|e(V9;+&s>9iUX}k@EN_~7OG{d+tAITz|AW-n+Y-4w$P%D-j{cRcqU$%of9Tfl{ zsT{JTpi3Y40o8T81`Be*GVD7YxEKvYYnE-LF#lt%%LXM@Z#FdyA$K&8r~oqR3FH_Q ze??hMuJri4mo8^5#doZlDI(V&Dpl4&(jN`KT+a6ts)=M{Q!r5i=url2ww9;mlp=b? z06-fDDb!5l6hhk6!2uGJsf4D`UI?_`R1+Rcit|l1^wu;dY55^i`v^5m)cb)J0BQgn z0VMPzW`88^NiCL=Xf{4JNCLTB2(<=LA%_UXhZb?7(Eksb04jgoAX9%W68O6dSUc7~ zU)e_DU<{-Z_ACw#u!m{BQrhABc>0K z2xKyC-;}sazbb+ITOGB5_O-$1lNvIT<>lP)ym)>N;9vw20161(4ZsPclOHmJt+(wq z3e*!D!|N~Hcw&7sVT@qs+Sh7W)19_CT1=%KhVw?ObUa#@pr;3w;J@7h5tjLCnt#C)|A2DD>N{ z;b4Xv<8e!D^|v$9aiP!{4aaC6Kz_#aLKR%*TA>a_RBK$vCx$}-$u8ISyR4>lx)$3t znN2wTH?C?c7Ht0V8ghQYD%(M50N>p}g#hcbcuZ$2ytYcSj>p5H2h*`)bbCw7Te{;L zmJjV~jl!jHT4V$@L0>QXVbiDb87INWykXfi6?!3nz9w3kq`>42S!c=TX*!+o1(Tza zeyfa(UQ!lKxcDZkIw^IPLsgEfI8~W6`CK(Es1V0934tKxSZTuX@kq%Y6c!{Hg#(}t ziwN$cYylbcu@vx;$1?-dZl6d%6qz5h?0D%8LisheZfO`=??{du7Vw5+o76W@C8c$Tr2M>V2fI$4P zEd>9^@BHy6KK3z;0sPq~2s_}iy+0xS{rv939|H47z<&oqe{>5908rphG4>lO;NG1d ze&GvuZyg?7m6*WYZZvAy&6-fRI(5EV778HZ=v^RQ7=&8~JwF3so=@~T$JWbvq~@AT z=A~~#?SG#Lucz~Y)IJ|oJ-zEn$nQ5wViQe$&(BfSW6~h~HLut_ZRf^~^C?+vpn79C z0K$Y5@)J@^P=4Ov)#b!1Ui8A}z5@Y3DUB(@gPWUZFErSNH{P00`h#qCmaP@uFV!~6 znO9eGH;oXPXG>1dT7sbg#I4r=BB_A4G}d$Ylr&(o&5ezIL!O+?+GmLD$CHb(Tzzmp z9rovEg(1v7h7^qF!|{Y0jfTSloQl>(f7mSCEEfu+eqlTuM@~SYP;8frHNcq26ix=v z4gv$JjlN~+yrBd9H*&PhNIL(-RSL0^0lJ_gX}0bfr2QXbirP|A?^NI(8Dfq{%`Qt? z1sX=$;7g{$(WwxMBH#2y($iR5GJ*Y#`Y0WQ%5b}qf*b?2Tc-rp@KZdaQ8a7HMiFeE zq~w~GVJNZ@Vy{r18P*LID1%Cwa#$5rGDY0dG~oNXXpq)@q9;N!C=YoxCwLl8OZ9nSxNh$*UxGPvZFa(pL%=_VgAb=z|x;61A_YFh>eQlf#@Wv0f;OAV&fl4K3*9A64m~v z(**sG+ZG}LK!zET=pXj~8cw2NM!1TFAJM?V{KxS>gdgkP3EJ^R^}3hhAE)##yLaHd zyLSl!5Z%ERH~ac77Tkd!{P+jo{KU_``OSsP@TJ9g@Am@v|KSe_N!XH91~&i|I@57moQy?Oy-mjFW3J3KO>3<* znWOTTj0PEvG7M^Q!GQxfpPx?OKg_;*kf=T5Wv|&Rz0~gLVzvB~m)U$~W7{pxMrZl* zxHf$MMo}1M8?*j+urb`O&E6)^!fulvdY;5oUMgOH^^0C=u=NU~rwhfJSblYV*xcX> zwSINl&kXvlexq@Legm@+B%7G_!Mrx_177G4#~CKU_Tdy1 z#&hh1M!!(ZZV$7?LAI6!D;C5S%dKmWegrihOS`6->H2kv5)IeYg8(HFtfiOMkUm7f z-wact5OAWzu(agqqHM7Yv#LXkS3B?tJX!Hk0pTqfbvR2&SZPsYh3htFBtYHQmr^A* zQFy7t+qBOLT1Q)>d3;C^5-F3BJOK5kFFGLN>uDz}uu0MY6nWE$fh0a=fP+iuBxE^h zsR^8g6v+jE7G!9?NG$qV8VsM5mDV&dA_;_YWH1o`cv>X(nnG{@XNlZSF2$0)w2*QV zE0}V^hcx;;&f!g5g8qLopx3m&WBS#9c!v1ezmo%?q60+zQv9*UAsJOd#3%q3#2-?> z@Bm2ok1ZJk07)|akBB}@KKALw5Hq}h|3$_>?*A3fGK)FDSY-I`)h`h7|3m-(TPXhl z{ulTAhyNcdftb*@u=-I{4*-B50)zz+w*iDAAS9rlzw?)$_~y@kKwJiN33!MHVgFH5 z_~ZBf@W&+GkM;dKME-9b9PNDk^ziTq-v8;r!N)(ox_@}`mL81P72f{oejve(u8t@D zdWi)Un4^PI8;vuIE=Om2b*{^zF;J#n9=ByA;2S1G)1~cU!EKFOD91mZT%0%MG!1!% zgbQsn>!fL}3Hd-1P*Uxnho;p*-Yq<8x6W#$zDg$pKH`A zJbR>n&29B$>_@zUTG+k;_Y23(+6>;oDEBG4*;VN{1309^s^+z*4^a-y4H z$_#CYMpvSEw?ro-*Kz7L&xbT!QM)XMCZtIpNrH|<^EAiNyq}_Vs0-bryNqaRM!Mq$ zI=zH~V8>RJ6~AjUy2#S3&Kk>E9&G}t6suHRN^|#V+JJs__}w*&w(W!k_JOZkil$n= z;)!5Rc`1)B_`Z`)RrOFdNC*yof$nLtuIVXKG3=-*RngLuXe)A~U{7Y^6S2m}0m@Q)o{L<6i~kLW+9 z|HQ>g@%WE81h{sI{9_T?_?3A2=i~67;Q!zG^1XWyf3hO+j}gE>%;{l)&Kq-kBP|eD z|6v37@4HYI2p7m4a9scV$0+k9KEQo^;)h>)2)zG(l>bAgpTon`{aZi%!WWKydX04c zKm6+L{kMsBmW=Wi`{-m=x*-*T-kG{Bk)b&%Uye<_$xg6VF6 zV8}IUwTfGjjB-V%X_d8Ez#>a*i8TyHVtkA!Oc@6`#pp@828T*!NFlyqX5b3>NB{tv z*LXftqAg$n3AW4oEW^nRYorW2U{y~~vsXh$g*)FR?1ZKC9L`>|P?xacD>Y zc0Y@#EJ4@0HW07Fi&!di)f?#{d?fY`*I|;)~rDY~e zuO+k;E1SiWklL4FLq`v-#q2{||-ApWD4U7`L_ZLs#Q0RKsBSdU|vMT^6j_JhMB z0}um%@h-VoOECb5Z2&ch ze;oh+^yJfbKK3dY_eyXY8C0*`|JFFm~b5NZJ8k6ge47bN2U%O6A^f&61f{DH{- zzx$={e)=Kd{yikjU&#OA&d&b94M_Xp(dkbi{3q!AaqG6*>sh@mi?Q!~>F({r5}n`d z7w_06DShz#9D%d4B!M^!mP z#CejLfaXW{8l$rj+Jm7F*c3JVFu>ku3=n#+Q{vj_2HxpyVhBK+Ky&4BBYXU`S6okw z``>udD?a)}_qp{~JlRma_2*=sReyzB9ixQr{k!AgpvDa^K3|pRPh*>d$)joi^Zo7i z`(Ik94Tghy{iXmV(U=Wt8`~IMQqOK6ZXZ0W66UL!VPP`&rjJ_4JfNKr^a9G79rqoZ zo1LI%oSxp=vwOj2iD87@jB8vw=vb_8UpqZHxt89u1M6^SD|ch7x4FYo4sX;ebzSq3 z(+f%pw+;$ExK3oImw{U)$S0@*Te>f0`~>ZoTHv~M8uUNQatul0cqu?-P{LAK2D~A| z!Y~kUgDsh%9pBbPmC?Z>^eaZlF+`40V&I#o!GM`9TZ{v6(e){xNfcd*^(+=3zQZ-e zG|NJYL%E5xf`W7frOLEMs`pIaBkjdZ&0B&UXBiU&Yozp2ibiNa)QW@KNELz(NP)%= z=z&^8g^;X6G_*C+>zi_7=36Yj8o~n;rxf7{Uz$O4OT*a~)bthWx|+FLHqK=x=f$$3_2Nq5%l`pU6J8FMkWQI8k7~F#Pcj+hSr*RJ|YB ze_UdJIoks{z)$?(2aE3i*kk=Xl7A8_-2F6403O2jlXbGQv$J=4w70dnb98j;2ypzZ z-qAa@_}u#D*2h;d_v_x?o(t%{zPp<#j;8ft1{9rqn+K=2Qb+TeOs3EWs1JfpzR_g8 zt|S#X8D(p(o9UQ{A2M~Y>oAuKa@}sj+P4^RVEgm)d~4c9$;~8h@!T z?w_7+)@~e~_CODo>ht*kK-}OB@4rr0&!6+0x4rNgAI(*6KIes6b@(>AaXc6fZ>pou zqfz)9#Mzq)?X!#Kqv^#lEk7y@ht-SO;Qa$Q>BHUa-CeFWs4utk=y_4}B!<+EIFon0?!2@Y(hgk^&WKtsTFz1t)G0ZUS-F&e`o z#g|5J><}k; zDL1%?0*8bK(2%tjQspc3<3vE*130b)(Ed0o`1?!y$A$ZEjTZTfpNXA*>@U55%SFYl zKQCQ?_z0noLBJUNiwO93m9N?`^1 zhNg8%XC%S3474JpImQWP%}gn#AEI!uK>KTYhNW1;T~o}IDx>HC`8&RjdI3*Wnz9mt zF@$PA(F;vkBaD8tNhO$qr=(Sdlmlp?;sLZ5ks4GS1=62zoDiUZsHB|)7=oq;%-^A5 z?1g3pL;*3S&|A#SJ z@Xk+s^AoT7K?DT;{IVk`Y5x)B643|%9{}|S3p(KN@Yc?a-nFatPB%b&iw+i@o#nmv zyv4e56`n7dKIL8%vh_->kQ+)&9niSh%F`TQ40M5Ka(a-l-OvomT%85m&1M6E=dvym z+3RlE&E*8UA&nsaF#VJBM=<^{WyRqcp#6Ct_*s#|NZ)ZWj{<*=;DGJM&dCXQ2`5Jf zJ(%?+2X%+F;ZwGYvpVzA^=IwA=tY%RY^<%kh%dkJMeAH`T>N^XdeQoP;ceOG)7?-$ zA2u&`HTi)$c~m{_UsR_z;V1YPeR}%&L8f2Z-d?|6A3Q1N)`}b1Yy)uL6bb$bO8-ag zW^*ivr2x*5duvl)`@+dlZz~9HeB4Ij<7#IoKtGSu)BS5(JGYOHFzf5iet??BqZ8sds5tVpB;&u~;Q=w+f&gm2 zXGC+uNM;~Zkr}`@_`nYmyaw@5R8pQqX>q+@Ob0U6MQo4j|L3Cq2my@bJ$3>X*N?G( z9OB>~=Qu_TKpazKrO6L8Ks5YgK?GXGg8Tn_`X7cDMy4OG01mh$3LuUE{viA(+pkW) zfA75j03z}q?0<~?ksYKz4(5scKO}wpzK_d?52L>RsQ*RikF`h{M$%6RLGK0rhuq)8 zd*8qRX|R6a{6qYAZtZLyTtzKgrBvG6+uz!<-K=!<6?gyELFa~!9#@6oxtEnN`r~Zs zyYp!~pc`$mY6G^e^G}r6AR~|#08BY+bgh8XB`K8)x{aVKWa|y!ovr6UTmsa{4r2dB!_)1x%KwRNz!ulR?DS5eS&dV&Dpr>FZ@I##As4-ALuTuB-%CM7l} zvd5RmDCADhW;n~Qs5-l%udK4&9b1BPuXor$hqMN^k}kP0@w7zOeX-=Lyd*NR3F4jZ zU)f@H!^h|~-{~$dsdk`oIo>Gg_+V0JXb^t#iBOnN(4C&=ZP#T%24WJr7*4M9U<7{D zC*gW8P^}INiR(E<@C^)sWnko~ChB`EilF~t+K_!ObWF>!Rfh#^2k75|E1z;4AN|0W zA@50_uznOulP${7R5%PqQZd%xHdsmuLc-ESLQdoCJ(^5P$3e z{;lDBB~7yb#Q(=4{QtP0LPyg{%DKIe+%pX*#H0Oz3;~vz&QS2ES$yJ|GPf~ z_)k25xR)o805l6l@$WbB@PaQ$KDZzR!S^Tn;=7Q2@cc>Oe{y)#yM_K9K(+#Ry(rk- zgKMl_hiC+Drcx<^o91Rv(JvQfFpbfskY zP?f%C{4xAZH~R%Y>#0mJ_C787Kwx zWV0%oiVpPE5=~yl{gh_rvqI9=WWiF^keSe-nACh%Qk`U4_PC~{SIvYIdZvQxKq^Fy zUFeY7%7m;V9f0pLH4|{Zp9ECEAXR`;ASXpkJ{778m_@V*NZ*3>cTDiAJPJ$!(IUd( zAUuu+iAICKAU6t88>Bc050m-fY3L!v00*2?}Guj5xwF$ zCq(??3yPR~BK{Hd7qtMu27zDb09gGf2teo`cL0U-FFKaP0fg&M7SsTCG4*F38Gx&C z{2#mjc#(Db{m8e6pTC&=0q-5h#VAjb@qhTShk*Yf`cdz0Sa+oTfBXY@{V@IC{KP`~ zvV6XSG%`O?r$D?z%zI?E=(lxSY)$X2D=yq-g!CqsG zHJoS1Gk|rOS+Qsp3$51X{h#h-KzPlz3d2HC(9zVtYNhNO3<`1z(}pZm>|ttv`+ zMo<7GydSfc5iq&Z^n85Q7m)s4OxtIV&W}0eV(5%UFYe3J zs{G(M3}2n!9zS^LOS6RytuY&xZ|-Ih@t>XjX3ROWHWY?;_J)^+@J*_Z&uM;#&ayTqG&32*8VG~Cxeg>;;8-P*hGg5l9GgtBT@Qd@&b1KQGcH33gwX5z zU5g6<{WBQz4G<8Hf>cuZl8>pYq`{XXf!ph7O;ZdVO9p9ICIgEV0^1q1p;=W&HcdyR zdQT*P&+`0vzKs-~fD$J}{Jg`2P2L0`uqJ4}TFq-3$<%YcmC^x6*p9xkpR~XOTuFq@ zQ1wIuF#{p!geoc64gDpO&zD40Odx4`vq z&xtz;dd%;|J*E+8I3exE;`M(G1s<3D#wY-_2>ug`Bp_A{0j44AMDDSV89*k?Rkd-^2Gp0mSl;YC$;qKS0J0S-)=*?;l&7HGtS7S%|pdoj?DIk` zzYjK0vCav>2HroL7B_F~?4TAAWl8rx{_&5a{^*I_!OdRrH;)>#N7WZ(C*%Io^9tua z!JT#8kOgV?`b)P5pa1-;nat$#eJ{K?L%#6h{ao|>xNVO5+r{G{H@e7I-zdr-nvJV3 zxp^b!kIz2Bg!32IhDJDiFl5kY=+TQC)!F-L?*akIdH(FYJutn{W0cNL+T=H%aP@ko zIu4dk_rTBFIqaghAH02i|Ed;LFzs{y%E5m6>Q7G+Mu2>sYbY#;3O-+P>567Dy5X+Z zTA`Bfn{3u+q@2VHDzf->E1`7`I`HdRJGY|&N1%0wGz-wd9RNpAGb|Pgfp@!ZprI7d z!8>-R)Y;l&b=B~Its9^y;NNrq7R-OFrIqyJJkI||3BYtj z0U-3>DEWs7fM|fo|Bp2QP9qX95P&Z)3IHxA|Hk~kt)2U1Cih(k?_%8lVy@ShK=6(5 zzlZ4D2icDn@PCZ_`x%IWm*yV}2ZH}ckO7YP)V*GKB**EV$0Yo3dJFb++WXg9d z1L97;Tq%xFxYq{Jrwnp@fxv6i4D7$z6n$f7lPLggHwDmcIk@~KK`J(i0@uhK-M+K4 z14`1}`%nNoFT3@Y_uSdO{xxtg}9i(T`}c#x67c{-$6xM53U{^t!kaUZm@H-w~uBHiK|Cf zI=#Jv)Y?yvPEU^Z53JRI)>t|KkYDjwvBYMY%_?hSl2A>O%P(cMQ0|uME^mnnyT8{} zt$iCpy{zlPR>jhpKtogoMlg#o!tz;Q1A3{WumQS_`2oPxoR8i4G_AnddT%z zj-%rrWE?<+$h0#L& z4cFdxY)wf5^-o6qbd;0<4iFV(&KHD0HW7CXkt2WKBqmk~Dcn&U-1ovi?*03}UR1pO zX{SI?eWMg$w2N8)aT4IM3iv(xw*UYT{1fOOFGT;?q7HyqQ4QcF?iUe%7Eu5)01*Bd z{{KP@|9=M#!0EjsQj<^6y}OYQ04a`2`?2BbOLxBn+kY1eEj*)+o-qG-E4KUJgaQBr z@Oual8=(xLXYidnx8AXH>kw;mbFT;Qqj$hTY>RFuZDS~OrP-*WNd-gO6KPq!v@B+vO0c)+ooE{T&~@i}j9|m1@`Daro;;uj3ymDYFA%-JU)JA3D2|< z7__J3vy1Vt0Qx`I?X1DLpJE-I;*Zv`?SsR8b>-?|dKKu&SJqacwNI~|99`+^fz2>F z4e*2XOCuKHLO@bPZ$iXu@OQ^H+-!c>IA) z8AX#J-MTtGUk8!O=XnkYzMc>foWXH$L|skT^;9?l?+xZV1$hJNk3pC z<@&yoT9GwG2%-o8FLXjs1VCVfD5rcLb%Ash_@1AlXf)gMG({nG$0GQCC=f=s5K1I@ zLZk;!U;u#zEg33>#L$YVM${jZoFlb~-;^i;=(ayo0M!3B7+8GSWgEko_Jci+F%Ga0 zeyrqU7GP?j2GZnsN&h44Ur2w%|BJc5E2}t=3ZUqKxw6FiM*+Yk0qAl1FIpWafUWP7 z1=l`m>5mAXboGGv6DR=o|L%A1zL)gw{Pg{YgbqOBe+Yks{eAOQ(E%s8fhHiAjN$v> z{N6Z3^nV2T@0@nJy}eAX&bzxgJr#7^{_JetXZF}GyOliyXoo4j?f%&uR+}votZv>u z-@z9?@$kEaY8&bPQc&!pTNlu@at2*FGR1kO-OkfnC0F7zPqh0*`1+%9V|v~i`rRA( zHeDFZTdH3uqFg`#{U*yY*>bBby%U+ySr{`xM`u46r zY|I{1A5rFup3C*yXXE49uvTD(?;Z$=WPSG97q2@P+pXufX7$#53h_ntZu8>713i57 z`B@XxgR}Gev#t-r{Q2=<(njN;N8{Gn=t1FpJ_Q3HoL|gm58ft>a5XwRo3!wiGmJS1 zO8)BpHE<#!@fEYx@ z_m;gzZCCGZ3Bwtr-RqWu`pS(VI^<$5#$^1xb5!RynNb;Wa=*RV%jL?&?5Ij}oos<` zk6`Q3|3ct%wQMfiDi)DZbLu6~R3xy~ZqzroHplt?c^mRSM(*(Xph#m3aAr>80~lPs zI_01`N}bKkYq#(2yzIvI=gVgo*=+CgZyV-Yk6u61&%QU7KhrO?mb~qvNv!$&{J6I5 zOfQC^3Iw{;5VEt2i_bs1Y|UHMi;J`OJ@DqAZ$HT6(a&#>`{roce|n=jIjf#OC{)ou zY*d_2Ch%xRn_?_$)t$7r>M&c5v8BboZ~G?(Lr*;!l&hdUA+NAcFj@ zy#t`8E7ZY$cX|J4uiHD_TZ6>Xd^d2(fKbr%T^s1Yy43{Xx$0$~=t=?Q0d;S+JhtZs zfc(|qpwl%h#qO{^gx*hs6^MDgT^%~tf{#yHhXuBw>6R&jLTSjfWw{lB@yxvA0NA68 zRSgL{*7s@NBo%!E?5|^V*ecQw=ZlC80RO?{i>4`R;3j!G$i->i zshALM8Gxsg(DV@B$cT_4yl>MmI&K0TOlqtJE>V5b$Fdr3;Z0_(RcG z;L>6m|>vkL(sOU@AfYaY)7$Kyd<)EO-FeMdV*n{SVp47SVsM zA_EY60b~dWu|G&0(06$>bZ3NM&pbt1B}X@ z^^2M1tD8bESlPV$@ycoTY}6V8L!P4(=NJ`tg?8;uu3T*c!AH`&UO zfNGdgyPshKln56*mKTv-2)e;0M*9`kq*v6&eUC0Ss*-^IzKuTW`^&%+@);?g1s@(v zMEq_yUX42ZIrpf~KA4`rZ}RB*^YGDm{`?~UU>^F$r){J1|k`~1vA=JIXV)#iBn)mg83KA)lJ7Or@{(Vw=**q%;v zt*JGVjtLD(ENM0bLD8$Hh}%9paQr-@6L+a*}rzQ zkH62=t6N48xVDS5KAd^h08gJbP!FVYRkPag=^)H=w2bNH3|E!#Xva|fpo^zEiXI3u zBb9*ggY8V_bS4oPko-arAXfr9UrMKm0?=g$s=%-z?^z62^nvfQ7O#pdtUYMHZO-wR zu9EITq0IG>c}#es%7{KoTS*Z42n>-+>$D+*0!T3>imK~O+44JSA&D~Lf|zP0C{ayu z7Gxcn1m8F1P_!sdX&RpIS3S@RkOx%gFa`6kHF=eFP-9d|1Z$LLt3K-|5e}$n!9zGN z`*0XEf*2|e;q{`4z+yzW7_1}_9;As(SzBr|WuhpUB03^L#bd=Ih9KFfxMKhoF8-I) zAnLLNAeR4V7taE@1OX|WEmD8D^B4gjS|G~(lMF!Y0uU5{;D6Uh9I#BPe^#RYKamFj z=x+f5#vH)d^vCtTG5ZH@0RI0H_U;{i3v4~ub`re<#E-UzK>kVZj~~AG(`WzyBJX`N z#uI=5T7LbQ;C>77k2b;`#N+rF79fKYi2oMA|D7Eax`E@{m0)mQI)ha#i@Fr7Z|8dY z)ly^gX#WV|#X;$83g0-N86ZhMEfnDbj9LO%{iD28FXl*o5d4|Rq%Uw1DTha-fZ6{% zZLH(T7T0t;QhJ5&t?w2}d+zng5YS+w&gHY<{EWF`tC$fo)k&KxXNnX2yUSZQ_pfH3 zUwkmzbB7=GgS-km-Ew$zLObj%V*QZ^Eci<@Xy}3T{mS_ z;g8XIeEPn5|F~8SKaV=yv*#O)H*hkjgtgrEv)oa2ym4LfnPQpzpzC2W{Iy&3iyPCqE@7_tr z2kvPDptEVH06_;N&vtN0^>h&C3pHNNV>KDc*3&|(DbhxYHEf%a#Y`_Ch#%{)953=1 zk62+L2|*`dS2PPKo@HCn_;6YbK`wS^1sozI73@{VvuKe|IF?R}WlLkxlstu)f|qDY z4#B0YCREttjA(+9#3&Y6fwX5Z>y`mrkJlI(j)kVnp_yv12*B6y3nxs*qRP4h1yI!$ z(USq|`?4oGqT|E+M60I_4mm&q5Vm~BTXk$913qg^~FTqEk^Z?XdZ^VGk`bf4J&k1hXY zK@+axg=3WR$0^w_-iEniNg4(_4g0Ii^*MR-48}zEZtEPFf1bfG5en| z2apViI)IQ~0qFXBcyw@3Iaoj1+UcEMW%X+viJ_(8d|GbsYn@(p3)Ad-ZoaZ}aCHCf zJJxrRyC1dO!n~Y0ACKE41R&M_80{++r_~I(9F4~4+*d^yFh@QBPW`s!);ZYsK18p+ zZY61^w7w_h1k?@{*BkQ|q`g$il*gtFSiIb7A^Xr6qlXB3+itv?W`+;WK3{;gdGwp( zdGpcvZ>Fu^Jo-(0=FA_6?d`EPX+M~$jg5ZwH<|L;`R6wL?-iPlzW01HVf8-Wo;;dN zo;7@1aRNv%|Aw1#h+Xn#tuEIa>jfz%8NHFz50;R!ei3SCX@B zo_G<6xplY`1`Ax#Fhx#PWfcTr9%;mMg3~llS9m?6tMveyr|Ebyr(4-|&jPH*=V>t^ z8e+Z0m=xm>#Uy#UFNvth$+Vi3RaRM$cSEoS6)*x-jkYt*P*1GcxR(%@EOwP7{}lj# zp)5KcB78?tLF>z#l(!@UEmSa}luY)pXjW9o-`am{3*he(2EZ%6imiW42w0T

    W+~ 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 z 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≪0levAG|vN{_-sZ@Xa^v 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%0A1@aUIIDS!kPv&~BR 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

    hNF)LTvoNN#K|iR;fyc{Tv5ee>Ioa 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

    P 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 z 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
  • zXBd+LYR*5PwC$)s#19Z%BGo;10QrC*x_ny{cL4zd;HUo}X59p&euQV-6GQ~0wNw`Z z%WgLa6l(s0$3JNN=+0AQ{`$K)o2{_ULU{&|J%vISFPN#NaC+6$P+NoWh6}Y>t zzQkygI*uy;mJahpFvbA*di!tZ?fwPq`(H#OK+5}9(Fl+(`yd<(g8eD7yA|B;(?96l zKlnV}7hy06_a<^hqzWX9UW-@_8EPX`ImAs8iSBrNJN=f2eqIAqU}M^BS*l;c5czRfG#+mJDUH<@uZ)GnI}eX z5T!b$Wi_&-l;e%z=UN;ADKq4Eiv7zZ?=pSnOBif;c`WjIjF~#~0rVuCDz7TzB**iXl0%G2IP z{Um>bb}z|dz0YYbpV!yLk(W(cctF=@flc0`5Ly(2$IA{luStF$J|Ra3bqzYBa-G%S z)TaxPbfQLzWWL?^Wt|voChZ^WIryF9c@eH^0B4HECcvhn0WJq2v(j*&rnxSN&OM4p zKshvE;py5^^GU-uxC!_T9Cqliyw8A8TOodc*ozgAX%ot+c zHvHk97%EN4ca&t9X5KxCTuui#WB}pbyYf@R2aGddTTefKNE-MK2r%8-F8iY~DQTgm zO<+*Q$gfT#h~ zSC>+^7-VVJkW*xd!tL*pMi+NjgQ5Li*R+F5o0g18UaTqA^@bH?sFJ9h-5Qm`&CsniJ zwm}cLT0%_5j}cMy1dyd$M^zF%DWn&*MnJjG#^09v_z20A;=b7XzhFF}=`iuHjm)3> zB{7EY$GiE=tMDz*(FC>J9A+2-< zdz+trwEh*JUP=lO3UbWEUPC`$t!J6BL2?vmN2%pEx2?wh-FNZ&0C3e3!qfzL#px*d zNX#E=y)cUI$;NBPN^8~!GzvyJvXnYgKsJE<#von+VQ7~=c6oX@ZU z9sDW6lTeC$o2U9mohCwjMEoBl4-3yO1;&?TDWY6JxmOaf!8CwN$h*$;v zRx2@O@STfh1)Kr80>q1;hHON}if((8JIpa!FGCc1Y#Flc;rM^8FA#1j1gIy4AKWG|a4 z!1bG`!Ma3YdDlN!YYL!LE)^$=<4;ynFV@~4ahLH{8%RcLYS$q!S&F-vg@XhYF-m_| z=r3S8d- zL<=+XC3c9C7yv@N;D3G(I?ppa0Hos;3d;eRfv!?%#-r(@*D;oO<{jh}uVnBG-KyC3 z?EEXv#olrPFgF6H$ZwD)amoUfeP0m9hp@hY#itJvp(5S6z#9XRLYzAv(^O8E<8Hz(-z+4$RCpUnU~ z;o+@_3p~LDP+~p>5MJ(yJ8*L;%Gto}4G}-z&dSu?Eov^{hAESb%eqyp6OWgl-!|GD zF;;<(kpZ)Rn{@L65hff|Jh`y~TYhr*2vMK+v)l-fwjTx_{973ffQN&t>=0#qUdS#> z07CjxcXvtk0>PK^A=c*qxX9r?Pi*Ipru<`7kX`RfM;jk`v6!h)w)cto-Gys_+#lgg zF4gsaqD-B`~M{?HB~KdpXYk)H$G9a?&19;{0R5QwP~PP>skvfk3v^ z{-LY~C?2cbp7@tyrvLpk+biMT2TMeAj~~h5RUj1d5QNW!ejS?9-D3SAc*xp6CQgS@ z_Z$9hR^c~fy(r@TD!TYwH+FP#Vw+3y0>9`1(nOj6nI`_Kj1nr-_=g(;bsj*r_qbNA zx5Zw!irP!1zz7V??yP36;@aw2vmhAJ1TezyU8P5#kB!p1yt5k^PTSY5+UrA_Mnjax zB5=NkGqI@%(;H_Bx#kcmFMokRDf|u_zf=j1WffaoSR$;F+T|U0vl`I^WBR_2{Zf@0 z-=c5UDF5Z1N>hTjo9*wVs=P*0BB_=D7+YPebXaQtEobJ7<)w)n#^h!3=?{f#KWh{@ zceyGAJJVoB{~ZzrO3ePt;?hwCO-!A2T7Ey77D*Z9d6b4}3jn`^*MP7wXfw5O9(W~z zg~JjVknv@4uuMen+;@4W-T1y*;e)WU2y-ZvsdD&!@^6 zdQno8(a1|k%v*w}z`X2a@UbuP<|C8y|oEX zwHc@IwIFCs<_K(QsYifQ7YhFC0QjexV{_2RxGT>mW0-_TuX@DnS}#O}bcL!rtgad& z;rFK(6MRIuYJro4;`xs04HCNh#YaH@DvO=t_a_Z_=r&<>a{Zh7Xf{Q=OI$zxThFYuY3XbbP-?O02e$TzU&wz`{=sJ2)5K^vSvH7=d zZ9F880u1dl4lWVmA5B2UTf#$bkLTkUUV4abdh<%ze$mtX23mCYIs-YR-SiGe`);1! zn)?pX+!6PWyj0zMz@hyWcSJpbt~b-TAN38R>--!9;g6zM)8x6?d!dN8cx%$4TG#^>q?#JsTgvF8AkqBwXuKu`KAO_VCmxS+|%ZsVqMArMtXw1_? zayjNNh8=IOCSEe~vd2dOwwWv^LHQo|``h^=JS5kP>{ZlKkq0 zdtHPa(ojd~+P3)S-@fUh_j*c(~w2-9*mX3NU!^ zoPXt-M@O*x9tQjH>8STD4`E(pF~A&#v92hPBioNDwctloNY^>`K+$Z950<#mWnJ-R%mX$xc;td~O%^uW(bT{Xrak{$)A<!Cn)JwHoQAH5Y(T>0?Zt zY{(HBEEi8#elLkvfF8)Ujw&6JKQdX*JaHXT8Y@Yw5az`*Vn<#TKhzYDmBcec0}RGv zAHQw@z8^%cdi}=(1|IC~D>fa-2VYHur-^^|0dXj!7gDah`5hF0^UKzn+FaSq@CkGi zD$LQhu3{c3$vT2J#hT=qrgL?EnTHMUUp-h?#f;rKQ&XEbPNrL+}mNi^&?xkE|{T9hXF3N4Yd+#BI0HyNglAnfWM0!cc#Gi;NeA zj;vlDhYop$dC~B0kt<653xRf%>BTj?(9PFlO6Q=ijvS&A0#d`*ZVG0&6;49w>+rO) z;J(3o2jJeyr5d>t`Hi9W%`u+O-p}!dt1w4~Ua&5tBQ=92l974qLr$rr!p^ z-Wa``6JgjFSC`!rJLbI1+B zCr-vr?kB7*J??4Qsqao$Ia4gK(TPRR-kh-BxqA7aC9Yc77T(~xdF5A6JqGdehlXn) z!X_~RKEKvb;0f3yAmzS*XkJ0OTnIki*Umeha&5PI5piRqg~70>e63j5`-GKy-{{5n zGD>S*B_BAWH_{Q-M$5P&Dghj^R(Z(DBB^k5vsyg+xmEy;kBxG#ZuS9AWVQMI*HkR1 z$m#`TC=_om8)5U-y?Vv-gtg6goUH*KHUEw)vKo?YA+*p;fg?k5plfdWJ-;>;kIJnc z9;NnOJDKugT>LSnRu*VbgI0dCX>9@mpo74PG(Nxo8dw3T@?m2JHO#KTE@EWNLhORI z`)&E_=U65RG32KchO*={Ly+j>B<7v*%uEOK+1<3W(#ieP{PpZiPk2e@gN{Cz={Xtj znUrCi&2f3sV2|g-8Rb}9u~o3-J|%Rv|wLeZf7V4Q<1jKS6vdk<}Y9MdU>L{cyX)Xa} zwBvamYrxtEyRy%2jU98JYhQEs;j0J=5x@UDRGA=7eI7|4MPE)8CSW97S)Q2H;ky6L z)`0uOs(hrgX03&^{Mvb6O**xCeS@}PEb{s*{~QSFU^IzSQK_|e!AXa_0qJpM$GY)k zAAYRb&2~%QsBhAR!xSZ*>lN=<3q0qfQ3D`w-uCNSYFMuvFdjCfof#~b?2VsSDR8yIRa+2bv{Ce5f}du zwM7}LzQNd>^$#kQ8V3dBxyO^waCj161FZ!eskdU+V4qO9H0Nt8rcSIb3JW<1Qo=-_8+L# z%`b$yLmc?Yc0^34?fADnOc$Yp=!^yNhS&H{u#Jf9mQc(&nC&)mwd+i$lu9@l54!fr zY*p>{*Of^^IXf{xOKZ+5&2vajWQ>+r3FN9&sHfW5mX$Row-iat>$R-6t2`Eg!4ZEq z!!h*c`G$V6wKhf+%GIVO%^ur6;b8(7l z7&xA>={@coCU#CUZjE-tiF9RSmrtlrOV?0^sdIgVVnfkTA~ib9sy^V1hqL6H zu+$YqWob^YRH}u{3i^jIV^ix_RTJddRk}8bwmd(-tD{}2?cNd^4D4|TXM3u5@I*{? z6~4ywdL8N?;&flSf{tALoqN7kK&^ME7(rz|%t%3p?&U zqrE~yoy;5L8#Re|l7+o)z++~g4%IwV;pr8i{1nn2%2>86`s$XYe)5}0pEOG{eID0a z(uP(V-FL|o6_{@8drfh>SQ@LrLFG{J*#rblO<#mk!$!H8s>%kV)$4Z)};|? zBka_bUp@3|r{WBRD|xnk2pB2er*;Z2eDWP>w5K!GnWV%U+Ff}PEDLcF#M&XGm8V{N z*1%+W?Zg+e>|VQlyhDJXX+fXfprNE6XkHAp%w;Qj+x0GYBHttja(A8l9|AG7t9G5M8o(9^~Gq?C0ON)u&i$4btpbDT+j zJno6^5}`+352S0j4i~Ni%OklBbz$363D)PQ@(nwyBC#)eT^+#+1Irv9zDJmLU*+*_ z+~V_hkF^(doN8v)^VywnV<7J!A-f4g_t&cK4Ev8YCLOB}frZp66@3;(G5EpQ5U)MQ zT~VjGf#!s31yZgG;8!FR_|k8&j;7WhTs>!aejdR2SjKcp&H+W>MP%7=veX#8dcAt9 zsA#J`&>m_9yPCofJIhbCv9w1E&QjMI&Yb%aW5h5|C^1mL7^tr1Ey47o3O=U;Nq%wq zRh&1cpVZstS}lfw`Xpnfi6tdoJpp5(+N;(>HNxZXkJZ)Z*js0)CijntN+A<|zW(Wi z+dstDJ466u=VRGjAzr`crPmlJo8L&MLg>Z&#xWk=I@sOPyWy0QH}sr<$iZ-?|6Bas zV@$~avJcr!6rJKsw$vFqAs>RV#r33@s|S47j_`Yea8UKgf-q4_NW0B9s$z+6b8Ae0 zuEmp<_vP8oeG*drE|}l5B|#;Jv+}EK&~EDhJHUUe5)ZTMOnLF4?n@K%6a;xh(qNZ` zKCToEIh&WS>Jbkj0+yHu&lnwsJLF_J@{Yxl=US6eu+=wS8JJE5KfP&w{Pl?1qlpg$j8$b{}XQsk`$t5>FLEC=*TJd^91M-u0vO z3c(+d$E7Ye)(X++kI%J^7m^7RXw*1lSx9a~D~gqI9p*g#G!siH6(+#mU)~-u$J_sK z*FEQTu~YeY^;!$__#06I$OPru;~=coz_G$mj=6jb+WmlgTq9JW#?*2J0ceD@CCY$%$3fWd#}i8Xo; zR5a1?A{VlLvwA7qSpET~L+_?C!f@lDw4n0GsDQ zDZjlbNp!%ystTp#Z)hT)W>-N189i6mN0L(7amQQKAT1CSwyyPE-D$@T+cO7Rx{X2_ zO4G8G$|#obc%ofAi=N~t-7|(45nS;^j`yy@$sCGF-!C=xZeBy_E!y=&zTt7c1*ty4 zK9Fv$cZ+4Qc*K&w=?-E4wzr`)M<4Hzr6DaeVey3DUyFEPlDb`kxShl#hlN_h=m^i+E$8v%75 z=0?-Z-gtD-$x^KPWLmO?!!t{2gYzfBaq=h69y`rpJG=3slp4+hFLYi!vFN8W#`9EP z)Jz_;JL@NG_n(S+64LZlw~S9FHSQ_%qTRS$0<+}+8$KllY6OlE5$P7uZK&kz9Bv5< z>EW%MVd{u!Nl)Z`Q}fE&07LJvs_%sD;ZG1@4x%t9Wo z%yX{XF1?Sf-a>R`?Rqrj;fYo*glS)gq{jpw6-1$0=c4PTX?8{#Fr-yo(W~)wl0+8!yuvkp*#PP(&N+F4vbiCA>O5R(aTnL-gvw_vs5OZ!Tg%$Q3u5Wz5thRT;p2q^1ICZbrjnzY@kV{o#qo*KkEjYusFf#rw~1GL>qEX1?s zG3iNDURkmowLKBL+iDz1lWg5*NAvJR@$iMw5fEkQGtHeiYDAZPVSbR?k#Ohjai|!h z7RGvLVF|lE>5!Fu^0%C)AF_2e2%a(Rbot*c^}Z`|$F7egQcwTMa+!=44mCB+5pNQ% zTu%#Hq71N&2N24&)oE5Or$!72jT14w{b;;AxUoS)j)hU}! zQh%IG$Q!{*cv=p~0E1TiHOdCUpotHi;B^C0)yK!{0qX%S)%QNml65o|^6f_Etlx;N z%xokyAgP}ASvLT3qu^<+Yy1LNO$}3bk=^9 zvrs*q=6K-Z#1Z*vsI1bh!;W^en&Fm{-wc5^sGTCF{B7i-cpgc&vhEiZ*V%I&yrQ#e z=Rka7)G(t)tn}~~nblddo{gL&4jL|UeF3#+W#DT-pN6H^FB&>sk?sl>rP|F%H zhDP+W9ewg@HLbfHbLv}#&RY`Kg8aSZ;ZPyNj#ox_0ikyVO5p*l6VhRRYMK0YSLX9K z$7i43Cf@&C_A%wHsgcRE?LIL=qKIc7t`*&We#-E)X*A>^8|OgA%%cB8riYn#u`D@1 zAGQF8L_Yh_Q~?|k%KwSRY+E)`-04===E$%lP-|Sisu@sY<+`uYVbUREe!hEtHZuvg zEAfy%yE{A;Kx;b7mmssVQmkBFG%iH1)SgcScRp^1Mq-yqS9(VT?*<5F*M5U3luXAy{HQi8`kO{fc&2tIC;Js0WIGTby5{ILI2|J~@QU~)zQY`g#6N!b{s zS=$KyHj--}49sO9<>St;JH{Dm8c9BS=8<-up62GB6kFi)#3I&od9i0GIenRv6p#Dx zs9sMkj|&c$a})ZqlDcrLA0WY4oCWV9E_^=dU`|8I(#1kOo}YR?m%JZ?{N?&(2}y~Vv%c^D4_y- z%T1iaJ2n+jZ$b_^@66Zj1~f_T@*T)G%GI|>gL=D`LXg;w9ch}+5H zoIl1Uz~YZC*EKCSy^+%O`StRM!N$aJFCF*wr5xx?txxP*nWyUET!Jm_>&EdsYLG)` zgI2fVFJD8BI8}a;D zq-D%oOT8FUd`=;nK>7gN1V+5`)Z8xD#rZUotGOw;uzra&OzwbTNdEk8MR{vGTp*VC zDcz>iV4cQ-_{|)9NOTq(CC|NPmz_)^dzEtj0p79Gvq;%<=655uDb>uM^!A4c*Wp2| z%rICSS58w%)J`PFEXg*M2zz&7H6F4e@^xm)G_bb&S(*8HYV+C$+66;yUaP*6bN<*f zHQDoreYK-_Q#{xs$C|Q9tu+hQgu!g^-B)g`BeoOQ6`!u;a~*H8!_!HQA48=Y9fGn_ zJ}beF78CA$jC>+YX-4{>W-rYxd$~_ClDVUa*$I}gK<=+N{%MG`P(URBT4C7JV?zze zi!)y9X(N!dp3LKb(=WXy=iP<0>|Y(`;>8^{iBa1_)!nDaT-0y!&h~}nvBGEN&c*sB zn_M*ain%qM-5ng{i3aWcNt}YH4dmIFJ3GmjvCjw%f;PXUh}z%S-joi1@shf7)+JPO ziKkDf9;QOP`#nzd#eJt(wE(A<$I7>NKdWn*7N~>}s66jTRUs)<$nC|%ha9{)*xF$( zG?@HQFq~dhjHS*7-^)IlZLhZ11b1v?8E?NyJRN4LjZTe~brD9tb;^Cq+BIHWOp3b( z=Nu$+u#Bzt;{E$Mg>FdRtDigM>1+~$$q&(l4m_z0@H6Ek{)Q`m7;XgZ3 z+L*#Zk-D`()=J;)?4xU*8#%UC++xn`)U-42vUr6T@9gplt!J9seXN`REPD zJUWX&I__3@Q?`S~g1j^4tM%%VacXDVn>d<6*?HPCwuCepw^Wx1I=OGg*jF2On>xj( za^F(j(UR!Li9y?S>*(!xw*9QZ=k&rHAa|Rp)mOH1EJ4Z`9x9+QLgPqsIHo4%Gv1fv5=LCJ{qdm+hY3MQ)ZjYT zu53)2AH$mb?pY5I66TMcn7reC=wP5vTHbI7_MC6wu+wQaln3d*FgnJPbsL5G*H^ZW z&_8>fRchLHEUrA$8D%#|iQ7#E0I|)f{%2hMP++y!QrS{!PPXeTKL6^@+3|+DXUp zy-Hj9vi2^RTquz9R5g8*1)EVFBJQptU-Iw-7PvME$@x}6jSuo@a^Ro+(%{g~_l?2kIz^-K%~{0Qv= zUV>u2Vpf8=l!&2+)}33Y3~{8@TJs6)GHkDV>|XeS2r<+Cyu-=Mr0IXz-vp~8?_v6f zYj5nMB6=U(gCy-Hb2j!(2_MBt6BhtFmQe@qv{Rf^y2W1X;UjM!_>h{C&#nq&yiDJV z4#_ZU;f=`bSwQ|kGjxlW=y=c6>X3;cdw%@$ben30O;-M(UF7gtSN2POXSUluUFQ<| zwyPvKauab>297U<+Qq#~y0XH2N(QA8lB50p?8v@?{F~(L4lCK6EOey>}CL zi`u&x&TX9)Sc!byPw~v*wGJcEXKlv#&d~(%KCLdM_|4`SA;ZXhlq}mu!&r|2)%EtU z%EJ47-R%wegVtsrP4Fpg;sXsZSB<-Fe*S#qtsd?auCN|S2}At48=r` zjU0FmdFlG#Ys4LCM`YdC-~6_Gz@r!(uJuxlA>(YrC)W{MuF5l>@Y(4;pUcXou~FJ8H~@=?J(oUVXPC<#JZvtk zRQ60}jWw}9rp(oThER2zju7v4d%7=*aK4QTxeUy|&9Hr(jh|uUF4NiTH(H`>HI{li zg`z|~aZ)nh-H>0VKVpr7O9vFK7v9uA`jo|{R2~r8W9VC-ADCq$-Ie&Y+DPk|YQx%K zgWHvP*Md-0pz9Ns`=mlr2NbPo=R9?}Z@`ySBG((5QJr>*rE4x5^j5s=4mH;h`l=F~ zUG6+5?VPLcJXw%SxzcQ$t)kUSlE14Xyp;dAiv#Y)1_zT`D?wKAWZG_(3@MlqR85Zl z+|p#t>NM zi6q@6ismVMRk=6#b?-JPhA9-DdS-NtJ})!UBVc}OQ=~!7)})y}@W@J;rQ2S@vap10 zfxg_bl+XpSrAbov2CNC5RGDJlf$8(sJdOV=fF!em5}eOV$iOD9DzD(Wwu;?`Vi-D;$zPa@YGYmScE9+o)4iDJ&oo1Fy>zsAVB z)EKWneM4HK4_g)g2r{e&5!DzhqW?A*!eF^oLBl6X#Mcz}md z(|kZPsvmh+n&y}^2XGTjk!*1G5@yRtN#@yAZ&Rwu$_HF5%*0cZ)pIhh&-TNXBAJ#8 zNi=7EFukqM^DHMxY8s!D!+{Mw0epE3nx{HSu2_7Ivzsw@E*++h_ZbAxyhm(X#s}E` zTeJjcD^0FdANbFO2q3pm`Nv-HpO$C2MR~i?x;wE6KzZ~I`V>Ijgl08^(uwC~)HGjc zqh_zOeh9ZY5MrE0>t2^I@A{DI9OQS-c5Ph;rAAADh8G0}CpYn2?^Z#x_a|iw1*4Pl zeAnO(a@C1(oz_j63wg4$hcdd17Vh@6=^Xz=@<{Y$?y%#7wu zj8Ib)tcyxHG$gn=`net2KieBqKZ^56M?+~tEs8}mKYZFy(lWj}Mu3`d13OBa>flrN z&D+@wBXyT-FAt8xb8Cwog~-A4E`=m$bJE&Oq9|BexUHUR)p=kOLL_5B@Q zwXt!IV@x7u%RY!ym<>BJfnZ872ue=33^x9_zEk)ebT+EXJbfj?^VDh&pnA*I3W;zt z#K$uZcCqquF~!HxBKFsJ3D&U$vAUPXY^uHrVwLWTf73 zaPU(ulbGxrJE&BFzu|_rI$jn!6Ia~9J2!mFM;yprAN%QvNuNrZ+;kb z6snX_b+SvI10YdzX(A!6m9Jv0MBX7MxJKNSWire#V#19@f5km13>4<;sMi@;rfRs_ zktRY}wnDUpy(lTZlFx1D0bDVSb1&AUAnikew;vq7csC@zs%CpyFC*H=6T}ub9t>;g z+T{nxW(O9$2b>95Uc#n1Y-APXfsrr7ovxwmzz7o+pzP18awTMH7{ccCzP9Atl7wp~ zfB8qMcRRvOme}4~RM_d2-R$w{F0oyRY$$Y;($i5t`C7de$zeEKG~6=zS{^0>WG|&O zF_=>LlDr6;_`s5{Fo6_lBfeB}V~k(}2fFf2W_;g8v=xe46e4|ecIRp2cBlgUk)Ytp6txT5b`&gZ? zQHufyz&t%O$S+s0w|EK2Eu#eAMK;_z-rSg9aj?yhq^Yb)xORkjGaVoDP$#nN-oLWH zVSJ=MT8?;Es0iF*J}!*hCw+gFA071;0GtwSr+27HwXUVbmw|ttUj1y8ZVy$Q(zadxz8|swp>|FJX_g&X#2m8j4IK4lwo{XQX zTD%6(WG!w+3gZ+Kj=8I)i8&f{jk`%+@!UT9=qBe3+y~)QTt|&!t)e1QmPzBY9ZTCS z2xcVG%Y%C!XG=Q}1luA6h*_Ty#3~|loSrSAAbD(_JQur5{qp9SGrIs`XT@FaV>f{H zrzZYLDY4bE{=i`$;D7}h=W~+KeKB*b!}-%;(V$-J79KWylTEXFBnA4i%IWFRq+;;( z=Q3fAPPB9%MF#fCB2SKf01lXDPyx`ZNCFc`oi1iU{z*YuqVI!%;d?ICxiMwm z_zBV_Hg5K)@TkuD=`rqGFj-~NKf(!p=d4G2S4ny^2c##7E6>kG{N9r1Au$l=RhI9X zzj{3Bhf97cLd#1&Gh8Vmwa;cgLvmWlU1gjzLTzAWL)(z#;oGUu)|pCKo7SX(t&39o z1)*d5jR!teTds1Cnfw2S`3nIy7S$+H~aw#{**ix>S1>LDonwl zQeA%96;E1Pk6$os?z4+jLPv$n?eV2VYUzaO&VzSu?`}q(=9?5baovtQeVjZOoz#ht z4_IeB_i;Ob{3L`;7rVXU5778SW<`K~k=O3O5@gVs%GP!#ZFqEeWS+L`Bb?X$7Jp zJK$osca)eCxGm(Dqy*--oy$hmyw}VyP+Th%oT3=$M@zp~ejO6)(NZAZ5*iS3nq$if zxTg-6_rFVt9vGB&V)5w{#%Q6alqKcCb_BEl>fT;xbNZr8!a5!%$gTOR*F+>aGBdt1 z3NOK@MYQf(z^m)(W1_0CUtm?*9KYKXA?3{NISk_7KwIiFs4{6Cbv z1z223_CDM=K@&7MBv{Y{588Nu0F7IK1b26L2<`-L+}(mUuE9cZcZc9ke?6Q1&(6&5 z>~Fv4si*tis#E7Z=dDw6>-N2UwGz)rg95ee^nteG&ea_IJbsGaUo7ZX<3&(wW(AYW zN3no6sl8S&@6$cyWy1G5$EeuB+DuQd-+q{_-ty{*q~QnBX7LrAw)sI36f-el?e-;kFM~w z1v{~)1En10xA6gR&=Q>5?J>BAF3VBfIH%7Em@V9&+U3s^ZhIT4CF10p zqYm%iz;2rcU3attgJ$Iu7LkmaWZDUg5YB5pw{#=(zn&ZE+VQ6>Vf{86oPJC8gs<(Y zRQ<7BHHIOhi-qcALg7+GcRksTv~1*kg!m*qEY3g86~23od6DaC_I+> z#&S!#47i3Z8m4beH3SOU_odB!N@hOjzMc?VOm#A3;DFN&WBj>!iAEZpqqX5uppYMp z$MfTUpTEssN~v-?VqZaVBv;TQPCl>6iYJ?$GY;J>vYdMf2YyfGr4(@1#KV3aJss-? zKJRb`?fT*=xIgh|^Hm0;=2c_ks~N_d$erl=4H6R3bcGqtrwl^&U_L?08*Y&TCu-)S z1kv>_1o)*{{yu|vCr?m@_YJ41i7rjot(1{A#nYduS-x}mE?!D`tktZ|YUacHnNb(1 zS7X#ioCs@6_-j9g+2omA%JbV#wgT6NNNicH9z8{Ss4uora@+WF2j7Q=RDbvKJadpa zcsmXlxLNT@y9yxHkYNFN6gIvG4UA&Mw{S|=FR7^>w3hKc`Dzbrx1<7V49pR0H@FCXf&)kUs_YJBLm4mqEw)-{pB0gO}Zu?lGSG0 zZM`|j8y>YU`>thhpVuBVQYGJ2twom-!Yoin1u4t4lcXqYfzFPr4sUPsV9Lnh4G>h$ z^PPfK;LFsV*>2M5zeiGAo8^DrZa_Ga$53{XTp-}}p6o9!-K|ozR?63!r9-|ns<0H; zyFsY)U^_Rjm@CK6*VN$xZ78L`C|9>fQ4xPk^?d;$5!YlqC+Ag?at%s2;@X{nu^cy; zQi&m(PNIBFQFzNv1@u(qZGEq6mtpiJ8hM$Ia^)(eKVIju;|m@-Lj)%SJ5TCe#FXsJ z)1NZ2BJ=ZbtsZ0n3Qcbq#Zwu#$Mnwv7t`Ro#b=>Is)t=z(B-t$;B(0`m2?Z=q{6Kg!Em!nKP$K4{hVfgXe}q=Xqc;7Rvgg}ae)ASZfMP$ z9DYtA>n6nne>@m}-|3V><>=6kK)v1uMcty(?bC(43fWhO$+1g_++Z-qryJHQbHqp= zlUBFauRU{@c&rSvr_xjQSTzr!)+6UqD)z>BAJH+NK^?kuYZ>5B2B9xxp(}Lu#*kIB z_ch~q^IK(!c+%MMcki03l7r^FR}4$O_dvP%Wc_L#*%u#;7>9O7%?3Hs$R3L>`+^{T z`9q6GOUs5i;%5%|o}PxErO0dRgpSp%XADdH4<(PZLVxytCP@-}qg8#^K|l?01fHrH z$v8#L`fcbEMc=a;$q1HIS__}fBU3|9EC!a$wW2+`d^UuVnP*W}ncoS%ro53imbtXm zM~}tEdiQ8nicqZb@+v4*me%Deoe%`-W2$(+x*q!gL9c}Nubht_wdx(Ym7OPtx<)10 zGOxKxe8`!}yuzPQKjsv)#m9=&;GC1k8jniN$_2tN4_zFl0IJX4190?IbY)I3_P#Dfu4KSe#AU+CrVP{{HrSgf|Fn--lqH2@f z-1IJvN~c%r?Iy(gJNxMUhSp)S$oP_iz+nyKB8uW}Pl)#;4V2fw1XtYS_=jdSUWcOw zcfHK8_XMlr-dxXNv9tO>H4gM5ADqBH>h(mu7TL5sDP_y62i$1HZhzhP{Zne1hOAfQ z>U_Y=_#@AFY39E8+2^-Qe%@8PbZVF)Ka3l0%Y2=ZT{Z31ihl%u)9XDQ&PP@^rx=A` z$-A;ut;Sq3PHrH1G;C(d+qQimzJQ#|r)U+Sy!X`jc(>T$sDGc`)%>20FU9&s=~HU+ zs0Ei!nBtC2%ENmu=v~4y?zq(Afv}PR5l&Y<&Z3fm`Qibp469b+Be@!*M{x6E25)oY zgU;7iba}kA*B+cVAVVRr`unH4dG42Bol;b~yW#*?XvM`ul2@_;x>&(&0aIFpXrq<#KG&7JwX697c*{kMG-%<_#mZzUa#0?=D^yw zW#Nd3*V~~7`LNbli6XhpzD+urfQPo2~{_@I$t%=f$cEoXp-tdpKx;<_Td|n)D_!dwF~Hp^hK+{X!g#P#olEx zKQ+J8?cK%Z>`#wlE*b%=J@*S;N4}1b+*9HsXy*< zU4QyXJEdvunoION;STwzJlw0Y*r~vc>pIZ(g+aBBYq$rJWR07b zZxcz#Dsc(Ez3heZ@<}!0wXr-@M5Q!5!ysWr)qnWadZyb>!sJG=nVqmRGRS({#n?5nd_{!8Iz(oM( zp5M%8260)>Z+MZ20zpnQyR@t`K>-U6>f`-Bi@rOFy)Cf0sk00Qr7?a5n0F zT0XR2FxfoHXYFWIlByNm#gbu^_1zWPC0*Ar{x#=xuT}9abuYGzwz}7ug|2mNR}%>$ z&qg)UC#7MUi6qAHzB{g;m3+EX!Rod<$~qDYng#e>GZl~8NBY`Km~sv1yIEBd6CIMp1Hp&4z+l()FeSAQ zf;o(HY(nu(Ydr{t92f2ama9p*okF#?bhYB%JougiiQ&uAF1ro~fpqsjov2kxCZjMO zd$AVpzgtS*jwcH#s4S^3EKmi#x8!|PM;p#UN>+UnTr#jr@nZm)&sKC8-obO6_y zc@T&(iT$9=r%0DsMtiB(J>)()%m5H{WrvJ~FmCg)x-K?+GaD_m! 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%0DNAYwh!`pA|m1KDSQY>q3REd9Q(vu zc!}DD{e9Hui4jg-E9J}SX9?xS&5@mf06zET6MinoY0ZjpSJ5dbDxqqM`l4?4ykV-w5=(8WU&_UGSvh_~E!uxRE; z%TZoTD@{~#d{;0bi?N~y!rTj(5`7hN0FHhGH`Leix}DhKIbbS7FuNU1i-+yt^s4;2 zE|^3*7?2@gfc$#nBRn^fs)JJLMse44;mEn*uAFlh@AnVSI55zGCYX~d3V0mR6^m-< zl!qp2_r!4_unh>Ucl_}Lf_ry{A`KjdB!j-#I(8E;4>1;j6h0PL7!D%Ar5QAXM z6`56TBhLU!oswX-Vv%tcG1y>22v5=75>-jKon4qFGA?$TbT>CAF?0(>&g>~S5Yqyq z`gTSp?`L|A*XYR<2L7n_Mm4z8LQDl)H3NI3*V71VSdOf11 zlvX5Yukq!wdBL$q_e9b(xjE4cAYcxY5?X6J;ghC{mE?HN-*yr#otoo3^*dJGGJSN|xf zs*h5Yu3JbtRU74T9-VvJ3$G=Qw(?An$BKGF`t56)&2G`h)wMdh=jgv`d_A1fN{SN6 zPWN51zgl#U^f_3DcpG|-vsD6-E4fRPS}I`y6TNe4il?uv@#=*xGH|F!ZzRGhH2{qzA5r~N^S}^jbzX)DbHaYo9;2yU(QySSk!1qy85V7n zexV3%?Y9?QS;EQRNGYmMK5u1oGyilO@!8=-Y<$l?7{x$OFI zPlv@N|JC)X=+)ycAn?F^F;BOXB zUTHJ1zXSl8>=R?wKljL4QHyR?&Ytks+^}(Zx8rRFC=?r72+B5Y-l!U3b7kh==Uyn) zp`;1{i(OJTDwU;yr9$I^U>iTH)Vj;=YQ26QK3UgIp$~5Hw3I4kH0%qS#_VOXXVPT? zqGgV-0r6{>W^?h><*QM?$7Nk*l?JAXIl;|^W$#*rN2lbej4EOCq|!kvbBcw-38a^? z>Tv#X+ivY=Os@sJ%Qq*XA7sd~8mGV{daYAu3=AsNN6X&5gKt`5^4CK81^_1f^#zU$RmlV8wr{gC1_D z_EafR!+wM_$T9ZS@6@aXIa1%l2EI=61pi)2${&M~c*y*z?B@6&-Rx%;EMfQ3_z2pM zwhJXo$q%}+%y~kh|{kf zR-%V!S^d`HW~E;36@Lu>`GVRFW9bzh*Vxzcu68?7PoZ``%g~_YRH0ncMH|zwnwHMzq;af zy!C$$eMs||-nFqTbNHr|xakRXIltB}yBeo;rQj0v^r4a2V*(PpE-=qUvVK(o8+7ib zvCZ!mipog_MWPiTfat3FaurbQz^Z-4q^Z2X$;C<+wSX(($wGe%p3Xa*ykpPk)xPJN zx=flu$Yyq8!~zAq!m!_d5~w==aKb=GBXSogCIUYLfFo~C?XiUJXt({d-(a+pG+Y|+ zb<}|cbLThFku8jwc&D%Dt59R?&>1GcKO71%rH^o6EZ<@Q@DWJqL^Essafp$mB_83T zl&DGCQwxUaxP9*ZHCLm(qv6I}`krtm-5A%L$XWRkppH3O*r68+NG~%}DpB28hw6HNU9YvlJ{qUVT3x6(+lXpSH8X|_5FiJ@*k{oPdPxgd*?0LD<>}TZXV;KPT zH`4pCwe_Kt)eFZYTDByBI0A+bw?UuOAiKb-zG{Aupi^6M+A6@b_*nr5!t<0co*wgS#4&TpzY&vjZ2}Fru3N<@> zmun0xe7$@uD>cB2ySox9(vbg1w;;OYuNE)?HnmNePu)E0l@n!u^IUEnZc(?UThr5q z<}5)85<8CSCnqmSmqPTCogSds#|76La;;=?4f3n2(?*t24-(j!x%7*bj5fyeLgZl7 zaae^dP}b0@);Omw+4gmAf|`7YiPko#kMH|s$(DgC8t{xC;b4L4xoksX8kv1o*VMku zu_j6&oxe8c+41;E88EB>5d5--1y-ZQB;hRHnj=&7S!WS*w6>k1taT{-jmL^Y?~UeN`20sJ=9{|g=Bn04T^F_HzX>w!x>47C2%v}CULB? zS!lq5uB}77#gzs+xg(cU2>d5fId}Z@_H4dob@(`a2LLRm06VIgyrj>oL2P?A#}+;g zn}vKfD(NbfkPWj? zmIC^;LvH63ASR}Msl4D^II)fhUK=spmt1}%W>Mc8kPWUCIHs`Qh2g6#IIUU*a@f%1 zgqwr5zACl3R?nO1)QSZThiCE8Iob8I+ed$^Kgf(55pZPhbrq^bH&T3Wb^Jq-_C+6{2loggtJA48RQfY`S6H`OokU zGf9Qd&8&G*b*1Bpa06v*%k&p~#v)159Q!a8IBLHu7dm_$`|<|tvqiM75yV2U7y0eE zGMNlwEkZw8WQ>W+hu+>nKBXPz{(!+Y(RUJu(uL;&ke(HMuSl+!9ZF^!& zNULN=HqOO&Q>!e2PC1SaJOTAxZ&MdnL3dkxUe@-|UFOg8u2&9;KH zozf>&n%6b^v3zOB6F)<2%D&jVK*HL91xwaQvX=BZi%;;U(kDf&%V+VvDgwtM%E39O z3KuPLIooh8ku!yU!~)pC$aJNt^beq5?)M0+0NT)uf?3N^d#>i~tV?%t#H#T#7NEdr z7tT4!4{O`P2|3v<2V_iSP$5DgJE^588Tt0~EufuGlkq+oZ5-(f?R0xEXIx6h<7XWiU*2SJoxpLXSjnGr;lMKEk^yepevA9rw*y;`t9| zQWZma?H2+5)v~Omf|n?6p`nK5eR_1Vg?dIX}~c?zH*+#tZn5uT@5& zjSo;AY?|{|+mDaUna-n&nIBfO0w(nN0&gSTx?U%puVvxNIk+zt8BT9rR^X99Y?hk@ zHYo`m5^I{K#n2@V1S{L$CW?GLUP+5*vfmCfq!F<9fih``)U9=(Sbgnzz^w!C2(lUl zfjFwg!)t_NaN)rjZsSFJ1befsclss7OvjCP0(5wM*FqVNVa@i+A&>FCl-S510T1A3 zHy*fLa%K!|bW}xjRh}R3KV41UqqZDQRj>|~y@4DI20Kn*Hh_7W6fwawEsJG0>6oDl7+_<9fPkx^`!BXv zR_steL*!One387@2dfXo!PxW)*mORhyr$v(e%PIr#8leB)Xxsw>vSoQGw|VWNv<3% zLTgI}u)krCxd~wcr)?zU#z;h#@8v#6e108<9&__)zWhqbs3UeQ#0%)}CLfE z3UFV2z<$4VMI1S2A79XYkNRWXx?GP!DN#TMjnDynpF^y(OL z*xfL5$GWUk|2W9G*{d(aa$@=OQHssm1v==-4HaJ^J$qzp* zVO&-`UWPy;jTXJ<%OI{oIyd}mFnhe0J-LONQHk%$DB3KSAbF0^hJibZgl`l}-UFW8 zkVTQ1m&ee=iaS2FR^1P+(8z}<`7P{Y?+&NoQH0($?jIj6j+P#>j5h5@7mu5r87T

    LST~^B#tuKha#jyVY!&Pi_;>siVTmIAbUvC#aVLXR zJ9_|CRP~CP_VRk*+byQ#Ed7erw2aBcH5-ZtuR8#6I9Or$UK#%E$0FLE6~c`>BWl(? zi;QiaN_pO(Qj5+F4pS{~8dcec7vL632xOG9q=17>p0rK%$+-)#k-zR=e5<^Zv|83vAMRw9DQ0pQm27$=c&bTfW1rlf@@o%9`I0AT<{L{w*( z3F=(4sBmlb+VEjjC%NoLbKzbf5Y)f(iFd=@Q}M@g^)yy|(z70oxY{(m09cDwE{JR2_Pq3AJW5B6?Ht*=vk?K#04cA@^9uw_Ni6 z$^_5;CfELvS6zI&Iz*AVbn;JuC3U`$#J-C5Cl4=(*$OeFgf$7R*0qZxkpo{Y1XT-2 z?{n!q^*RqD4_HYpbw(3ME4-X3L}>0cQk3^$Bw1xNmRtu%$FNggNS;etrvCPoa7>$A`e z4jRaePkA@q|A+)=G8@B3pVax~VDKjvKm%+?Q== zVv1qed_bKYJ?NiMUDYQbmYX$R*?Au@B{a-p$2n z)IZbh%y9`a8IS!zg^Nqn-+GchRGM}tN9?{85mKH}zb~|{F9GMH)${n2ZNQq z0$;|)vtubAqH`CdM8@dMJWe=*N>HvIHE26g_-hY@u(+A=QviZE}1`78p2!0IIKeW4>A*$Gc zw22&=@$xFLx5v^D>B@iACo6m|Q9}T1BvRDTwM|VRXRlD-!6VM7YhiO0&yi?^h-m<1 zb$>ARIsE9Wyeu+snRxDGtt#MI0q)FD1As4_TH)OX0=pQ;zLaBylDuqzAI#2cQ)`)t zPCr3wUH2p4ahcse{venox6IhW*U1Rrh&L$(2Sx|P7R~tNu&2kC+n&3SFo2R<{KCl! zZil(Ark*o)_I+1}%R9!WJVlEa#Q}~oQg$Pj!;R&nU`?vSp(pOTBfUFCYnN?V3l8sT zUDL6?8V|V120)!PLRD*d`TN6X4=(--Wio$MgFo(LF;SxF|gT7MWvM zpYQwyqGlhd==uw+36mGBY93jXEqI9(!D>Ew31FU*xph2#7|W4$6!lRsie5-Aeyb7JToE>PPIX}yd+3&-&aVO13!dv~qSX8WfRo41Vj)1x2wfv0?lV?<( zKP749*~i1lv@}%}>l$yU!(7dkhU)V7tCyFVB0`#vQ1u&mN7S^t&JqZN$#hM{ocAM= z+K2ORi)y+K9Y|{YXW3!pUyY!ggo5T*;?0?ktDK{rm-d;StK!WS9!KJ8ZU^)1OX_I| zGzXOhUsG5+TAMJ0dqpg4+x4#258gI+W`jh0)~0D>KX33oOSRT(;cdf0Rqbi^%+fxK ze}zLJEMqv7Vy}Cdbll3}Y?KeXp{uJEy0`MxW-=x(=lPR7}&&xj3$hKRgabVy_);MDa z>QVi1f){hx;6VMebr!1d&L`(Y7Iy%q{&1?jm;3R_1F?4->(vyl6{4Sb%Ey}vqYo$hmh&(syd z)wPt;dVEuGQyQFfP9MPng8sSc4d9JiHv>g283)gNzFN0iWOb@bpzD$v?8j5VZaqW8 zVn_JsthI5jGT(UY6Q^`PQ9!@NO7<@pI(4emST~7ur7=-?4dMiPHQhs#PcwOoV2{-i zdQCoRD;_qjWS>>cdSBR=H{K2|5^B42B&Z2)cb2d;t=*Tf&Oq)oMm;VQ)C9Ko(93zC zjoZ4ekKR~}9HhtgbQQ;?VGG{jRK(wof<*1C;F_3ZRuy{wOxyGDXw zCe81;WqHK!v_Z_1{5q5tmC8A4PcU=Sg~=S=cpg7MfkShDI<-8%`u@g!6+91oo>%Pz1 z!y1DLplKx9%T!xFy%2^$PvCl}qaE21ntl$VU(>Hn(h|jy4%+l{8fcA%?V_mV#~^Wj zj1ngyEigivMyy-TlM*llRI&b)q>CSZ~F>Q$qqqpKA zjwLW`RI=h(pUzD>`DJAdN=1U<`>000ac-ZqN}Q?og(5}h$R25vF+DFR(7@^~kCJ+t z2^E?i31j{?PH_0G*aR|(3XFukHu4%hF-sQk&^GNiELDjgTr|&}@GAaRrz8AX;rqy` zLWSc=f5j#IKH+D&O{=3MLKyMiX1&j{1X8S9qiL1U8YeGe12(ssJfN$(oeD0ef`dyjGp5tBN5gz6TtX}IR`VXIM(c0&{ z?tWf!JcT!`o$TIAmVBr@$1D|6?X@%c-WH8COb^rFyPssa#Rw^zjZu050~U<|#~9GO&h zyIcBNj~K!zH)#=8)qB-qB+Gy8yl@}zVwQd@!PGdL3$RB_bbuYqu%25_jg|rEY0ba! z^6m$Xsq144FmSOsm3D_9Qm*49en$`3ZEeuUiG509_-*fy`wA)WeG|6A?Qq)+uEIMX zX40AYwoCh_#>62l9#b;#hQqy37@$ zXBZp)aL3k$u4oIUErNPcQeV8mC7xKLajDV>*RT@1(Zfu}Mk4fxh6_bVWwqPF8$h!7TZVO(U9dqu#ujK7#a z4(uN_qfpfy3)gL=qPW__@scZhJwauV7zc%LDJ%js=!xv}oj4GpE&7)ho)4QL zYziA3ROgco1LU$$vXEcAY=LbS)kzYO4_9XJhlGJQMwwrl%I3E(y@WW~O{QOtVa>me6QXXo!v)gqB`d76{AMOk=C$+jvy*X*eE zByi!3(r5PUljRTV&oHC}@xts2??(MGR`vaq@*VfF`ie6hE59vfW`{?ifm6fkU7hVo zWFxd_ zu+4S1^D@hEIj0m;X7t>LB?b0h14tE*+k0o*TFVB#OoyfKBHNG!Y2tIMx6M>$9wR!Gr{Xv$RM_KctiPk$>U&fbR%t?@$gJV!<};UEfe z{d96-bx7_0IhNU(wj~mpdGNiZyzplM)lRWy#rO_aJ;jwl}jlkgO&c1627-&Y|E|X9Mg{~ z_8v1Gb2R)WOFn^eDUO>{i_;5+_g53HBU~fq6C?UpJdqfn#De;lo77p+8YMjmR6ex) z{YQC{Z>jXay#XkNgC2f63LXLr5crO>dd}4>W=>swsSbFM7Mj#fZpgYQy3M;nay&K( zQ^HaJaW=jGHNTj)H9e+(Q<>zZvxcv$40@uD;;9M3C7#TiH+P#}cR8trb%^N9WAH`w z7`*W36{mGTI~)s!TqGQ0xzFRI_#Dqg^Awb)G$@nTLHR6rZ?S;0VXymaV?okGwoQQ> zJOG{KONMc>OB-w+y$UvkAa0>C9#WLgG>;bhPoCtm%*mOm5z&MLhp9}9!(~mQK?3v! z8$5Ln_Blml*lz$+UyMHC=hb7BN@*5iJ04zKuKLC;sbq4miO^Yc(RV~~-Dg&n?6v|o zWfJi+Q|e!%{g_kqR*QSJ`TVuPOz8NKppTM)PJbaK#X6!tDA^-Cd~%HEnKFQR*lZ}i zsY`)Kci9Q=aFI;w@?<~kWA?6ilx3U>f4@_r?(^1K6=3Hcd|3t+PR1dLOlX$%iq3-MeFxFfl$#5LRp-Qd!f$`h>ny0f5d`aK*1~v34UPM+&4(yicdlY78BcP69QK1ftCkReD1fy#* zXt0k`!5q|Y5**hFg1hx63NdFuEA?sZDCMH`crUVK5OO#3t;3?@i_vh*nMCSk#g~`= zAA4`%)K>R}jUp`+C{i4P6{olq3c;P?lop2q#oaBq7q{T9EfjZmw;%_Zr#|2GR$g#G0h^IkM6tNrPycq1?$vTtG;>C%g<6GTZ zfFJ3BWIW07$Fi+@{QEaDOu4#3SXbE+Qcyt_9bWWK@ZZeCq#=k3vbZi1H5D1AWel|p z@)cVQyQ^xSa|8JpH(N&Quv$y~pQE?JH|PYen4rJr#eMLAAa6*Dj(n+y{wg!4|M5*@ zxcrkwzoW0iFGot0KUHe0h0c^H#OF-*Ogi7+kF^;)GjaF-a`qj}~Dlz6`l-*xt&Z+1j+5G40DX=|cpYtw1BDjE8nvLN|;m&8VuGQ5L&rL9!> zp3Hdk`Ayw>ns-vh4zCSs4!WW4Ytwq9TnIvPVaJ~>8AOW@J6+b(|EN(G9M{R*VWxTD z)rZSKL{ENrsN4A2PJYK|fG1w2k$*gAT>4ARf3sd3-IO$>5FV=n+dd5#I1^|tip1bH z5xDLxXCqE-gd|0aK&s0sD=^=E?nkQe$O}fQD|8X2dZ5CMaI%~eoh>oEF=Iq#7F`YX zx-t9CEb&WdVSzPKS}ID@<=ui#z(AOkiO{gGL&QacHilUfM~6@*}qj=nGYO@@KSIdhjgQVUZ7>N-!aLhd6Acq--sC5_&!5SeoY~b?Ek_Wp*`7;^JF-ui zLmGXBj8>FnF*12Ziq{-`_rdGGghl4w_)FK@Jb~<_c3JPBvdWb~+VbGql`tTG-TM^w zkn%zyc4=`84!0A;Ldlxtr-j*Bd%<{g^E@?9W-s66wv;1kpMwd$4VwwXSH zI`*71bHqE0eR{8aMwtcID5>1$9L;S;hT8o6u5CB8VBC_vfvvCR;7 z-2KG0P)zOpsqXEIs`F)irY<)x7IFj_FG21M4c$)v3dv+4qoT;x)s)ia(^&!{d_bHhB}z>3tEX<=W=?4L^p?_w}XlQGz|A z>8^zR=+$H*m^CH+MML-m*s0O?^XSS7!va215079;CMI+{(d6a$VaecIief6G%{uVG z`2!+(R6Q~MQ0tL*uOsHxm2sA}Kx5RuNjO;Y!swOE8>xT3rJ}y7%SI3$Kbp3G-4v)T z=~6gIpcg>>6R%8+MDM~6sh|0qN}g_oNiCw~sQphy^g#M=Z=MBIfG{dae6F92D6AaphG?rIjs+V+H z9;~~bBg*Dh|L!7|TUN0EkKm_}f7mNjc^`N{MGGGBTPL$brR2g0rb3>yaPX%jO^Jf; zZVQws3M-bfsP9Pv*&r@R40UwD=p;rmx#B3DbD9s)UBF=N62Hbo7xczg!RQP#Z`X+_ zbNtSt-+fO!sEaHT|H{oTR*@Hdv`kZ2?>srxkMubeZ*1{UPkzpnbTZ-t6h9H_U&G@M1E^m#u2Pr00TWz~mH0)Ya zoo~au@j;gj{T|b;9<#{shW*alhwfhpJj!Cxvb#b1R`Qv0XXY15H0r~@xIpzmqpKgW z*~D=_o5HHySO#cwhcgb7VBXr)H~|!xIS=n}PNv0=R6#j*wp|h-lu?^l_*0<9mYUJP zWmpVW&ilH;FbmPksd%K%faz25;~Wz)nA+d>O|P0b7#LCi6I2yFVx;3QOqG1Gz1jZS zQb+qQh&BpdsS-M@)8R0uG6fsCgm(?pvsqFSIklWeAe9gXwI%%hb%Q$W`G3rx%oOSW zV+rr3cam8Yl^sCmKXL|ke{WB3Esl~pqeKyhDWCK~2jweS=0k1SH{;iwB7Sy+k_L)&yOB z#t(`y;6+e!i}-6=HyspbGtgjoUbq8|Cj)Ha&XF=eD(h>~@6ed!Mb7bYy+_d{@|g%;i7 zk%!>h-ydj@CqroMvA@cCBD#mNGg{?~wkhlIIGWKyq9;c}{%Gm&m|Mv(s7zFT@kC~} zlBPor8{GVXFzj;YZ<-_+42^^)<(0Z5=a3}fSxT!}LlAzGc}j#k60>W6BidKxqJ910 z@eo}efdQ4(hDB6yzU`qRIe!%LC#g!*oL$k1S;E^AzJl#)(A;5^D=Qa+&L$7A3G1F~%+}6PNWxwn1>zt)Cp&V8TAm<&buNE)OsMc(pL3VkaO z%uA>;JpZ*US!>ZURSATvKSOl0?@*jx`3YZF8Ayw;Qq}x*J=f!wKL5Xj)TJ9Y3En3! zs+82!Z=^9Cs!^Z-GlS=lz8T7u{@&+ygVkYazsk`{G8|zqR4O+{Y3RgdA(l&E`Wu3t zlU{@%bE~2$0B5x9d>A_}`!e(AHVB{&*wat##{Cvd*Zzvxk3Kp2A^D26K$HMXg^%== z3env}hF^@mqzV>PD3ZgDb$mPMgvo-zj0r9yE% zEu4|`{M$Mb7I5i#ZUF7NpmiFAY;z4v|c{%TYk38o5XRfkWuC)d}uK(;+ zQw^2cZpD%WL*DxHSW05GB}&dxcw~{{I2CfqThV#{gg$YhM=LPm8ayv|*}z-=KVkq0 zo5^e>47l&GLXw%m)S%&{9oBTEbmA~xqt|^5fB(LwUq&YoUCqW^n>MXceZ{YGQRJHc zM*Tf<1V9~_udl@*EHd4WA<5roW|K+-QlG`L=>!M6Obs)J+}M8c1z;!;nWRjpwTLZxhxZ zCx%hd6#1cZQAkPfP(2r}?EH^<-i>>9cS#fmyLWmIYh`27|iI;wtUt#g=l?MBq@ly1tho-oE_SG?1n) zlSw%96_xrK8aMu=i!efKwb#WT<#v6WZx1}^;pQNG?s!Xisfw<<=}k|^-& zWd{s*TfGeQh1Paji~S7ksHyOsLXq6D4?KxNLp^Wjj*vh6=f|@5`v3D|8y9y#+-$p= ze}1fR1Pt;#=w-mnk>W9;66VCAEvx(?x5(>P`!!k4xq?i?P|i#gOWhC+>9zVaz2Blk zCSHYvAzEqs=hAv#{clN#Wa~Y>ywmhs(D))z^t=>PBbACxMur(InZv(G6r0Em|J2&k zI@WR4P)bz@BWRr}(dl5?=InsfXBePmygu#FAEhkFavS<`ouU&)}1y}0$BB`{{ zWR;n}wL__2@WBWxv(0GHc%44Ce24!N0Qi57y!+e(hzmw78~&HXe`ER8)~a5N0DA6t zaC5V8k%=&u1b}&(`D~C_wBjp4=1prXTJ=4^GHc#Bc<%u>af%1zuT z!Hh}CJ_ZtT%r>4igj$XCQ*px2{n0y?(@eN%3p#`v>6HVPgjjG~J;})>@-JAOBfwpo zQmpoUMQg~LyPU!(xPI58Ns=de?0y?@-k6?^;{kg#&xq>K4lBAW43V^|;AbNk;wZh5 zD+%?T{=c66e~~zw=uj5azWnS}FiwpR* zN9v%TzQ>d*Ht|p@NM7+2s?RSJ1QP`I^^r`0Ad3qHQkcLrnW#^>;qS78NTtIl+blQn zga;Fu%(ceE5tC+h19p^^_Qk)7_#s&uhKKx1f<(Uns3Zd?58i~4b5ndI#a9{D1T_ns zaG?P-w1=HH5=wrvCf(IsXd)%@3TLu!;B`KIzS!kQVH94qAt{0LW;>KXd9?5%89q2{ zVi~Ucg0DF3EV`zC7Ct{}ef4dC8B467)g|eRBbm-U%7se24!g1`^Ar<`h1!J$`(E3s z*r@k0SSR^9p~fQ=nWb|1ipLWrsDd1Kf~Q#WUuTq3lxqI$MGjE$!pBZeh({su>0?+{ z&U3`x9G`wuVmLqtecCCgFSwKg9JbQ-r@~Lj`*4EXIaCK+zt?rU{1S6vmiKP2f@vqR5RKy1Y1+m`b0vi|8w7H^8a(+hIf(VXe0UGwp-~0%ogGU_E}@xJsDUm z5LolVkE+4a%q&(9sBj-1ud7akG+4$8X@fKUYg_7$QN-T^+-h_V688-F%vb&dyG*;O zvatMmwGzgNfS(|%QX!F?S%+xfTTO^Hc6;k4q!i9Ebo=`lD(R`ly|KO7uwW{F4Qpd^35_c6* zCSybR+&0W|Wmfhw<&zMnwOgCXk5fsgY9a-Ze|nFbOYzM5fbd^j?#BIJT-MC$fWs3S z>NN^&RG~!_DKUTV8 zaX4;`YU+-Z=2Zwr8_dKw;n!$Hy`KxNwaFi2fatO~^bf-6u(QbWRm_d%+9a(4?Z4z< zx6V6?2p*S5oOr+}6^l-;4OJIWR;EKK?(LCBdr?5FS8D|{Auu@JNxGD2~n*L9BVWNahGP07_ztn0JeD zQ;KEqN&giR$5kgr1YWEJQa17+GaJqj#xzEYH;n&yC}V{0#-d5o6$dGDV6z&U`ym%M* z#s;BNQ2P%xsfB<)`lK|*{URis(Aq#Juk9xxmmj{M5=u_l%pqxIeGOBsF0#14hW<9L z3a}g}x8Dd4OI4*oqkN9)A6l0*u%Z&{3IwZD?tO2{^Kda+DhI*yZs#42xo!=Gk>;;8 z=I}MBJ(xkJ5(KZ85k5-if5fi~f9nUmL>zH%>atJg3xSva2^>$U2z=Oke7le8z&TFs zMoiRMNStn7ZhY7(`OA1LtX%SMyfeN!cx*7`l zT4ux5M75{yG{2HiBJ}-vDfbX74ZWuL9_)r{j2>-`B1mXeEOb8}fbP4WDQvhrJCd z`;Xh@q=s_@EW%=iAb>be_)Rw^e$N$j@|Tqhoi>~T^D&L0wqTb+Gy$OitUAAeMTpZT zCuds2Md((kf=!>q!&T)PggzMqREfzB19sA~W>D-yQGwg8EXU;ypZ7*QQdnrG5PMI{z zF%BBPN?#@s!q-^jNhMDioy@)09|?o(d%rQvQ1tCqxPA(tya;j&L1{L%g4#@z!M79# z(P1(0s5%Uwkq79f?I580nw8cBf-SJna++J#am`cdxh+FT8_w5pUSsAC|CePL+u*Sx zA5FO7qbMR#B}Nr;t@@MI<4YV!n-py*O%eV%WeNut1W!-9SW#AE$IP9CC|En=r4(y9 zkw2bJM(O`G;aqLYT^qUf{ceGFkXOi{sg`1>VRiC(^b^9gp{bh^)1J4=eVMI~`y$i6 znZUA;2>4dLh$HYTcJ(@EC6YPYYsPWSBECWKq01sZgrShbBnyl102P{)wXi}u_$b>t zFuDIx_y|a*?iYQiel`_xE{Aqm(crMw^6dzS_K}e=3g3QgASzOc;KxbMizDkyXZrAw zhG|FOlcX3w9CXf6y`6+~?g-$>JUHp;xhEjJk-4qBp-yj#PRIw>{tAR|#yM*LBrh=B zKNYWer0hUd3l1C2RuVa(-EkBge3w<1lWI#dwsH=R!p46QXZX;%Mbp6=b{`B4HK@&_%q$`;wHr* zE>E9Bz;Pl!yuJRNg%vSh*AjU~O~cFr?YFzsbRc*GahcYy`M>A-?OkfZvNX=Azu#4m zID3srbzG1$;BIgJDaT(d_iY&zC*U=@xRDG=bY0ARX!2r}{R3TT&~a8ntu>RnXoc%< z31Be5(@L#P}~xGGw*8vZ^lACpqn6g5!t^9 z|MeO^R_K3Y^{V+QK6?QMvj(FRL*_X6Zf%yY#|(xUmny#sic6Ow+~Y+DFJ>9nb;VX$ zIec0Cg!l{N_Zx&9%U}S5?e}0n@X^zj>1J=Cg~@`0h*!-dyyH{Am;A|T`1G}|4Oykt zjP&cDy{~oQ5QEX&U@(+2%GmXQXRaF)&DEK0?G3kccNRLI5&~xcuMz@?)W)m<3!1tt zx_QRKm%md9WmK8Pu8kCs6zhSijoQK?WX!2yS_6Oqp{GU%S5&Xv`^h8;k>8fdv=?(^ zD++JSmYCEI__1bjKmj*IY(F{}P=R&b9R=NEoce3V=cS4h2)Tu-HOE>R;0KMdZ(Un- z1U(n65t{O864<(9!C^4_7RwU6yKMy2-*OpNG8iFSRgJ(*O=?hWM<*YUyt~IC};iR-ujfjau z{aKfexFxm~h?25j!XU3UEitupP%P~V8<#)QehZhw441jxt9Y;46TS1AMv22)!O}cJ zfBJ@agf6|IiD#PrV=@Ar-d+C%wTdyOT%6{YASuzR&lQ*|iyvuMsaBz=@Cg^{(jS5?}umNwg~TRGjgPD zm;%u48BF^Fl3RU6|FPaAF77K$cc%1j#^C!*;0t^~O>$t7<3ncfJ~ zjAtw#(&Gg)HJZa_Ms2e>+F=!DPx6S5ha}`{|(xIcU^vn%>N0# zK+iG}g%Vw?ob{?3Gk_ibciKH;nTvCNhFXOh07J3P)S)y=XXu?*$NO+c*`Iy1dBzgB zzss<+b@u-WoiG7Eq@jKW4r(We_c7XZ&R4B;QY6hecoyF!aN8hQE^v6z4t@WN6`4SE?iL;c z!WaYJQq5E!h0vNeXACzi+UpA)#OzXGe)p7^${Urd?8`r3q>sbT^C0GPm1WHHdlgLc zH};CBucz1}n2hUDlkWO7c89q$*Tz>N+A)PQ?=4Q7(z=sQU3h+$@egwnm%VsQOe^_n z0dsgXf!Ft}A2x{!pz()6MX0P60Z58SvD$14EW_UsAuaYl5O_b-K=kUJ%B+21Z^V&_ zG6Smp+M?d=M}@zNu3N3cX6GX|!9R1hqnz6?;rlVFqg^$FLMUL~8PaNwMujA=Ogk4M zTF8k!ot*IfK-oZvB&)rZOVOaRzv9y@`1Rza6gELU6z(hf*hFK@%w+yKYd6A7ef1gOt?#vlObwt6pVX_v-s@Jm%(yr9m7b{F$-O2&=i4 z?eIM<{+UY5 z|BOB;FoedXbW@>$t;^cZ62aq}qCS3T6HeX=70pBiYmL`UPr8{mj1b{UAS4DpICz7p4J*fU(9eiswg-+xgClS7 zUPT2}isvwqx=JKGJ%~VCpBFAVW}GX&q=`lm&bdxazp|{I@8#Qkv}y49m6~PFIc2+U z+@)5V@^66}Fa5s-YCQYvuT)s+zv!GxTmI~Mg)?Ks^^e48#-Jx7Sh^dQ_=mO2`|{=x zc}*CYhayf}l@*Jz?BquB%JszOqxT|aGHB=-n@B=5L$>94{_E`>6d?Yrcrol z2#nBQqICC*aSN~KxzMT!t#_vR!Lw~27^jNm=FX8xXuH++o$8423yEf)%`K@YJBorl zJ4!~^$2EVW02YX7v!dx76E}aqwxw!<(CXb`cMM0c-8{>8;=OV%W%d0<`Y%z3iRlf^k zC{TI>g=YC_UX-FwHv#-pm@hy zqmaeFwG5G!rs4IFtkdrTMz$G(cRjnI{vtGd;;s)=B8Ry)i9MMaAJcZ3LMe&0@>sX+ zLs6&SInkWFM)n7+t8u<_x|_d+Wf!1QtSx6e6QpKJ<2hVf_I5-wJJW0lWvstA_76-j zB%@A|hoXM6cGf0VEDi?12uJHaH>?TGFw#?FpPsQnz_ub~hMvvuxY8UOy9OCUfr2SY z7<}jY931d#m6D66N{!N*$1vGN3l4fq=eQ`|n0bMmSE(aNxG z6udblkNc>gj28#9%VAyKASQu7aGZ1R(xA+!X!GUmDst0U{XFMjzT zblQ2r{chn`Mi^P*F^c@mT0$rC-I&?U&(Svn+~>l_TQp!GHKnh#v|TfQBy*w?X+Esh z-qUyi@OXr@I-E zVN*JJD9G@;yIy|F_W&cp2T6I27p8rqDBS&&M>Q-@;_RrWmZFc8_IJZ)Sye*7HaFLbG{AWA{}+R-eWx2PCx5c!|T zh`=SB!|PZyeX#F|{!ok=nP%Yn=&0F5Vau^!Bbh!6+H|gyeU{dp-ilBUHdvQA0unQG z+I~h$=FRMP6tTInwyu@$(F1zslLsg!LA)0E2roiD>Y|v6Jbk(BLQSd z1RtC(Pity9hrgyjz4QEjbnTAc;k{DOwOV8o!FzwhTfZQ`_#(PEVE%B>vpexN+gCtj z^?b}XH0@@5#l_QUdkX$uCGKQiWxFgbU|l`P)ca{__}7zL$V@ z`xEn!bJ2UvIP7d?=?bi5i&*SOWyo~LvX{WDzw-KOPE^DVch7DIKWOK-W0S9^W@u{5 zxPG(a^*M~&Y@%FGxTU_?(ei1uf9HI7XaBw*mNZsV{B4dG%r~Li4C3wB%nRHSTKBDi zIUKMGYTXs+`gn#*--@Qj>0=Au!MkuB=&UqN>Nf=h>+HVUs2x7Hxr}X`TUI|!@0*Sv z4D4DR4DN#$L5vUPBdY|!7oX>8-wLv5dUOnrQ{<)ZK{xNWU-oX=V63o;dyejW3jwF# zMsx-8CpA0iX)nqZ59z65F5SC0K<3zu;6n{hH$#X;rimksY4G?h zYWp_dIJNOgdRN=8lShbMu!yL0o<+|`u`NOQ zq})!o|CZruwCTn%2KjFF&_2J|N%`XIU^5-`+QGsRwo@|pv}ghNX1>#1$>@8$;;c*7 z-fIsX_(ORQbza;c=3Vc)aSEM4cbjqxb}#neAjd-GR?9-HH`#h3(a%%*Ac!6LyEh>= zGagZ7vg6`pjAkjF-1c&@e8S~n5HCeh6Ii4pkZqZAk+m+L7zH*T`3Tak* zJkQDIBX09{_cf)yq0P>rdRk4@p4-DPc>n_+dzMxU2zL2v+HB6erPgtKW@y?>Icw=t zOKY>I!&iOPtS$IX++@-($0WUOta2&JgYx zC2If4fw)tyfsz>7~9F@I*X#|*Thi?a<;3TKqec86Votp3KR_*JHkGMJ9eKWL> zQNNpT$uWQl{dBg_A&>onXOkZh?0f7_w>Y}%tB~$o#nDeR!amtA5`WncIsr1V7cpSb z@qHA;hV)g}x4W8dj-H>xwrkP>#cUOsLf+wQDI-J^x>`C0TEZ=^=NrRG2^vavk+$!) zlSJun2K%Zd^`39G)I9gEYg#qW)qG;l@~_um6|o5 zWjuE_o;FVJE_!xBRa@|RaPc&;aq)gS?1%OxXn^OGvZ1XYhwXFdP{M%~!`wHeY^@f@ zol&M{C+jpeFx}5}FANXy@fSY)x8ct-ZTH83AxumE=$_b~i&GqjyryM3UmUjxjrL;6?k9=&>Y zUp#Iz>{RAfFX)B)oC5`%9BiDNGFM15w2L05_J7SXA6Qu>v;ASU%V=-)vAEhuz4}sW zHa9U9JoGU7()mD_RrXln&dhm-(x=`ewyiZzVsWX_0h4J4;L`|Nwun738J(B%&-t^@ zbtnUfPB40Q4Jd>!GK`hVdq(w=d3pcBC|vGl&I`8_n2F|;%d-~>R-kmA%uNp08~fW= z5y>!ex;DG5ghBYyY7b$uA~0V`WWfV&?sDl}$KCCA6##!#b_(<72|a99kP;nRFiG0X zJYXkq%l|T9LYzXOVn^^E1*+!b+0jd*unvJN`nHSF1=3k=wE>oM+&T0L1XX2holQD+ zwX$+RM&OXaEx-)ifp6TcTNGLGXOB7u8gT*(OKE8GmE$j#d_#)47`UI9vL?89Umhtv ze==Ok`0gqa>0cAf%u4_+?x=@}rfg;?RwVn66=68Hnp15|q19m2BsbDW`dACc9us6? zFzpT;^F^xbQxKD*Oh^g`{Wr7CUelEcY)=b3N+RP0;xL3PFg@R-r_CPug?A^ndB zhe4qoK#9Y%%^1sDvfcQsm)e!_L=oiugIW6KnM3F>H!3DD8++>S5!v!msG<5Q9qEpE zC9mEuvS!J}Sg);vsdSnU5}33H;n^F5NI1NWH(i-f3-mYhG5Itc+2$KZ;ZEdZ;I_TX z#Bnq%Qh;*u9ZI0D_S}B*S^u5jMd@bC`6hdB`Igm7ssUgzNoTjD2cQwsa5fQhLfX^M z-z4-rnjNzB%x{TpHZt18vwBOUBUC5Z13DQzL2c<+?#`DnZwajDd4caM8)w%G@;H1P zBfpf6xAS>>7G>`anPl%8Zjh3m0#Z_}r4H=+UgD{e)hjL9^X+xGR|CF~$oW$Ep`Nq0 zJV@}pEmXBVooJ3aUKWh?5qaIie`s-hYJ2(z$V%fu|)E(5i5#h z3jL%{K914Yp)R)MW+(8Y>@UrHj$sak=L8y{(NWpe;q~!1>qs1Gt&#A2<^#f9ILRIT zom1bQ`O{qoS1_wZh>M8B7&QePG8e38JeG?;Uyzv51_2%$q^FgtQ&8hsU%+S1sAd#2 zPuQ<~Go|lr6RwS-NtMr(&N@zpq87?U?)4rka@&LkR-HDp1-(|vrG%L?Cg!-k_m}k| z@--5iSUm5k|IZv4(w6urnl4J_e4dqL9W{#;UJuyc{#xc^UgdGVA0JPdSe`3 zZ+7R8dLAUt%@JB?RkeuOOsLhPP9e)3i+cXh;uuhLky0+T1DBg>bEG>}XraMGha~c- zdk8@AAtkQ6u|`ry{C0+u>pqyIOnb{PWSWsr z&7v_mj%U|{>g4FOUSf{=OCxS1c#gj*gT4IX@*U}g8se>CD-?FJ>L7p&3s^Tc?cY0D zXC!P9cvb?>m$#T<{AnpTQ6laQ6ZQp zv?KXJdqm{q)RuL(8Up&e%q4>@c;MlsZ!m=Q48`uCJA!oF4PZZ63k00g{Q))^`;I4| z9_O$sPC1jD-3>YU?6xK8Pu)6Spcz3=v7~Nnj!kIW0?tH*uX$#Z&HZ|&OlFSyPq~af zU>AfB(L0hjcoTolUn16%CXstF2wW!vv(c=GnHHq`o^XA>h@iZERmH0(E!as9_vOj2 zg}*SR0P>T0*f6mRh}K?RNHj-s@!%GYgn5z3O$VT!uMEqResLt|NCpVT&b?ih*QQWp zA2{qLkkg=dsxk4zTo?M`d>P*#33*ZZ&huUuL3-*#kW2)Uy_B?97dxL^4-M>VBp`nz ztWU5~Qs=jRx!nsF>HDR%MepX;1wYg+&tXAnwRLwY%5CnKFaf)qDAijLy`r}EPY%)b z7`277M$LJj>OqDZ+UJZX+vK%F7ZZvl5BpExe2-3 zs5}o2zbi8-c#N^K1~!9I!PgvmstP-3effQTopVY8nXSyD#LLNAT-r47$rPrEEbu~z zalC5$0E+^+5i8zAK?wQ^*CFR7JbJ1m8$X&B?Ad9U!cfn9W0`+4>7q$bLo-lArX3n( zf27FBXw2ZcWNo8LSDQ5Fxnq+Mw{dYBJ4;3V(~Q2Jg`Cfk@M54>5x5yhbmg&0+B71BCJY5ok} zfp(XZbvMA%?GyxAO(>03ULmu|7@XKOPq!yH&nhH66TWD_=!A>s>mWnyKk7U#0PxhweFFRXApqb7D)pW@rGSj6T*aggVdclID_N3d= zA28w2(N;w;R-U_cG-O1g{8j!offC#DDyLaW=2Yp&i$>};zW)40oczCwI2r*rDK}Q? zesudB92qi{?Pksg@ya>?JXs zW+H-pPc_7g|26lx9ht=57yB zHUWKM&P3@Z&gMNXt~();6BKUg?3l`~y*y*Mj#ElcNe$$Ke5OgZu(J2?N24I}tLyL2 z+h|hwVR<8yvXa16Z{hejo-^Vdsx18TsR4YrKwv^7*l$HriQ6wZNAoR?x$XXQbTTz2 zXC78*C1kykm&m*xAEJvmXe`h#5D8wdQQL=4-LtXMr*_l8q@n@&0C#EWSz}HeY;42B zJiz-UMm7NoMs@*;b#J2w2*t&B2Z)1`2juAs>5$BQb#@_E`Y5E{>@Zca;m-eMY^r~C z^M&mgC!u4&dz;7?hU7-_U{+fNAqt%^AQg5(PahuzG5DTb>fejS)^Rf*B+qG7WN5Y3 z)_ZuGTwNAMl$vSq@hWEvcv);t^)WM>!5AdIjXS%Tm|US6wOhFd``hJuYY41@*cw@2 z>WY{ zCt-_3Eqh+f6=t)qUk$_SDp}FWPnSZ@e>v`XMC^IErBAfnIofOqThgCDP{weKra_di zPuJ`-rC0aXR^BfEnDPwp5k6VDe6O>TeZ%^0P^O20gIu*- zshJa%y=wtzL-1bhy20|LjqFG`qYIe-5y$&CTECK5_Dkw=BlnW(2d>bwH#8F$??75)nvc|bt4PR> z;^+sdiQWC&6NAbM0|ngR=d5YBh`@QKkp(o5n3nK&10y%TkdIYxS^VmBeH#<(y#*3T zy$=VrAN?B(R>1g z=T+!fx8PSzK>=%&5Djd_J~p2n-4A$++j_FI1wH~MCr?B(gDw;z(Bu04nUehZ*rs0YoT?>DML5l4NWyE(2NyAg2K&7K4+w+r9t=q=iLJ zo>pP+lf52^8I+Ay@Y^l|h zXxxl^rWaFK_Q>gQ9yxC)FUxed-_%Q=Q??dvygLH$l?$Jr`lW#6S8C2~-CEBz!@Sct%H2BId!hcNZCA`rn zw-LU+-RVirYJA8Yplo%xf}mE|>Tlw;ht|&>;|pF6&K~elbku9jXO&dP-b^XYMvaA1 zEVtG}Y7C?$6YU?*cM4?&Pxl+9pY^@Ama((v8-0u~ptA=j0%w!6p%p7ndv!8wwz{qN zYqJ35Q==o1qN5V(CfBPeWZmt%&sE0mI@-ChMwVMnvuM?50qq!xa?o$?|mMRv&uLV5%E?bSLiF zhjqOjWyK>x(E$pe=A$56h2{6QCe@#9E_RLg8z|RK*G8S(0Qt)GmfTOfWTv(jwRYX! z-HGJefI1!mtY64`TO}9W@3_1`F_g69zDkR^Rp(Le3x{abr;n5!x+@+wPI$U(*mzIr z7Jv?2dO62lH(a-b`i;GxVP|bVi7Bh%i|(27H;~6QJ{^!tl=5@5=h@i-_9ffJK-j&( zB(HF~+th{dOLFg~mDS_t!^S%1v$^o6FSS>0MJihe&tzA{76&Jzg;p_3?py0;@nkd< z?90jh%0E7=#ilEE_{*A^Gt=u+>#wx7dmQznm5(%DI8a|4$iK`)VpKm-Qtv<|AI?Us z4*Q2!k)96ljs{*YJZIx1yd;Zt1jOjKRY8E6cGUYewO7oHm)72tXX8_@jT&M*(HdK& zVJ<+MiuBRZkX6}47wCOb`!kN-&Xdlx2=%#{`GJNfT)Zz#oW3FTXKI4(SDDyLoi{~i zYKH}82OuZU&3Q!O`&+3ra^)F2&rKKRA)w8G+d6b-8xnEs9#sK}dvbvG?>cpj6}=F| zQRLVNW`#e=(<+CYP4o7ThImj!&LvrF)_7Y#?~EAlibYM=xU_%jKly3=SN&+Hf?;zv zcVJ@Yt{`glJhj#7IxEMBe8c8&D;hp8*m&!<(EgLkIf^F93OLWDLil;|Huat~uMJ#J z?G~BF`a-kOnp(dh$;qyG>@Z;Bn*ZmMYBzk4l8FtI-xS^zL8j_8^U65F>*t~=&%#oU z++A+ILHln`?)!W2^mmlGBI?hc0LG?0BI1c&=74Nj@}sj*zK1!82JV$fZIp6B=B4!* zrJn=I4F0NT%>7Lz^Z@!m7Ke#M93Vr60D>)Vdqy`vjqA)Ssucer=&plg5d zFXd|_+abzsr&`=SuzE4^#H&6#EmZWWXvnTdZgczGU@hEwH;KmpH@H00v{$H>d}yomr0nh)BhMRL zKc`UwWKEV9^i+1hQvWNBZ19^gIh%ht6`JactBt)w6=k z&MI(DHTtR9gw7e;UJIVRY3m{@Jzb^lkC}2^95m`3mVSv{%Pu%lDbn?VyP9yBa>4^Q zeFK9Wn@JjzbUV64gSitjE;c*cNz8y1Gv|mTKy57PYSj(S)dw3pgiGtEcN=Zl^&t9$ zkAE1lKRIkdH?cdcmRuvuPx4r^hcag}!BT=Jo2QO|o6(lE3D0w~-D4i1j2Jng`#D9^ zW1d#;$ZyPB8%pC!34$Z#x~v*iAgznKcr&f;n%mh|r+=tVx@N6n1Y4b=pY*y##B^2y z-!Y^=e(?8Ast1O&%(toiW$X8q8--U}vyadYtuxrqTHRpno@e{OfeDI=y^|c)ljX1k z#aqRdxhe3jFSX$5EieK$jBx|B7`ZTzT?kp7_9nOS01rkCMw4$O@pO=n--N`c933QM)Z~!QfJC!UmuFm#;I& zl(9nwMMr+WFmD#0$_3mVxo35y#l=9ERd42CxfE~`8gs*<6vg5bREd4fq7d@X|=HU6Gs{%lo$2zfBu5YGPFo8S} z<*t#P13!;Xk$)fxKl83lDiq?j?X_5y($Ru)=L_A-2R+RRMbtT9gxp0bTU7B@-+nUm z1=pYFj9+8#cf7}4y&DM}?ggoUR*g;huf~rvJYQH<>n)gz^<_rBWz#-RK)(ImJ-qGm zPWj$WJRArF3N%vEMCK0%>YPw6`O46?lMM%s^tQ@mQ}^FYf1_Bs9}j%MkX(<*6KsqI z|GB;2ZDYr_^?qFQiT&Qr0sj{z`BDttHFE8Co5v%xzvnW-@MuG;x9~ohI}76A;V^tc2iwPL%D9MZ=Ou^E zql%7fzTTQ$mV-89{QkcE;m7BRl1A&>C&JYuPif}-7WJab$e(G?w5g9b36TYrsZ-n^ zem}VUaI}n%OZ@buc_H;%dY{-6{*cIL9XEF3jV88#14Qi}@oaa93E#%y`b5v>nw9KL z$<$Z9NOEZEeS+m4=e#PpI0hSavtNNlj`^Uzo9b9#Fm+1Ex^eXSblQKhB=_Q8AR-`R z`~(q_Q1sM(ylK#<8h+lsyPo|p`G1ef#SfEz(2k$x?VC8-CCy6%>v=5+%>Q@HClSD} zEZ0+555RBx-;wh*=MPpb`p4F<@NfSe-y8m2a$O{``~PqI|EfS%zYeqrE_Wo|&E8qm z!)BPjm|XZYJX(zFm%cd9T3wv<`8>Il25Ja3HCQFaJh;s=4}pNtWZl3e~s?u=lnN*HUD~nl>Ljq>wTW(gAwQb>;0LXRcCHMkHGFAm!tBA5_QBhG)5D~(nwSppxih_U; z6_K5=2M8gxsMHEXWM89#pah7>61F5FLSzjP!xl)`mxLrhNJ92s=ybj_&U8ARR_FPD zzki=R&->n+_r2S>=ic|+b3XUnw5pf2+BOidAD?*1-!Q}}yF&JOsp354B&)}6`Q;N% zDz~SOJ7rsTUy- z!1SrrWB4IEIp2^&joS(^hE_a*PIgUkyvsDd*971}k52Vp{ZnQGv>irrF4Oh}DW*dL zFT)Vw?C@?;%Q1;+1iF$;+aYRs1x^VC=P)IP*)=5G&+V-=lvN-#_%Au+Umh#BFPL!w z%1S47tuvMmfVa4GqvC5hy3vt?Keg32WMj@M?M0+*E5Z+|mt6x5?d(~jJ@dJ&g-gk| zSO*wPqaksKVF(hI2_v>YntA6NGWRgbw~h4(%nJI6YF~M0{gtAXh7pgafWnS%oYd-W zYsx73QxPKubKl?$D?~Fr_QdC8cRB45`=%k`y<5uRG8zJvmO8qb9mFAEfh}5q=Ialw z&#*2omLKR(FTu)QAA0<^jud73t+6WbW3%q$z?J~(v==F|OiNI*?xy*$K{ZN8vTNFL zN2U3hCu9jl-pEx)q6bL#3UgfzSJ`jPt*PyJwRS6MC}fTGt7ps-UH_G4A>iy=n+_G$ z(${BwcYes(efYCj8q;u<#?_19UZrCz{yE7vPAZQ+A061~7Bll(ll(`ecpz6@JaGk( zH+$$mtk`cX#W&=;{XcleB&p>8uIB%{QXUt}pIaTkc<{0z_zw{IUzbu&T2Z*8MCC$g z+8^)AUnu2vrQz|-Zz@i<$P<6D_kSVDzhjv9lE?ID0V`G58M@@eM()i3g=ng5psl20@Lug=Lo zi*TRBo?jDsKZ!k`Wd47>W4@FXzjO_T3Wde*SRmmWFE*U_3W6E%T$DU>ak|gGkCDheoEGhZnsifA65P5&!Vk zFke*}y_&NAP3eu_mS}m+iVs|~Q7+u+ z(712CEDu*%zOv8*^y;!3JK|;e%0ee8TiRp=%kn)z*|J{ts;uD4I=?NEtPO>(2CIHY z-F{<(vSexlQfbzT-%+>U*dS|}+KG0#<4=E-*#Ejg1v1S|$N~31{ZV57>jn{J$`8X7 z-S0jq_q^92Pnq&erB&&}a?g7WT3&_3PptoKoc&(iJ`INtIKKZc42NiYx%&lMri_6x zldY)6L{-kHJm)dLk+hG)TUrcPxZG&|GHd{;7}brtB#>;GKnXA18MW*R%5Kdw0z8!r z${dLA<9OTB3fd8sQ5|#?xSU8UD5jRp5X4S+) zt(SZ79ji5hz9>o+=O=& z@{lX9h^eR|MO$mM@=~@OFPZ**i8)fX>A@&SNEkDZ@Q}m-u!c|l1%m7!Uus);Cadno z&ECuRi3244IPU_jyu8@9&$iWtyVWKsG$!RoN1Kb)6X(Gt4*kYLPB`_ln!S4}cX0pQ z^`bvvM>*;lb|{S_X2|bQEC020VJ(@J*)nrgK*-}i|6a-bXqcxK2)9L@9FZBK!uGY@ z4PE~?3F6AFnVvP~sX!xSbgpew>e#d>Sf2QN#?q=<>WfAHkKA!tb52ww-yyRlmG?63 zZvFL_YPmWGL8?dQ3<5+OABPQqoO6%3{7=!xk+0OUJ{M6+}Kzu=Ao!25=H{94?^F%j`TCL3c{15xxgIiX|!gNd~nT$&mZ%3W7W>q^#X> zJ2K~jxlhMYJL9MEeOWSRgBB{K2IR|}B*W`Lm8GzEGQTN}KaCuU%j~IGjamceV>-zpP$#!k~@*iIOaFOTBHVaq1&E_92%l9hyDgR&Z zvrqK-M4vx0vj4a9&svoH7J^oX5a*YP^Ns49=@EI^t?yQ>=yG-BBZKEQh0ua1l|23lq7;|79J$2PsR+Y`HcXeng9 zGP6E{A{Xe0WY#-aQ5oi!U=amT0ltT`iPrWqj|>-J=~(Lx9qX;Td-v635IQvtc^%!F z#Fo+OM#!{0#)6@HAFNF=4r?7=yz2JCf~@1qTi>xy_66rE&f7a^OB0eJn0zCF!0!!h zLCzL51i^;xfigld&@EPls&EI*rR6_`&1`&0$+U_lrI}O4s%PDyW zcfdLYF_Q|McuE<(2+{PIh*x=OLYsR{1NJR81~bFdjNI$SL(R{{SN!#J`)V{2y={pr z=aMDEVQ`hQCPPpVMr^P9KySNDCSyt$BSL@Zg(qwKiPzOKGeRY^eo*k@JfHWP78DUI z1rA2^3q9kd{3_`cR5~^Pvug|>lbCP_r(m^w|-#` zuucZNnEdAN1c2iu$EVAp-RRPswtaw{)MBYZP>dD~kvl;k@4t*hSYaO0G3;s*js&9w zDtNF#*H;gS2}d9UV)xP6bf8P+6-e9jnPQ&QFJ!JzgJ&)3_&!A2%ZTpsE=&(d4{Pr= z^lN`UUu2-?A`7~(E8?Akyo|i9wf!Esd;EMYGTXqwbK%(#sUs`vNccOZm%FufhLIPzLRUk>O0}YP zAU!d04}8Fx(bjw>(VS88A0)4k>4%2LGj^W3mu!s71fJiM@^NltjhUFQOi!EUEln$! zn49Ju6ZM){q+NO2oXfSn`r(@MZ>VuWxOc@R|G@SKPu{fTP}@;^H?@A8n+y(kZnc0&)`kZ?DG+i$` zQw}1xiiWuv;5+|rwD>;y08@6h-PMo)=)B&2^D{xbQQByxoNjb={lVxv^>jsQO_il8 zWR2!x6})fVIKQSb@jl3@eL(p7aEc;mTTw!Ru>bh>ouS4 z0X=o?JImCJUUiY^v<9{9K<9kPhMUiM+YD1e1~N0>PJPAL2|svF(jIN_joljhc=>Um z$s@)dL5*x*ix&2K9gx!OQ%tz#bM7YBgpSp{^!2_452v;n=U9vNE>LxQ9UcfYCtY@mT&F_y9LP03Qe$yO z|Ebao%eM|*xbT4Vhb|}GM#fxC75?Awq@ZcBg4_X0Y9s;@O?ON3lfWxos*5r8Ji?Xbpkg4My$8+jqNA+qB-8 zRD?a?#_s=C=zsSsaM$!1Lo)KdY~MoOHnvH(i*|SQqx0NH>Q)AzooaVJCAoXpgpYIbpelTd1v7Y3E++nfnuF?G zbyf5vk-i2srF(*e^Yj-cB$`tqZe-u=xNfMLa~>_vv6t&c za!=WfU=3KEc2Q%n9pC^wUKd=c8-rNgnmhDXrao~^d(mRi8fK^c&>H(0%kgzaR(tLf zrek7KNC*&iV>D8YS~ic_wzv?2d3W0x%rvEk8x<&r>q0RhMmQfHM~S7}gnXDVKgHZE zDYIv5q&3)!n4U1oeVJ9@ubY`(WbS{3N+K$=*tzi9;Fc<^(;Cr5w~!Y`RIKCbzx_g; zR?C0_7_~9ATQo5lM=tD&m+g7_pWoGRtkV~=y%(A5{(i|{z49@s{v}5P71RM>c-=Ei8-J@D`IZnv-AIo+&K9Zhbg8&*CFR z5Km0E)-J-L_|@>pnN~uG=IRJh(|qIW+Da1`ezt2BIF$Tdjv63%xbs$S^huuDfS|3b zUZw0vImckzc4fm*k6YME_oHomc=XFN%NizIty-(0c=PYjcrSskI;Io2X!D4b8M{kGad2=kGW#JLS1X+lto^hZNRUaXbduNxcQ zvt0egh-cN8VOJN_5I_Fc`Xf!un4(A!9c}CF$sWGO!)oU4=*ojEO7KfVOT--K%`oBE4hJ?5MwhB(o+?T z13Xf~M0`0|&1;K22Rb#>KU>(OFs}$JfBj}~J3p_~b*%g1#HT|RkCvXM zp?xr{WOA7h%Ts~4^NQ7mBpfK2j7RjLp`4`^E^WbZ{FZo-bW8Df*r|pfD#oNYWfT9% z8|!cE&=a>Wer?yBW^v)^=Aa*+@4Do8xZw9k26DusI44PAdU`6MEz&Dg;-w|4kyw|f=xe%s}DXLrS(ZY3~UX^wSz*cw>o8uW9 zl{YcXwHa(SB?f*M)pQ4;=rMiV+}gD7buc$PFngO5q97&?fDrB75Xo!ma?kN-P3f}v z@r`w*<(7|RyPZia14(;Zmp#>*=m{IEC#3M6{fI1RuvAgXXlu){X^)EFo_8r1_01~E zM?L7#3(^*r_NT)L>(Wx&&w1axE6I6#03K3oT&;(TG0@d*bl0Tf#PK67H|V)RJmO0? z`q?3ysRf&v1;e{NdxyrvbvO8&^0a=z+R^ojC(^r~dnlD37QX2!rdrjNfAw|$>uklF z3T=Kkc+a=t_1~IatmRbaspKL4&L|kU9Y_}u}Yk31Hp-5 zUcwJV6BD$tWp$#We?weZQ{|m<7}>OoG1-~6Kl!xtt(j7BC(C1>V(nMg_gDAjeE??q zM{cZeTKm=6;;vEI#@amVQbYX3hx?1uY%O;`@BUGC#{CD2Ebk838}dz&q1{J@HB>L_ zv3&K40Dlw|@vHKW42I#iyp7ap16}^ZVln=?Sh7qM>a=U9Ir%XmAhKeomW8@<(hKIf zPhIpM;%xqFUF0jMonzzp6H}^UBh;p8-W3ob%AZR_4O1_lX@6R6BgM_6`hy525yR`G z*ZQtdOxc@tb>-6L8mDef_lB^w%PplpQVYukScRv!mEYhn0d#aca2O}|8peyoRXPvoBRZ$o;VPQP_m20opM(o%!4Ep$=p24C4C4B^P``9 z=Y$xFy3Va-XhZHgQP?0&%xU?i|_#JY9-$QY+xL z$R=8%+Sv(QaSL!!!(MK&(MmNUR^qjv*Yn)GkT9p_CiZwOuki(3Cs8e(Cx6`!17fOt zrnKHQo4dQgm3 z!$L>6>t>InnoUjnPkyN#H#Z+FS{0L1C4M#Seaq24k?Q&;QzdYEFM89&9OFou<2p1| zzxFub#asqcp?RUSbY|qF;tz+bp!D1_*0Cd}<@xZ|?woTt)ebz;%sb+N|}xl!{tI9wf|4g8tbs_RH3(KVVwSGc8@H1xab?>6x{= z>t(k4xSln-Fc>|R=Lfy%cgN1Su96H1W8K1(uXjqzP2LdagK8_LRv*H{j=H&pLj8nu zgDv$dO5GJykDaC$VAesj2eBsRHN}KkMvC#e@+TIWPpcc`6y>q5ey&VxE-P*OMg(>; z3=?vdSz0aYOdWT6DhSwDEACSk7eDk3GV{XNT_?SeqUK-^y|blu}lRLtG47c1>lKOCbujpQ#x52EX#Duzs@& zV*fB}rcACmjJ49Zqg!(BP41JoOyutYZsSbZ;OmrRj5{vq|L+uA5~n zZZLPG`VykZK~yu?PPpU|g*(fSMo3J%fdu*%1v$LpcZ8u+RNR$EZUZY^it{#qrL8O9 zsdFdw>vGG-Zrf3h;?HNMn8_(FJbCJrv0hQ@jZs*#QIFD7l7jXXkH>m42Z0;;iDzBj z<*82XGS*Pe$$ZL+q?m$3m@D_~OTgB+DH`XSXt&+;JfNLuco0=~*QDHM)R_8c_uzM1 zLRL8{y==W^aRyjE{e*La8PdG=F(y}4>{fT=C}eJ}<9Ss;RYFf_c8Zc{H(@5N%G7cy z3ax?XI`;vF;q%=R)%-S%?x=#AVEH!7gC;50G7-E0czM#d<{0oD#J%t|ZcO|^e%Hz@ zO-nr{cSrW)EUT#>13eQg&sq)$lRL8eJ%eAQdex>%zTX-C62@zTJFo68_5V_3Q=F<% zWR^+RF4gRcF}dm4JoY#I@*lFdw{=^9<2$x7R3IVlDXFBSrih$_MF8vR`HLI8&YN=9 z$f=E`f5X1jOQ<_!v!8O7qBx+w&5EM<&}%2uUi9iPWgYjrn%%Y63t?RAZjk?$_tWkO;%-P?(ZD5n3 zNd&x%7Zu_9@)gVHZt@iSIOLGXLhEAVsw1n?%<&EOhbq=USZCSM$P-1k=hB~iCHQ&^ zl3PL)YH)}3cZ#t02-0h#2j*(CMh1<{guR%}7Ld2@Kaz$px4vRnSzxP$Ai50EoBDA~ zvOimU$^N_NH1H;Yst4r<2-^j8haC9JDKJ~J<;_RB(Nk77!oHLFeCa~#$xNgK^L}|lQhJBhtH-fY6rXfN3VvIt3HHJV80Xji*6qqckhkB2Ld&0(JQkDOEng@^f#Z--7t z3`)CAxS_#+ug6W?FY|(sSlHe4clzL<)!!ha-PpX`J(44@7hdv(hc`o8(U)H6lsSo~ ze087MnPz5S)c}0tGB|4ym*16qCDYVb^0;uV9TIWKf46r;(U-gom#uxz^YQxwP&0RK z$-Dub5JKe=kur3HBl!|hLi~+yC=NrMcm|GX)PW7c+O|Ea^C}TBs;{%7-t(o z;6XZ22a951wuLo>a4eP0H^EIwW(q*P7e_uT3N!={|Mu(ciQw--H?mY{7vli_EA0%j zKiu_opy6@A4?Ovh!X}ktE09?oAMSe1K;@Cj8%T`dr7`)e?=D$SeYmSXCTjjURr2<} zB2L7^iLX}-6xqR`VhJrXu+}7L1k+CI@Zz-GoFOH| zW}-a_Zbd`Bc2oGa=f~m&QwjbARct@R`bO|R&eOrljwaJDxNbZP{Mh8N@THbE@M z(j+)i+Fqo#!HP|Avqt4HM$1aSp7ANtO%LCXZt>DR$jUa+$Q0Mv6d{g`jE|65XPGk% zV6`0OER`#`eL?mJ(=f>#4V=N{vvV~VV~fobiS&G<>ZnTRB@$$cuJQ7vhWS*%=&|rX zOw>|S464NKCK!hMQSG{DXUm7{|9|GD6E-WE`0JuRPVab?zj(2}Ia-|`$!;d0Q1Bjm zV8T2<8Yv4N?JansY#c_fFXP-hJ$4dbfWclt_%0647ujx`c@}~wA-wUg#MUsUQjn`z;bcXUq=&Ilv^_VrJ&1^a>;ZDsni>OU@_MlIc~3wrlPU)AOQdCF!d9ypBRaaPnP zD6JE7mIRsubJL6S6S;KWQSpYuUGN?dCPF~#x+yNKI~go^k=5{Kbg3kBnYzjWeDxHnr$Y!prUFUlQegxHe;UnFGF;P;q37^GNZ%pk) za%fDi5MC>t$EuUcAxZVAy|_C5guOjdfng9RdWuAuh6^4+V=RQ=NA3y~Xdq`b-|nk) z4{FxJ__tW-#J6Sz3;dL=C5VdVJ!m)^NoctutP=bv+H_8MjNVdxZXCLi@0VII8sSO~ zSQzI=>wgx^oulb@Anwn%w{I60ZwkjxI`5kRTrEM=iN^(+#YkRxA5r7$O1)`|MLO~3 zP{}ubrfi=e=pN@@;KtfI^e#}nkF|d9)I#O`$mk@O%1CEQP`^srr19`%NdL(JGxlkA z%>mL!xN#JFsiNXulS)T%X#?;IO5P5qNlk&8n?)RPgjd(fL9MZ?gkdD}z)(j|JgWjU zPHFdzh-n-0EzBynLjr0ktDXBM8Q1jC)$T*_7gp4!E21$pWi*4UIiGhzw z$h?DS)-gLLK$DSED=z}}*lMsB6z?YuBT3o|9jFO#F6gqxla>PM15joPtjo}&&?8Vd zDY)2ivt92YnyMA`VheN#>Ml2$MBrBNnrFkMVf*hf5bYgLaYHTkGX`|b%e$+bBQkqq znbV|9a;vAbqvve_&!*D(LIr3wiz-jCrkmdj zsLwmCh0uM4hi+7clR3>*&hQr#nG~V*X}m6fsxd&tM$MGL7Hsvj7uA84hhSPzdsYSD z8c)37%by~%VpI24k8>}nUIuPt3C_D#I(wjf1^m!F1xtR7@(-!2<`>`(wCA^m^_xDJ zh3EgF;ajM&0{rceihK(37K2y^&FOV}el){8<0IW#Xa2-^#Ml(r=vXxV*1$gNOHG5o zj?D#cEaJ0*8uyhxRG+#MVDSeZEi8*VY2=2qjUOTNpKDu}CEqDO$vw2^gVy|gQfL>f zDvz!Hk>g$0!&$5i#n0H&VES>z?X4gySZ%@7jp%b5L)Qp?G zQ`_Djb+&L7w;+nE%DR4)M(-i9jdt)O55>eO&UbQ`MMQ;mnm)Q`5; zcbj)qk0xDiP6#SFw%aanJWJF1k3OOVnF*6bJ7(PjyTfXXy%!r=Y|}mPD71k8IQzo5 z`3rPn>#=Lo0jI3b642)Hq1~Ot`-2qIhpWybb1H#ri(-^#Qp*skg|Xf5=v;{RHc^rE zb^ybOq4_|KD9*9O5w9^~GW4AQ<}+(&XGh2{L-`w;Up_#)MM7%~sLB;UZ}#LQZToiq zW;}xW7Bv*^%5IvZd1r!i-fAmh1*CS8%;)-q;s&*11Zbd8NP=2zeEE>r&Tc18fY)eE zJ!RO4I*gH{?rtA+)Jk6qGO2<=gmW?0M!{M-0vy1$l3JuEZN6sEIjO2R{+wWDQsl~T z&I{rjL4&`>Rk3JDygA+;0gK!vJHwechD0d?q2jKDs)2%zY&Dcyq+rP>H%J2`EFKF1 z%$ydMjS+fns*%?6n zIXP`P&Gl3DnG5$5$S9{Sl<&JH8~s!~NDapxF0wzl9SxA?9*qz#%`VX}+yAt>zqiMW zk(!B)haX+IztPt*>Ts;^P+6R{zB`^U&c4C!=pfrYQ)`Ndhzx;7hKqXUC!)*RYcpHr zWJ@WN#0`dqQTq{eIdi=!=u<5`b^QD#LA4!gg)N6im<; z?gK;duflC|!=1Tr%DarZ>$J{Re1sdu1;zwZrS1bD-9ZUh#IlZuQN3cyh^~C!?*i*& zr%jJLKiy;y`WA5R0=?dIs-|t?X;;*F}N1e3!Y@RvUKRGkMo&3WVl8GaA zX8zt{b~D7u9yg$q;Av5as%(w>{MubN0^Ze#PBSIAj z<-WL+1)_X$o?ve?@it`t6kh*Sl-4@q)ZN96d*${!jMZ;n;9SV7+~N?D>yu+#C79(Z zYP4vdGV(d=1m>ql8XG4C2z|<2JoezdQ7KA;8`N_*w{A1b1Lef=wYDp;1W-trVev{@>{LnF1Ve^{M>U6!a8#3zSj zA_+^L>dbt#_VPsVAGj{E+Gd7FM@MCb&RN=%C~TC~4}w(DpuCwdl^!K_d54>Y`N`#q zTSK&kY7FYaQmgUzPnKhFoQG8M%v3TrbCrr%Y8`yJs#X2~;Kl5#M@NpYC#L^Sd zTMAi<5!7S36SNeS%b{2MZ@J0+6rjom+WftGdQC(2!K#V(<%5 zU9=d2fgPJB$x$ zSWFq&1pN((oUG+M-I~z{Sp;Yh${x_1yKO;@D-BxKIlsUnawdO!*ZoRvzrZ9JIr0)3ImU1f2rOocB0d^Jy;m#W zaF%Rk^S_&L1IwfEGS$Y0{Q-yF`&3$1-50S63i!Ws5Wkn(FOB@l)1)VR@mSmD>9T6N`x($V2aZ;(|#*#f&T&;*C4aKe3?lBd_ z==hW&AoJdIhJS@5{IJi!tbVVl^q{9V1t{cB-S+Rhi}H;Ul98knQzguMyyT-oG941Q z!(e()5s~CjDt#v~x93HHDjfA$jkL^_3`*})5$#Cr+Y=(|s;dXH)L@g(2d_Z6 zOElxdr^%B74>osF5XtTt*`BE%Zv%(Nqp+pb5v*a_b~s$Nw1ToD)!A`@veSVhgXd-n zWX6{Mthbl00A-Iju5`QxoYJ55xpXL?9!WxSAcLmYKic&B-WR~i2EZI=)9rDk)q=RO z-1hBo6a^A2W=;w)s|;58=J=JsfYzHp#)c8Wg6W0%u3U8k+j0UDWfB$<7IJWE-E;C( zKGZk?9;G=(EDl^aZM`3n{7uImTupfGKAp%K2h%9~CVL!FqeH(yP9tXJjBnsq16d|j z0@{4@Ez9QW$@X^st*k`(UWIs7W#56`#CG-=di>a8rE;qL4cRfm!x0C~i>Ks3K0R_% z_?3XFK_jvez$6jrWsXj)n2rhjPVjn?b~exxgo4L6qD{y}aJW3eG-?l2L#|-umor=21C*93PU}>2^x0;Uhw4K!le0^f3pW^n z!i(L#3f5$Ir%Sc@Do^dV&#>(GwJdp5EModm6CKZcc3&c_$9?=5v$CXPoVM_+#y3Fa+9)z%gd$f-ydrX_o)VVC}7C^%_QMig8 zd3h6Vl~2cgH$WP8I)Pj!R*Cc7rZjb;?68fMzc$0eGmD|-|wFJvHhqgRoZ_UG#I z>ymQ&%FJsfLy!5Ilb9f5hV9;mJ1)*JLxeR!J`p6GD&r=3#BQ+WVT$M((A{qS9 zW3wcC1(XTZtbHebkQjO_IhT2cK0|*fEKo_A@O|T+$!0)z+kc%mc`gvJt@ZIGY$e9n zpsEhGOH$TO(huFB5qJS~7p+eh+UT?V(7ZhA=gh8zEPE7oZte<~KR!QE-(R13(A6lS zvi2a46i|yRp%C?ftotL9E~o)gtq88IpXys5p<&wRSLbI3*s1?!zIi@9>ygdT)IbX+ ztF}e96*DAa|J0nmV4q`_AmD&{_=^^2|G}OfmeKj!odg=A4W(9qPsP(?2nrocl$s00 z)ZmJ#5jo4^;j_3$i0*yh&d|ticlev2{msVLI^{d`7ySGo_xVfHBQxSn*IF{$6?zoz z1WOmOM$qvHw0A-d!>a&osHsaND;Nr#$(*iFQ8+GP`GAZD%6bAyL!ULg;fZP!kX~h|qyA-2(5KnU{T+h_Qld_d@(&Ge{3ZB0bARoi2G2e#A!A zDUOJsESyft8``$57L{9kJV$*2e4d}A>Bloi;gO65UWEYRFR?lO(N=_uyM`$U>ascQ z1k_Y49{E^{s!zhuZxM!s9a-{PT3Sqo@NJ0}jo@PQ4mUm(dT0Q5g-7Q-k+?~N09zTr z5=g()Lz;pIGQaq0ITkKDCFGfl=VNT-gpxPF$@la-!_k;k5r#`|6epxwI58EeHtP*6 z@%ta`&vJmvcok^aht_;gcSLOk9HIyNCrEhDFw{8z09hdI5v-ZOxH|XNg)4RWOW95m z<4`I7rL(DsaeW`=)S6H^-!%3PH;xz6t%5eGDVj3&r+~C5vz}v?^a{T z_g1Z99r^=+vHzX$uougV%Tdh9?7{#!>sR{=fG_KnfZX%Wqs6%I`A`Fy+5Eh{bi-=@&a-P1@P|qw*Lgt_-_@-GFa9p z)Z;%OCO@(DKMaXaZ2dkE_|t6t4>SFz+4>*m`%km=`v%F6h*AGETmQpsjnyrPzJLEd zL}>C7d0#L;5U0w?T$T7FYS3-*8C5y$E6$i!uDN*fRo*i-u#z!NUvAGSaaHT|d7Qza zeKfqY8C=kQ>&$smrcT5=ki6xOf`HVn+^C?d7v8~ZWB*`Q^S@@(9d(qG(En3;{+4ue zgQP;euYcW#so@7}Y&Wsq+$3mNYIOo>AD~Sb{&1Tz{&|bXry=kUL*SFZ@=0L%)#d2_ zPJyL!W>}v!>XOs`{>Sy7Hvp|b#zv({d|id7V-#a%sG*gh>~0uHO&`!J;02Tgot3oC z!fJWIn^Gs<*~zmoE*8s36kXybgpzqWa8YV);7m#~6`Mo$9-Q**H<)5Du}s$WmpPwF zL^i6lS5Prk-VcQi&DP^F%E`KV*V~~02G=s~auC(J*4S3PXAn*FBh*EqOzj8QyXOOR zVPtjGVX zNtwxe=4+1D5jhN1*jT_ZKYVVW(SnAkHQ!3D+HS_RDm*#2A_5&_`5B-1184B6rSTFrB4~<*$F<6QomM zOsdP2?DOte+JkZ=G-q)yvi$@FMFwM;bza6UyBR&%eS1*Rlm66c9$vE5MNAvXEo>Keoq(R5;PMt255?-fp+9t++(XGH@!sq7vt|5nBA* zq6}rB`;co*PoPfD+InVOH6GA!C&kylsG!%F`>Lq#?8q=|+J69C)R^>mCN>ma?OTEk zmYABBvRD{=tL7AM$-%z^IZ<99um~rzTvj*J$B%$x(Wo!S430u)>193&Wr$Op>Ki~# zwQf#X@mWMK5jCkw7@n_VS4Znybau`pDG-#JX*%6o9pDSGxtt|0?``3s-s|a=T^Ci5 zoz?Y6@oAnWyFHjk&guybOZ*o{Bm6l65c75LZuM)&@%!kq#5EP+92%x?;bZ# z+T9JQ=vvz9O^P^%&wvgY`QEgstUl369#O7YoatY*=VB)%fL3qjSrcz0VLnq5(3Amz zB-$5GO~zxFIdHfQgK0+gK!E1FEO(Ibi049^GQk+i9q65}Bc<2-C7YbZ+ZW4COft@q z>!RXKLgX_Fb9H>%Vh11hMUL%rurHL)-tHU$*dJm8x!di>aXnY`QmYmF{Ai;|W}!02 z(>S}ULgD59lWM(g&L@@LW%eF3T!Z2lxfh*qZ$qjJ4d-XP=iIs2HPZTpZFO~PaPRud z1Q}qWDX#*&I5d8VUY`yjyJRBMZKdtK+~-YrW`$>6O(OZk5W6Oj9byIy*J`3H+LPup za#3fCUBO~<@ALVAJbAhwTVP;ToI=`3Ra*TA+(?j(o_zKt+2Q6dqz}n)4SkQf0>XpSiz{h za#C&FY@4Q9-q8XF?xmg*n9dcJIPn^XR@FO2iO2U3lB=t8L4M5X?OE06XkA{YESQ-I z5Ug=$GGye=mIWte)R(mka4ZP4+!4#~`%hBjJAPME8yh(_e%md1QSU^sKWLU%M-F!$ z!-|3A4H@f3J^s!`6uP#cP23+`; z_9nEEBoO|{LmD`wHi%and2v*oqArK)AT+Ap-mdFGRUt~V|l*MI>omcv>lGQ4w}>y8M~ zc&N=!wxuAdE&BgFG{tkr_?7WHGXxP+GX4KzeM*Re_`~ZaztoFrXa0Cx>` zq`?}y`CrPwY5;aZF=nAJC!AEnY#=d)<^Z)o(2x?<7arVVtjFUQA+c6NI13h$63ebE z;C0}xhp!ftIM25-IJ3RhL#1TE3@cGwL$M74nmh8Mv|1%4lTo}-N7bD%3%vra1`oH8 z=o`$&!Dix(qJ(9Y)XGxS_Q$rBXuA!pX(r)z3VL@R7j4AwlgZY$VOPfF4eH)YsG&tEC zZu4Rnj*@9#g|h3OCUQJ`F5l|%-H`8DfRbxm81SGehciN}@Wl}Yz)Sr$9vQ$9e+XsH zYVS0kgQD~YYDk*f(tOclHBHHVdU479KjtGlv0bKhjtV_hYcQ|Rhr=%swXC|=)3=vp z^cXTu>RDaL_p-t7Kza5q zz1=SizaGqD-h}&vh3zdfeJLpAc!ezexZWO#(|l1I_o$({s4cc1Flp>@h{L_ALOJ~U z#Qnb4ZMBE{nq1nUQ2W}l*c)8g7aMFdcq47}Gf& z&mfEIV@NJk@M>H0?QjK*PAzyF8D|LYq5pCCfx6ga0I6KF&!3>H!m zaVo}_8(&#Gu6N$Wh~gkMN7e?;QeQ`1%&$c4Mm5hJzs9@K2V7&1JX(j;_Qf6SIamY< z;cDL>*$n-{yWu3X+N+@g`$NSw*uE4_i@#83N!Pch?t4G`!SySNiuqER6F4zvJeP6D zGjdwxI=J;&j7cTw=oNE@x3H7^m=@da!1JGGyw&|)Bb{QFYrly>Pt(t@G3S-rZe~=> zZ`J!AcmmmEE>pt@f}0}99s{OV&>%e^CKu+o4__9%8||D+<;8m4fHTkQI~)7nj7Y|o zO*pVFCxk>$p550yF$hFtp9bY{&;P)~k-@$AMsL-;*@=9$%yz1eBvgyvd z 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 diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua b/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua deleted file mode 100644 index f19f52154..000000000 --- a/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua +++ /dev/null @@ -1,439 +0,0 @@ -require "util" ---local intro_screen = require("intro_screen") -require "story" - -global.paths = global.paths or {} - -local think = function(thought) - game.players[1].print({ "", "[img=entity/character][color=orange]", { "engineer-title" }, ": [/color]", { "think-" .. thought } }) -end - -function check_for_player_death(event) - if event.name == defines.events.on_player_died then - game.set_game_state({ game_finished = true, player_won = false, can_continue = false }) - end -end - -local function on_configuration_changed() - game.reload_script() -end - -local on_player_created = function(event) - local player = game.players[1] - - player.force.enable_research() - game.forces.player.disable_all_prototypes() - game.forces.player.reset_technology_effects() - -- game.forces.player.reset() - game.forces.player.research_queue_enabled = true - game.map_settings.enemy_expansion.enabled = false - game.forces.enemy.evolution_factor = 0 - game.map_settings.enemy_evolution.enabled = false - --game.disable_tips_and_tricks() - -- game.players[1].force.disable_all_prototypes() - global.storage_tank = game.get_entity_by_tag("storage-tank") - local entities = - { - global.storage_tank - } - for index, entity in pairs(entities) do - entity.minable = false - entity.destructible = false - end - global.intro_entities = entities - - - global.car1 = game.get_entity_by_tag("car1") - local entities2 = - { - global.car1 - } - for index, entity in pairs(entities2) do - entity.minable = false - end - global.intro_entities2 = entities2 - - - local recipe_list = player.force.recipes - -- game.players[1].clear_recipe_notifications() - recipe_list["chemical-plant"].enabled = false - - local technology_list = game.forces.player.technologies - technology_list["automation"].researched = true - technology_list["stone-wall"].researched = true - - technology_list["optics"].enabled = true - technology_list["gun-turret"].researched = true - technology_list["logistics"].enabled = true - technology_list["military"].enabled = true - technology_list["logistic-science-pack"].researched = true - technology_list["steel-processing"].enabled = true - technology_list["electronics"].researched = true - technology_list["fast-inserter"].enabled = true - technology_list["automation-2"].enabled = true - technology_list["electric-energy-distribution-1"].enabled = true - technology_list["engine"].enabled = true - technology_list["fluid-handling"].enabled = true - technology_list["oil-processing"].researched = true - technology_list["logistics"].enabled = true - technology_list["heavy-armor"].enabled = true - technology_list["steel-axe"].enabled = true - technology_list["military-2"].enabled = true - technology_list["chemical-science-pack"].enabled = true - technology_list["military-science-pack"].enabled = true - technology_list["logistics-2"].enabled = true - technology_list["advanced-material-processing"].enabled = true - technology_list["solar-energy"].enabled = true - technology_list["laser"].enabled = false - technology_list["flamethrower"].enabled = true - technology_list["flammables"].researched = true - technology_list["modules"].enabled = false - technology_list["speed-module"].enabled = false - technology_list["effectivity-module"].enabled = false - technology_list["productivity-module"].enabled = false - technology_list["sulfur-processing"].researched = true - technology_list["toolbelt"].enabled = true - technology_list["advanced-electronics"].researched = true - technology_list["explosives"].enabled = true - technology_list["land-mine"].enabled = true - technology_list["electric-energy-accumulators"].enabled = true - technology_list["laser-turret"].enabled = false - technology_list["tank"].enabled = false - technology_list["circuit-network"].enabled = true - technology_list["night-vision-equipment"].enabled = false - technology_list["solar-panel-equipment"].enabled = false - technology_list["energy-shield-equipment"].enabled = false - technology_list["battery-equipment"].enabled = false - technology_list["modular-armor"].enabled = false - technology_list["military-3"].enabled = true - technology_list["railway"].enabled = true - technology_list["automated-rail-transportation"].enabled = false - technology_list["rail-signals"].enabled = true - technology_list["gate"].enabled = true - technology_list["rocketry"].enabled = false - technology_list["battery"].researched = true - technology_list["plastics"].researched = true - technology_list["fluid-wagon"].enabled = true - technology_list["stack-inserter"].enabled = true - - - --blocked tech - technology_list["automation-3"].enabled = false - technology_list["automobilism"].enabled = false -- only from start - technology_list["explosive-rocketry"].enabled = false - technology_list["braking-force-2"].enabled = false - technology_list["cliff-explosives"].enabled = false - technology_list["construction-robotics"].enabled = false - technology_list["energy-weapons-damage-3"].enabled = false - technology_list["exoskeleton-equipment"].enabled = false - technology_list["landfill"].enabled = false - technology_list["utility-science-pack"].enabled = false - technology_list["production-science-pack"].enabled = false - technology_list["advanced-material-processing-2"].enabled = false - technology_list["personal-roboport-equipment"].enabled = false - technology_list["personal-laser-defense-equipment"].enabled = false - technology_list["nuclear-fuel-reprocessing"].enabled = false - technology_list["nuclear-power"].enabled = false - technology_list["military-4"].enabled = false - technology_list["destroyer"].enabled = false - technology_list["defender"].enabled = false - technology_list["discharge-defense-equipment"].enabled = false - technology_list["distractor"].enabled = false - technology_list["artillery"].enabled = false - technology_list["atomic-bomb"].enabled = false - technology_list["robotics"].enabled = false - technology_list["automation-3"].enabled = false - technology_list["rocket-control-unit"].enabled = false - technology_list["logistic-system"].enabled = false - technology_list["logistic-robotics"].enabled = false - technology_list["electric-engine"].enabled = false - technology_list["uranium-processing"].enabled = false - technology_list["spidertron"].enabled = false - technology_list["kovarex-enrichment-process"].enabled = false - technology_list["lubricant"].enabled = false - technology_list["low-density-structure"].enabled = false - technology_list["rocket-fuel"].enabled = false - - - - --blocked tech upgrades - technology_list["braking-force-1"].enabled = true - technology_list["energy-weapons-damage-1"].enabled = false - technology_list["energy-weapons-damage-2"].enabled = false - technology_list["energy-weapons-damage-3"].enabled = false - technology_list["inserter-capacity-bonus-1"].enabled = true - technology_list["inserter-capacity-bonus-2"].enabled = true - technology_list["inserter-capacity-bonus-3"].enabled = true - technology_list["laser-shooting-speed-1"].enabled = false - technology_list["laser-shooting-speed-2"].enabled = false - technology_list["laser-shooting-speed-3"].enabled = false - technology_list["stronger-explosives-1"].enabled = true - technology_list["stronger-explosives-2"].enabled = false - technology_list["stronger-explosives-3"].enabled = false - technology_list["physical-projectile-damage-1"].enabled = true - technology_list["physical-projectile-damage-2"].enabled = true - technology_list["physical-projectile-damage-3"].enabled = true - technology_list["physical-projectile-damage-4"].enabled = true - technology_list["physical-projectile-damage-5"].enabled = false - technology_list["refined-flammables-1"].enabled = true - technology_list["refined-flammables-2"].enabled = true - technology_list["refined-flammables-3"].enabled = false - technology_list["research-speed-1"].enabled = true - technology_list["research-speed-2"].enabled = true - technology_list["research-speed-3"].enabled = true - technology_list["research-speed-4"].enabled = false - technology_list["mining-productivity-1"].enabled = true - technology_list["mining-productivity-2"].enabled = false - technology_list["weapon-shooting-speed-1"].enabled = true - technology_list["weapon-shooting-speed-2"].enabled = true - technology_list["weapon-shooting-speed-3"].enabled = true - technology_list["weapon-shooting-speed-4"].enabled = false - technology_list["worker-robots-speed-1"].enabled = false - technology_list["worker-robots-speed-2"].enabled = false - technology_list["worker-robots-storage-1"].enabled = false - technology_list["worker-robots-storage-2"].enabled = false - technology_list["artillery-shell-range-1"].enabled = false - technology_list["artillery-shell-speed-1"].enabled = false - technology_list["follower-robot-count-1"].enabled = false - technology_list["follower-robot-count-2"].enabled = false -end - - -script.on_event(defines.events.on_script_path_request_finished, function(event) - local request_data = global.path_requests[event.id] - if not request_data then - log("No request data found for ID: " .. event.id) - return - end - - local player = game.get_player(request_data) - if not player then - log("Player not found for request ID: " .. event.id) - return - end - - if event.path then - -- Path found successfully - player.print("Path found with " .. #event.path .. " waypoints") - global.paths[event.id] = event.path - log("Path found for request ID: " .. event.id) - elseif event.try_again_later then - player.print("Pathfinder is busy, try again later") - global.paths[event.id] = "busy" - log("Pathfinder busy for request ID: " .. event.id) - else - player.print("Path not found" .. serpent.block(event)) - global.paths[event.id] = "not_found" - log("Path not found for request ID: " .. event.id) - end -end) - - -local story_table = -{ - { - { - init = function(event, story) - game.players[1].teleport({ 0, 0 }) - game.print("Welcome players") - set_goal({ "Enjoy the scenery for 5 seconds" }) - end, - condition = story_elapsed_check(5) - }, - { - action = function() - local character = game.players[1] - character.insert { name = "iron-gear-wheel", count = 47 } - character.insert { name = "pistol", count = 1 } - character.insert { name = "firearm-magazine", count = 1 } - character.insert { name = "electronic-circuit", count = 90 } - character.insert { name = "copper-cable", count = 38 } - character.insert { name = "iron-plate", count = 10 } - character.insert { name = "steam-engine", count = 1 } - set_info() - end - }, - { - init = function(event, story) - game.print("Collect 10 iron ore") - set_goal({ "goal-iron-ore" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("iron-ore") >= 10 - end, - action = function(event, story) - game.print("Well done") - set_goal({ "completed-iron-ore" }) - end - }, - { - init = function(event, story) - game.print("Build a stone furnace") - set_goal({ "goal-build-furnace" }) - end, - condition = function(event, story) - local has_furnace = game.players[1].get_item_count("stone-furnace") >= 1 - local furnace_on_ground = game.players[1].surface.count_entities_filtered { position = game.players[1].position, radius = 100, name = "stone-furnace" } >= - 1 - return has_furnace or furnace_on_ground - end, - action = function(event, story) - game.print("Great! Now place the furnace on the ground.") - set_goal({ "goal-place-furnace" }) - end - }, - { - init = function(event, story) - game.print("Smelt the iron ore in the furnace and collect 10 iron plates") - set_goal({ "goal-smelt-and-collect-10-iron-plates" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("iron-plate") >= 10 - end, - action = function(event, story) - game.print("Well done") - set_goal({ "completed-iron-plate" }) - end - }, - { - init = function(event, story) - game.print("Collect 10 copper ore") - set_goal({ "goal-copper-ore" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("copper-ore") >= 10 - end, - action = function(event, story) - game.print("Well done") - set_goal({ "completed-copper-ore" }) - end - }, - { - init = function(event, story) - game.print("Smelt the copper ore in the furnace and collect 10 copper plates") - set_goal({ "goal-smelt-and-collect-10-copper-plates" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("copper-plate") >= 10 - end, - action = function(event, story) - game.print("Well done") - set_goal({ "completed-smelt-and-collect-10-copper-plates" }) - end - }, - { - init = function(event, story) - game.print("Craft a burner mining drill") - set_goal({ "goal-craft-burner-mining-drill" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("burner-mining-drill") >= 1 - end, - action = function(event, story) - game.print("Great! Now place the burner mining drill on the iron ore.") - set_goal({ "completed-craft-burner-mining-drill" }) - end - }, - { - init = function(event, story) - game.print("Place the burner mining drill on the iron ore") - set_goal({ "goal-place-burner-mining-drill" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("burner-mining-drill") == 0 - end, - action = function(event, story) - game.print("Well done") - set_goal({ "completed-place-burner-mining-drill" }) - end - }, - { - init = function(event, story) - game.print("Build an assembling machine") - set_goal({ "goal-build-assembling-machine" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("assembling-machine-1") >= 1 - end, - action = function(event, story) - game.print("Great! Now place the assembling machine on the ground.") - set_goal({ "completed-place-assembling-machine" }) - end - }, - { - init = function(event, story) - game.print("Automate the production of iron gears") - set_goal({ "goal-automate-iron-gears" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("iron-gear-wheel") >= 100 - end, - action = function(event, story) - game.print("Well done") - set_goal({ "completed-iron-gears" }) - end - }, - { - init = function(event, story) - game.print("Build a transport belt") - set_goal({ "goal-build-transport-belt" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("transport-belt") >= 1 - end, - action = function(event, story) - game.print("Great! Now place the transport belt on the ground.") - set_goal({ "goal-place-transport-belt" }) - end - } - } -} -story_init_helpers(story_table) - - -local init = function() - global.story = story_init() - - game.map_settings.enemy_expansion.enabled = false - game.forces.enemy.evolution_factor = 0 - game.map_settings.enemy_evolution.enabled = false - --game.disable_tips_and_tricks() - -- game.players[1].force.disable_all_prototypes() -end - - -local story_events = -{ - defines.events.on_tick, - defines.events.on_entity_died, - defines.events.on_built_entity, - defines.events.on_player_mined_item, - defines.events.on_player_mined_entity, - defines.events.on_sector_scanned, - defines.events.on_entity_died, - defines.events.on_entity_damaged, - defines.events.on_player_died -} - -local function on_configuration_changed() - --game.reload_script() - init() -end - -script.on_event(story_events, function(event) - if #game.players > 0 and game.players[1].character then - check_for_player_death(event) - story_update(global.story, event) - end -end) - -script.on_event(defines.events.on_player_created, function(event) - on_player_created(event) -end) - -script.on_event(defines.events.on_player_crafted_item, check_automate_science_packs_advice) -script.on_event(defines.events.on_player_created, on_player_created) -script.on_event(defines.events.on_unit_group_finished_gathering, on_unit_group_finished_gathering) ---script.on_event(defines.events.on_gui_click, intro_screen.on_gui_click) -script.on_init(on_configuration_changed) -script.on_configuration_changed(on_configuration_changed) diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/control2.lua b/fle/cluster/factorio-server/scenarios/default_lab_scenario/control2.lua deleted file mode 100644 index 60adec886..000000000 --- a/fle/cluster/factorio-server/scenarios/default_lab_scenario/control2.lua +++ /dev/null @@ -1,6 +0,0 @@ -local handler = require("event_handler") -handler.add_lib(require("freeplay")) ---handler.add_lib(require("silo-script")) ---handler.add_lib(require("paperclips")) ---require("story") ---require "story" diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/description.json b/fle/cluster/factorio-server/scenarios/default_lab_scenario/description.json deleted file mode 100644 index d217d9b78..000000000 --- a/fle/cluster/factorio-server/scenarios/default_lab_scenario/description.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "order": "a", - "multiplayer-compatible": true, - "is-main-game": true -} diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/freeplay.lua b/fle/cluster/factorio-server/scenarios/default_lab_scenario/freeplay.lua deleted file mode 100644 index 5afa12dfe..000000000 --- a/fle/cluster/factorio-server/scenarios/default_lab_scenario/freeplay.lua +++ /dev/null @@ -1,299 +0,0 @@ -local util = require("util") -local crash_site = require("crash-site") -require('story') - -local created_items = function() - return - { - ["iron-plate"] = 8, - ["wood"] = 1, - ["pistol"] = 1, - ["firearm-magazine"] = 10, - ["burner-mining-drill"] = 1, - ["stone-furnace"] = 1 - } -end - -local respawn_items = function() - return - { - ["pistol"] = 1, - ["firearm-magazine"] = 10 - } -end - -local ship_items = function() - return - { - ["firearm-magazine"] = 8 - } -end - -local debris_items = function() - return - { - ["iron-plate"] = 8 - } -end - -local ship_parts = function() - return crash_site.default_ship_parts() -end - -local chart_starting_area = function() - local r = global.chart_distance or 200 - local force = game.forces.player - local surface = game.surfaces[1] - local origin = force.get_spawn_position(surface) - force.chart(surface, {{origin.x - r, origin.y - r}, {origin.x + r, origin.y + r}}) -end - - -local on_player_created = function(event) - local player = game.get_player(event.player_index) - util.insert_safe(player, global.created_items) - - if not global.init_ran then - - --This is so that other mods and scripts have a chance to do remote calls before we do things like charting the starting area, creating the crash site, etc. - global.init_ran = true - - chart_starting_area() - - if not global.disable_crashsite then - local surface = player.surface - surface.daytime = 0.7 - crash_site.create_crash_site(surface, {-5,-6}, util.copy(global.crashed_ship_items), util.copy(global.crashed_debris_items), util.copy(global.crashed_ship_parts)) - util.remove_safe(player, global.crashed_ship_items) - util.remove_safe(player, global.crashed_debris_items) - player.get_main_inventory().sort_and_merge() - if player.character then - player.character.destructible = false - end - global.crash_site_cutscene_active = true - crash_site.create_cutscene(player, {-5, -4}) - return - end - - end - - if not global.skip_intro then - if game.is_multiplayer() then - player.print(global.custom_intro_message or {"msg-intro"}) - else - game.show_message_dialog{text = global.custom_intro_message or {"msg-intro"}} - end - end - -end - -local on_player_respawned = function(event) - local player = game.get_player(event.player_index) - util.insert_safe(player, global.respawn_items) -end - -local on_tick = function(event) - story_update(global.story, event) - game.print("tick!") -end - -local on_cutscene_waypoint_reached = function(event) - if not global.crash_site_cutscene_active then return end - if not crash_site.is_crash_site_cutscene(event) then return end - - local player = game.get_player(event.player_index) - - player.exit_cutscene() - - if not global.skip_intro then - if game.is_multiplayer() then - player.print(global.custom_intro_message or {"msg-intro"}) - else - game.show_message_dialog{text = global.custom_intro_message or {"msg-intro"}} - end - end -end - -local skip_crash_site_cutscene = function(event) - if not global.crash_site_cutscene_active then return end - if event.player_index ~= 1 then return end - local player = game.get_player(event.player_index) - if player.controller_type == defines.controllers.cutscene then - player.exit_cutscene() - end -end - -local on_cutscene_cancelled = function(event) - if not global.crash_site_cutscene_active then return end - if event.player_index ~= 1 then return end - global.crash_site_cutscene_active = nil - local player = game.get_player(event.player_index) - if player.gui.screen.skip_cutscene_label then - player.gui.screen.skip_cutscene_label.destroy() - end - if player.character then - player.character.destructible = true - end - player.zoom = 1.5 -end - -local on_player_display_refresh = function(event) - crash_site.on_player_display_refresh(event) -end - -local freeplay_interface = -{ - get_created_items = function() - return global.created_items - end, - set_created_items = function(map) - global.created_items = map or error("Remote call parameter to freeplay set created items can't be nil.") - end, - get_respawn_items = function() - return global.respawn_items - end, - set_respawn_items = function(map) - global.respawn_items = map or error("Remote call parameter to freeplay set respawn items can't be nil.") - end, - set_skip_intro = function(bool) - global.skip_intro = bool - end, - get_skip_intro = function() - return global.skip_intro - end, - set_custom_intro_message = function(message) - global.custom_intro_message = message - end, - get_custom_intro_message = function() - return global.custom_intro_message - end, - set_chart_distance = function(value) - global.chart_distance = tonumber(value) or error("Remote call parameter to freeplay set chart distance must be a number") - end, - get_disable_crashsite = function() - return global.disable_crashsite - end, - set_disable_crashsite = function(bool) - global.disable_crashsite = bool - end, - get_init_ran = function() - return global.init_ran - end, - get_ship_items = function() - return global.crashed_ship_items - end, - set_ship_items = function(map) - global.crashed_ship_items = map or error("Remote call parameter to freeplay set created items can't be nil.") - end, - get_debris_items = function() - return global.crashed_debris_items - end, - set_debris_items = function(map) - global.crashed_debris_items = map or error("Remote call parameter to freeplay set respawn items can't be nil.") - end, - get_ship_parts = function() - return global.crashed_ship_parts - end, - set_ship_parts = function(parts) - global.crashed_ship_parts = parts or error("Remote call parameter to freeplay set ship parts can't be nil.") - end -} - -if not remote.interfaces["freeplay"] then - remote.add_interface("freeplay", freeplay_interface) -end - -local is_debug = function() - local surface = game.surfaces.nauvis - local map_gen_settings = surface.map_gen_settings - return map_gen_settings.width == 50 and map_gen_settings.height == 50 -end - -local freeplay = {} - -freeplay.events = -{ - [defines.events.on_player_created] = on_player_created, - [defines.events.on_player_respawned] = on_player_respawned, - [defines.events.on_cutscene_waypoint_reached] = on_cutscene_waypoint_reached, - ["crash-site-skip-cutscene"] = skip_crash_site_cutscene, - [defines.events.on_player_display_resolution_changed] = on_player_display_refresh, - [defines.events.on_player_display_scale_changed] = on_player_display_refresh, - [defines.events.on_cutscene_cancelled] = on_cutscene_cancelled, - [defines.events.on_tick] = on_tick -} - -freeplay.on_configuration_changed = function() - global.created_items = global.created_items or created_items() - global.respawn_items = global.respawn_items or respawn_items() - global.crashed_ship_items = global.crashed_ship_items or ship_items() - global.crashed_debris_items = global.crashed_debris_items or debris_items() - global.crashed_ship_parts = global.crashed_ship_parts or ship_parts() - - if not global.init_ran then - -- migrating old saves. - global.init_ran = #game.players > 0 - end -end - -global.story_table = -{ - { - { - init = function(event, story) - game.print("Welcome players") - set_goal({"Enjoy the scenery for 5 seconds"}) - end, - condition = story_elapsed_check(5) - }, - { - init = function(event, story) - game.print("Enough relaxation, time to automate!") - set_goal("Automate it!") - --game.players[1].set_goal_description("Automate!", true) - set_info{text = "Automating things is a lot of fun, trust me"} - for k, player in pairs (game.players) do - player.insert("iron-plate") - end - end, - update = function(event, story) - if event.name == defines.events.on_player_crafted_item then - game.players[event.player_index].print({"", "You crafted a ", game.item_prototypes[event.item_stack.name].localised_name}) - end - end, - condition = function(event) - return event.name == defines.events.on_built_entity - end, - action = function(event) - game.print({"", game.players[event.player_index].name, " built a ", event.created_entity.localised_name}) - end - }, - { - init = function() - set_goal("Thats it guys!") - set_info() - end, - condition = story_elapsed_check(5) - } - } -} -story_init_helpers(global.story_table) - -freeplay.on_init = function() - global.story = story_init() - global.created_items = created_items() - global.respawn_items = respawn_items() - global.crashed_ship_items = ship_items() - global.crashed_debris_items = debris_items() - global.crashed_ship_parts = ship_parts() - - if is_debug() then - global.skip_intro = true - global.disable_crashsite = true - end - -end - -story_init_helpers(story_table) - -return freeplay diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/info.json b/fle/cluster/factorio-server/scenarios/default_lab_scenario/info.json deleted file mode 100644 index 19765bd50..000000000 --- a/fle/cluster/factorio-server/scenarios/default_lab_scenario/info.json +++ /dev/null @@ -1 +0,0 @@ -null diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/freeplay.cfg b/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/freeplay.cfg deleted file mode 100644 index 5f466bfce..000000000 --- a/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/freeplay.cfg +++ /dev/null @@ -1,4 +0,0 @@ -msg-intro=This is the Factorio Freeplay. Your task is to launch a rocket into space. You will need to research advanced technologies in order to unlock the rocket silo. Start small, work your way up with automation, and don't forget to protect yourself from the natives. -scenario-name=Freeplay -description=Your task is to launch a rocket into space. Start from nothing, work your way up with automation, and don't forget to protect yourself from the natives.\n[font=default-bold]This is the intended way of playing Factorio.[/font] - diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg b/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg deleted file mode 100644 index 2aec677fb..000000000 --- a/fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg +++ /dev/null @@ -1,71 +0,0 @@ -description=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. \n\n[font=default-bold]Mission Parameters:[/font] \n- New Research: Tank\n- Enemies do not evolve or expand\n -engineer-title=Engineer -msg-intro=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. -scenario-name=Mission 01 - -# Goal Parameters Configuration File -# Maps goal parameters to human-readable names - -goal-iron-ore=Collect 10 iron ore -goal-build-furnace=Build a stone furnace -goal-place-furnace=Place the stone furnace on the ground -goal-smelt-iron=Place 10 iron ore in the furnace and start smelting -goal-smelt-and-collect-10-iron-plates=Smelt and collect 10 iron plates from the furnace -goal-smelt-and-collect-10-copper-plates=Smelt and collect 10 copper plates from the furnace -completed-smelt-and-collect-10-copper-plates=Successfully smelted copper ore into copper plates -goal-completed-iron-plate=Successfully smelted iron ore into iron plates -goal-expand-factory=Expand your factory -goal-setup-assembling-machine=Set up the assembling machine to produce iron gear wheels -goal-supply-iron-plates=Supply the assembling machine with iron plates - - -goal-base=- Go to the base\n- Get into a parked vehicle -goal-build-tank=- Build a tank -goal-construct-tank=- Place tank on the ground -goal-ressearch-tank=Tank is now available in the tech tree \n- Research tank -goal-ressearch-military=- Research military -goal-restore-oil=- Restore oil production \n- Repair pumpjacks: __1__ / __2__ -goal-connect-oil=- Repair and connect pipes to the base\n- Oil must flow through the pipeline \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Kill 3 small worms nearby the pipeline -goal-destroy-spawners=- Destroy __1__ / __2__ biter nests -goal-destroy-spawners2=- Destroy __1__ / __2__ biter nests \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Roadkill the nests by tank -goal-grenades=- Construct and keep at least __1__/__2__ grenades in your inventory -goal-chemical=Chemical plant is now available for the construction \n- Build chemical plants: __1__ / __2__ -goal-train-station=Automated rail transportation is now available in the tech tree \n- Research automated rail transportation \n- Build __1__ / __2__ train stops \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Secure supply line from abandoned bases in the west -msg-base-1=[color=orange]OUTER BASE:[/color] Anyone survived the crash? If you can hear me, our base is located [font=default-bold]NORTHEAST[/font] from your position. -msg-base-2=[color=orange]OUTER BASE:[/color] It was a blast! Those bugs can shoot the plasma really high! Too bad they just hit you. -msg-base-3=[color=orange]OUTER BASE:[/color] I can' t send people to you, our base is under attack! If you arrive unharmed, get in the [font=default-bold]VEHICLE[/font] parked inside! Then I'll send you further instructions. -msg-base-4=[color=orange]OUTER BASE:[/color] The bugs destroyed our pumpjacks and pipelines. We have no fuel supply for our flamethrower turrets. -msg-base-5=[color=orange]OUTER BASE:[/color] The oil facilities lie on [font=default-bold]NORTH[/font]. Can you take a look at this? -msg-base-6=[color=orange]OUTER BASE:[/color] In the [font=default-bold]VEHICLE IS THE EQUIPMENT[/font] for repairing the pumpjacks and pipes. -msg-base-7=[color=orange]OUTER BASE:[/color] I love the smell of running gasoline. Great job! -msg-base-8=[color=orange]OUTER BASE:[/color] I have good news and bad news. The good news is, I've assigned some robots to repair your ship. And the bad news... We need you to solve an insect problem. I think that's fair, right? -msg-base-9=[color=orange]OUTER BASE:[/color] If you want to survive in the wasteland, you should get some better equipment. Use our [font=default-bold]LABORATORIES[/font] to research weapons! \n\nIf I were you, I'd be all over it, because bugs never sleep! -msg-base-10=[color=orange]OUTER BASE:[/color] Well done! It's time to finally get rid of the bugs! As soon as you're ready, head out into the wasteland. -msg-base-11=[color=orange]OUTER BASE:[/color] [font=default-bold]DESTROY[/font] their [font=default-bold]NESTS[/font] around the base. We're not gonna let some insect terrorize us! -msg-base-12=[color=orange]OUTER BASE:[/color] Dead bug, good bug! Finally there will be some peace, but I don't want to jinx it. \n\nI would be quite happy if you could help us construct a box of [font=default-bold]GRENADES[/font]. It's in your interest too. \n\nDon't forget to research advance millitary first. -msg-base-12.1=[color=orange]OUTER BASE:[/color] Just don't throw those grenades under your feet. I've had a couple of individuals here who've... But. What a waste of words. -msg-base-12.2=[color=orange]OUTER BASE:[/color] At least you know how to make explosives. Believe me, bugs really don't like when things explode under their legs. Unfortunately, they're smarter than they look and flattened our [font=default-bold]CHEMICAL PLANT[/font] in the last attack. Will you be so kind and construct a new one? -msg-base-13=[color=orange]OUTER BASE:[/color] You're the one who crashed on this planet years ago, right? You have really bad luck for landing, ha ha. \n\nI think I can trust you. It's classified, but screw it. We're kind of researching new weapons here. At the moment, we're developing a multi-functional [font=default-bold]TANK[/font]. -msg-base-14=[color=orange]OUTER BASE:[/color] However, we are slowly running out of material here. The base is normally supplied by the train line from [font=default-bold]WESTERN STATIONS[/font]. But we lost contact a month ago. -msg-base-15=[color=orange]OUTER BASE:[/color] The bugs probably got them. Either way, it is essential to [font=default-bold]RESTORE SUPPLY LINE[/font] to the base. Make sure we have a link between us and the stations so we can continue with our research. -msg-base-16=[color=orange]OUTER BASE:[/color] Research done, now we need to [font=default-bold]CONSTRUCT THE TANK[/font]. Your automation is more efficient than ours. We'd be happy to have you take care of it. -msg-base-17=[color=orange]OUTER BASE:[/color] We need to do some tests and our good insect friends will be "happy" to help us. [font=default-bold]FILL THE TANK[/font] with the ammo of your choice and [font=default-bold]DESTROY[/font] other insect hatcheries. -msg-base-18=[color=orange]OUTER BASE:[/color] Well done! The test met our expectations. Thank you for your assistance! \n\nWe managed to fix your ship. It's not as good as new, but it'll fly to the nearest dock. Good luck, and if you accidentally fly by, I'll make sure you get to crash... I mean, land safely here. Ha, ha! -think-fall-ship=Ouch, that was a hard landing. What happened? -think-ship-members=Oh, no! My crew is dead... -think-find-base=Okay, those tracks should lead to the base, I should follow them. -think-assist=Great, now I'm a plumber... I'd better get that fuel supply going again, or it's gonna get pretty stuffy in here. -think-assist2=I should handle that vehicle with care. It would be pretty sucky if I had to run around on foot. -think-base=I should go back to base and automate it a little better. -think-base2=There are some coal and iron deposits on the northwestern tip that could be exploited. -think-weapon=Hmm, the planet was only recently discovered and already corporations are doing weapons research here? Suspicious... -think-tank=I have to admit, having a tracked armored monster by my side is quite reassuring. -think-car=With the vehicle, the disposal of nests will be easier. -think-grenade=I've never been a fan of grenades, but I don't have much choice. -think-done=Train stops are done, now we'll focus on researching the tank. - -[intro-screen] -header=Mission 01 - Return -continue=START MISSION -intro=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. \n\n[font=default-bold]Mission Objectives:[/font]\n- Find the Outer Base \n\n\n[font=default-bold]Mission Intel:[/font]\n- Follow the objectives to get new researches.\n- Scouting your surroundings can be worthwhile. -param=[font=default-bold]Mission Parameters:[/font]\n- New Research: Tank\n\nOn Difficulty Easy:\n- Enemies do not evolve or expand\n- Increased starting items & ammo\n\nOn Difficulty Normal:\n- Enemies do not evolve or expand\n- Decreased starting items & ammo\n\nOn Difficulty Hard:\n- Increased starting evolution factor\n- Enemies evolve and expand \ No newline at end of file diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/paperclips.lua b/fle/cluster/factorio-server/scenarios/default_lab_scenario/paperclips.lua deleted file mode 100644 index 99edfa4f4..000000000 --- a/fle/cluster/factorio-server/scenarios/default_lab_scenario/paperclips.lua +++ /dev/null @@ -1,81 +0,0 @@ -require("story") - -local paperclips = {} ---Init the story script -paperclips.on_init = function() - global.story = story_init(player(1)) -end - ---Register the update to all events -local on_event = function(event) - story_update(global.story, event) -end - ---Can also register to specific events, if you want to do multiple things with that event -local on_tick = function(event) - if global.story == nil then - global.story = story_init(player(1)) - end - story_update(global.story, event) - game.print("tick") - --Other things on tick can go here. -end - -paperclips.events = -{ - [defines.events.on_player_created] = on_event, - [defines.events.on_player_respawned] = on_event, - [defines.events.on_cutscene_waypoint_reached] = on_event, - ["crash-site-skip-cutscene"] = on_event, - [defines.events.on_player_display_resolution_changed] = on_event, - [defines.events.on_player_display_scale_changed] = on_event, - [defines.events.on_cutscene_cancelled] = on_event, - [defines.events.on_tick] = on_tick -} - - -story_table = -{ - { - { - init = function(event, story) - game.print("Welcome players") - set_goal({"Enjoy the scenery for 5 seconds"}) - end, - condition = story_elapsed_check(5) - }, - { - init = function(event, story) - game.print("Enough relaxation, time to automate!") - set_goal("Automate it!") - --game.players[1].set_goal_description("Automate!", true) - set_info{text = "Automating things is a lot of fun, trust me"} - for k, player in pairs (game.players) do - player.insert("iron-plate") - end - end, - update = function(event, story) - if event.name == defines.events.on_player_crafted_item then - game.players[event.player_index].print({"", "You crafted a ", game.item_prototypes[event.item_stack.name].localised_name}) - end - end, - condition = function(event) - return event.name == defines.events.on_built_entity - end, - action = function(event) - game.print({"", game.players[event.player_index].name, " built a ", event.created_entity.localised_name}) - end - }, - { - init = function() - set_goal("Thats it guys!") - set_info() - end, - condition = story_elapsed_check(5) - } - } -} - -story_init_helpers(story_table) - -return paperclips \ No newline at end of file diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/planet1.png b/fle/cluster/factorio-server/scenarios/default_lab_scenario/planet1.png deleted file mode 100644 index 0af9eb0eb675c5b114e8438708cea9edec97b866..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505684 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

    enO%K;9r;rR;Je~>hN4e76XN%zxNdt~GR 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

    hNF)LTvoNN#K|iR;fyc{Tv5ee>Ioa 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

    P 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 z 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
  • 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%0DsMtiB(J>)()%m5H{WrvJ~FmCg)x-K?+GaD_m! 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%0DNAYwh!`pA|m1KDSQY>q3REd9Q(vu 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 z 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 literal 0 HcmV?d00001 diff --git a/fle/env/factorio/scenarios/default_lab_scenario/control2.lua b/fle/env/factorio/scenarios/default_lab_scenario/control2.lua new file mode 100644 index 000000000..c3f166fe3 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/control2.lua @@ -0,0 +1,81 @@ +-- control.lua — save-based mod entrypoint + +-- Helper to require a list of modules under a prefix. +local function require_dir(prefix, list) + log("Loading " .. prefix .. " with " .. #list .. " modules") + for _, name in pairs(list) do require(prefix .. name) end +end + +-- Initialize persistent state early (Factorio will persist `global` with the save). +global = global or {} +global.actions = global.actions or {} +global.utils = global.utils or {} + + +-- Load utilities/admin/agent tools at top-level (outside events/RCON). +require_dir("script.utils.", { + -- e.g. "table_util", "positions", "strings" + 'clear_entities', + 'enemies', + 'initialise', + 'priority_queue', + 'reset', + 'alerts', + 'build_checkerboard', + 'recipe_fluid_connection_mappings', + 'clear_inventory', + 'initialise_inventory', + 'util', + 'production_score', + 'serialize', + 'checksum', + 'reset_position', + 'connection_points', +}) + +require_dir("script.tools.admin.", { + -- e.g. "tp", "ban", "blueprint_io" + 'clear_entities', + 'enemies', + 'initialise', + 'priority_queue', + 'reset', + 'alerts', + 'build_checkerboard', + 'recipe_fluid_connection_mappings', + 'clear_inventory', + 'initialise_inventory', + 'util', + 'production_score', + 'serialize', + 'checksum', + 'reset_position', + 'connection_points', +}) + +require_dir("script.tools.agent.", { + 'get_messages', + 'load_research_state', + 'inspect_entities', + 'production_stats', + 'clear_collision_boxes', + 'regenerate_resources', + 'extend_collision_boxes', + 'load_blueprint', + 'save_blueprint', + 'render', + 'get_factory_centroid', + 'clear_entities', + 'get_path', + 'save_research_state', + 'request_path', + 'render_message', + 'save_entity_state', + 'get_production_stats', + 'load_entity_state', +}) + +-- Register events/commands AFTER everything is required. +script.on_init(function() + -- any init work +end) diff --git a/fle/env/factorio/scenarios/default_lab_scenario/description.json b/fle/env/factorio/scenarios/default_lab_scenario/description.json new file mode 100644 index 000000000..d217d9b78 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/description.json @@ -0,0 +1,5 @@ +{ + "order": "a", + "multiplayer-compatible": true, + "is-main-game": true +} diff --git a/fle/env/factorio/scenarios/default_lab_scenario/freeplay.lua b/fle/env/factorio/scenarios/default_lab_scenario/freeplay.lua new file mode 100644 index 000000000..5afa12dfe --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/freeplay.lua @@ -0,0 +1,299 @@ +local util = require("util") +local crash_site = require("crash-site") +require('story') + +local created_items = function() + return + { + ["iron-plate"] = 8, + ["wood"] = 1, + ["pistol"] = 1, + ["firearm-magazine"] = 10, + ["burner-mining-drill"] = 1, + ["stone-furnace"] = 1 + } +end + +local respawn_items = function() + return + { + ["pistol"] = 1, + ["firearm-magazine"] = 10 + } +end + +local ship_items = function() + return + { + ["firearm-magazine"] = 8 + } +end + +local debris_items = function() + return + { + ["iron-plate"] = 8 + } +end + +local ship_parts = function() + return crash_site.default_ship_parts() +end + +local chart_starting_area = function() + local r = global.chart_distance or 200 + local force = game.forces.player + local surface = game.surfaces[1] + local origin = force.get_spawn_position(surface) + force.chart(surface, {{origin.x - r, origin.y - r}, {origin.x + r, origin.y + r}}) +end + + +local on_player_created = function(event) + local player = game.get_player(event.player_index) + util.insert_safe(player, global.created_items) + + if not global.init_ran then + + --This is so that other mods and scripts have a chance to do remote calls before we do things like charting the starting area, creating the crash site, etc. + global.init_ran = true + + chart_starting_area() + + if not global.disable_crashsite then + local surface = player.surface + surface.daytime = 0.7 + crash_site.create_crash_site(surface, {-5,-6}, util.copy(global.crashed_ship_items), util.copy(global.crashed_debris_items), util.copy(global.crashed_ship_parts)) + util.remove_safe(player, global.crashed_ship_items) + util.remove_safe(player, global.crashed_debris_items) + player.get_main_inventory().sort_and_merge() + if player.character then + player.character.destructible = false + end + global.crash_site_cutscene_active = true + crash_site.create_cutscene(player, {-5, -4}) + return + end + + end + + if not global.skip_intro then + if game.is_multiplayer() then + player.print(global.custom_intro_message or {"msg-intro"}) + else + game.show_message_dialog{text = global.custom_intro_message or {"msg-intro"}} + end + end + +end + +local on_player_respawned = function(event) + local player = game.get_player(event.player_index) + util.insert_safe(player, global.respawn_items) +end + +local on_tick = function(event) + story_update(global.story, event) + game.print("tick!") +end + +local on_cutscene_waypoint_reached = function(event) + if not global.crash_site_cutscene_active then return end + if not crash_site.is_crash_site_cutscene(event) then return end + + local player = game.get_player(event.player_index) + + player.exit_cutscene() + + if not global.skip_intro then + if game.is_multiplayer() then + player.print(global.custom_intro_message or {"msg-intro"}) + else + game.show_message_dialog{text = global.custom_intro_message or {"msg-intro"}} + end + end +end + +local skip_crash_site_cutscene = function(event) + if not global.crash_site_cutscene_active then return end + if event.player_index ~= 1 then return end + local player = game.get_player(event.player_index) + if player.controller_type == defines.controllers.cutscene then + player.exit_cutscene() + end +end + +local on_cutscene_cancelled = function(event) + if not global.crash_site_cutscene_active then return end + if event.player_index ~= 1 then return end + global.crash_site_cutscene_active = nil + local player = game.get_player(event.player_index) + if player.gui.screen.skip_cutscene_label then + player.gui.screen.skip_cutscene_label.destroy() + end + if player.character then + player.character.destructible = true + end + player.zoom = 1.5 +end + +local on_player_display_refresh = function(event) + crash_site.on_player_display_refresh(event) +end + +local freeplay_interface = +{ + get_created_items = function() + return global.created_items + end, + set_created_items = function(map) + global.created_items = map or error("Remote call parameter to freeplay set created items can't be nil.") + end, + get_respawn_items = function() + return global.respawn_items + end, + set_respawn_items = function(map) + global.respawn_items = map or error("Remote call parameter to freeplay set respawn items can't be nil.") + end, + set_skip_intro = function(bool) + global.skip_intro = bool + end, + get_skip_intro = function() + return global.skip_intro + end, + set_custom_intro_message = function(message) + global.custom_intro_message = message + end, + get_custom_intro_message = function() + return global.custom_intro_message + end, + set_chart_distance = function(value) + global.chart_distance = tonumber(value) or error("Remote call parameter to freeplay set chart distance must be a number") + end, + get_disable_crashsite = function() + return global.disable_crashsite + end, + set_disable_crashsite = function(bool) + global.disable_crashsite = bool + end, + get_init_ran = function() + return global.init_ran + end, + get_ship_items = function() + return global.crashed_ship_items + end, + set_ship_items = function(map) + global.crashed_ship_items = map or error("Remote call parameter to freeplay set created items can't be nil.") + end, + get_debris_items = function() + return global.crashed_debris_items + end, + set_debris_items = function(map) + global.crashed_debris_items = map or error("Remote call parameter to freeplay set respawn items can't be nil.") + end, + get_ship_parts = function() + return global.crashed_ship_parts + end, + set_ship_parts = function(parts) + global.crashed_ship_parts = parts or error("Remote call parameter to freeplay set ship parts can't be nil.") + end +} + +if not remote.interfaces["freeplay"] then + remote.add_interface("freeplay", freeplay_interface) +end + +local is_debug = function() + local surface = game.surfaces.nauvis + local map_gen_settings = surface.map_gen_settings + return map_gen_settings.width == 50 and map_gen_settings.height == 50 +end + +local freeplay = {} + +freeplay.events = +{ + [defines.events.on_player_created] = on_player_created, + [defines.events.on_player_respawned] = on_player_respawned, + [defines.events.on_cutscene_waypoint_reached] = on_cutscene_waypoint_reached, + ["crash-site-skip-cutscene"] = skip_crash_site_cutscene, + [defines.events.on_player_display_resolution_changed] = on_player_display_refresh, + [defines.events.on_player_display_scale_changed] = on_player_display_refresh, + [defines.events.on_cutscene_cancelled] = on_cutscene_cancelled, + [defines.events.on_tick] = on_tick +} + +freeplay.on_configuration_changed = function() + global.created_items = global.created_items or created_items() + global.respawn_items = global.respawn_items or respawn_items() + global.crashed_ship_items = global.crashed_ship_items or ship_items() + global.crashed_debris_items = global.crashed_debris_items or debris_items() + global.crashed_ship_parts = global.crashed_ship_parts or ship_parts() + + if not global.init_ran then + -- migrating old saves. + global.init_ran = #game.players > 0 + end +end + +global.story_table = +{ + { + { + init = function(event, story) + game.print("Welcome players") + set_goal({"Enjoy the scenery for 5 seconds"}) + end, + condition = story_elapsed_check(5) + }, + { + init = function(event, story) + game.print("Enough relaxation, time to automate!") + set_goal("Automate it!") + --game.players[1].set_goal_description("Automate!", true) + set_info{text = "Automating things is a lot of fun, trust me"} + for k, player in pairs (game.players) do + player.insert("iron-plate") + end + end, + update = function(event, story) + if event.name == defines.events.on_player_crafted_item then + game.players[event.player_index].print({"", "You crafted a ", game.item_prototypes[event.item_stack.name].localised_name}) + end + end, + condition = function(event) + return event.name == defines.events.on_built_entity + end, + action = function(event) + game.print({"", game.players[event.player_index].name, " built a ", event.created_entity.localised_name}) + end + }, + { + init = function() + set_goal("Thats it guys!") + set_info() + end, + condition = story_elapsed_check(5) + } + } +} +story_init_helpers(global.story_table) + +freeplay.on_init = function() + global.story = story_init() + global.created_items = created_items() + global.respawn_items = respawn_items() + global.crashed_ship_items = ship_items() + global.crashed_debris_items = debris_items() + global.crashed_ship_parts = ship_parts() + + if is_debug() then + global.skip_intro = true + global.disable_crashsite = true + end + +end + +story_init_helpers(story_table) + +return freeplay diff --git a/fle/env/factorio/scenarios/default_lab_scenario/info.json b/fle/env/factorio/scenarios/default_lab_scenario/info.json new file mode 100644 index 000000000..19765bd50 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/info.json @@ -0,0 +1 @@ +null diff --git a/fle/env/factorio/scenarios/default_lab_scenario/locale/en/freeplay.cfg b/fle/env/factorio/scenarios/default_lab_scenario/locale/en/freeplay.cfg new file mode 100644 index 000000000..5f466bfce --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/locale/en/freeplay.cfg @@ -0,0 +1,4 @@ +msg-intro=This is the Factorio Freeplay. Your task is to launch a rocket into space. You will need to research advanced technologies in order to unlock the rocket silo. Start small, work your way up with automation, and don't forget to protect yourself from the natives. +scenario-name=Freeplay +description=Your task is to launch a rocket into space. Start from nothing, work your way up with automation, and don't forget to protect yourself from the natives.\n[font=default-bold]This is the intended way of playing Factorio.[/font] + diff --git a/fle/env/factorio/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg b/fle/env/factorio/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg new file mode 100644 index 000000000..2aec677fb --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg @@ -0,0 +1,71 @@ +description=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. \n\n[font=default-bold]Mission Parameters:[/font] \n- New Research: Tank\n- Enemies do not evolve or expand\n +engineer-title=Engineer +msg-intro=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. +scenario-name=Mission 01 + +# Goal Parameters Configuration File +# Maps goal parameters to human-readable names + +goal-iron-ore=Collect 10 iron ore +goal-build-furnace=Build a stone furnace +goal-place-furnace=Place the stone furnace on the ground +goal-smelt-iron=Place 10 iron ore in the furnace and start smelting +goal-smelt-and-collect-10-iron-plates=Smelt and collect 10 iron plates from the furnace +goal-smelt-and-collect-10-copper-plates=Smelt and collect 10 copper plates from the furnace +completed-smelt-and-collect-10-copper-plates=Successfully smelted copper ore into copper plates +goal-completed-iron-plate=Successfully smelted iron ore into iron plates +goal-expand-factory=Expand your factory +goal-setup-assembling-machine=Set up the assembling machine to produce iron gear wheels +goal-supply-iron-plates=Supply the assembling machine with iron plates + + +goal-base=- Go to the base\n- Get into a parked vehicle +goal-build-tank=- Build a tank +goal-construct-tank=- Place tank on the ground +goal-ressearch-tank=Tank is now available in the tech tree \n- Research tank +goal-ressearch-military=- Research military +goal-restore-oil=- Restore oil production \n- Repair pumpjacks: __1__ / __2__ +goal-connect-oil=- Repair and connect pipes to the base\n- Oil must flow through the pipeline \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Kill 3 small worms nearby the pipeline +goal-destroy-spawners=- Destroy __1__ / __2__ biter nests +goal-destroy-spawners2=- Destroy __1__ / __2__ biter nests \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Roadkill the nests by tank +goal-grenades=- Construct and keep at least __1__/__2__ grenades in your inventory +goal-chemical=Chemical plant is now available for the construction \n- Build chemical plants: __1__ / __2__ +goal-train-station=Automated rail transportation is now available in the tech tree \n- Research automated rail transportation \n- Build __1__ / __2__ train stops \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Secure supply line from abandoned bases in the west +msg-base-1=[color=orange]OUTER BASE:[/color] Anyone survived the crash? If you can hear me, our base is located [font=default-bold]NORTHEAST[/font] from your position. +msg-base-2=[color=orange]OUTER BASE:[/color] It was a blast! Those bugs can shoot the plasma really high! Too bad they just hit you. +msg-base-3=[color=orange]OUTER BASE:[/color] I can' t send people to you, our base is under attack! If you arrive unharmed, get in the [font=default-bold]VEHICLE[/font] parked inside! Then I'll send you further instructions. +msg-base-4=[color=orange]OUTER BASE:[/color] The bugs destroyed our pumpjacks and pipelines. We have no fuel supply for our flamethrower turrets. +msg-base-5=[color=orange]OUTER BASE:[/color] The oil facilities lie on [font=default-bold]NORTH[/font]. Can you take a look at this? +msg-base-6=[color=orange]OUTER BASE:[/color] In the [font=default-bold]VEHICLE IS THE EQUIPMENT[/font] for repairing the pumpjacks and pipes. +msg-base-7=[color=orange]OUTER BASE:[/color] I love the smell of running gasoline. Great job! +msg-base-8=[color=orange]OUTER BASE:[/color] I have good news and bad news. The good news is, I've assigned some robots to repair your ship. And the bad news... We need you to solve an insect problem. I think that's fair, right? +msg-base-9=[color=orange]OUTER BASE:[/color] If you want to survive in the wasteland, you should get some better equipment. Use our [font=default-bold]LABORATORIES[/font] to research weapons! \n\nIf I were you, I'd be all over it, because bugs never sleep! +msg-base-10=[color=orange]OUTER BASE:[/color] Well done! It's time to finally get rid of the bugs! As soon as you're ready, head out into the wasteland. +msg-base-11=[color=orange]OUTER BASE:[/color] [font=default-bold]DESTROY[/font] their [font=default-bold]NESTS[/font] around the base. We're not gonna let some insect terrorize us! +msg-base-12=[color=orange]OUTER BASE:[/color] Dead bug, good bug! Finally there will be some peace, but I don't want to jinx it. \n\nI would be quite happy if you could help us construct a box of [font=default-bold]GRENADES[/font]. It's in your interest too. \n\nDon't forget to research advance millitary first. +msg-base-12.1=[color=orange]OUTER BASE:[/color] Just don't throw those grenades under your feet. I've had a couple of individuals here who've... But. What a waste of words. +msg-base-12.2=[color=orange]OUTER BASE:[/color] At least you know how to make explosives. Believe me, bugs really don't like when things explode under their legs. Unfortunately, they're smarter than they look and flattened our [font=default-bold]CHEMICAL PLANT[/font] in the last attack. Will you be so kind and construct a new one? +msg-base-13=[color=orange]OUTER BASE:[/color] You're the one who crashed on this planet years ago, right? You have really bad luck for landing, ha ha. \n\nI think I can trust you. It's classified, but screw it. We're kind of researching new weapons here. At the moment, we're developing a multi-functional [font=default-bold]TANK[/font]. +msg-base-14=[color=orange]OUTER BASE:[/color] However, we are slowly running out of material here. The base is normally supplied by the train line from [font=default-bold]WESTERN STATIONS[/font]. But we lost contact a month ago. +msg-base-15=[color=orange]OUTER BASE:[/color] The bugs probably got them. Either way, it is essential to [font=default-bold]RESTORE SUPPLY LINE[/font] to the base. Make sure we have a link between us and the stations so we can continue with our research. +msg-base-16=[color=orange]OUTER BASE:[/color] Research done, now we need to [font=default-bold]CONSTRUCT THE TANK[/font]. Your automation is more efficient than ours. We'd be happy to have you take care of it. +msg-base-17=[color=orange]OUTER BASE:[/color] We need to do some tests and our good insect friends will be "happy" to help us. [font=default-bold]FILL THE TANK[/font] with the ammo of your choice and [font=default-bold]DESTROY[/font] other insect hatcheries. +msg-base-18=[color=orange]OUTER BASE:[/color] Well done! The test met our expectations. Thank you for your assistance! \n\nWe managed to fix your ship. It's not as good as new, but it'll fly to the nearest dock. Good luck, and if you accidentally fly by, I'll make sure you get to crash... I mean, land safely here. Ha, ha! +think-fall-ship=Ouch, that was a hard landing. What happened? +think-ship-members=Oh, no! My crew is dead... +think-find-base=Okay, those tracks should lead to the base, I should follow them. +think-assist=Great, now I'm a plumber... I'd better get that fuel supply going again, or it's gonna get pretty stuffy in here. +think-assist2=I should handle that vehicle with care. It would be pretty sucky if I had to run around on foot. +think-base=I should go back to base and automate it a little better. +think-base2=There are some coal and iron deposits on the northwestern tip that could be exploited. +think-weapon=Hmm, the planet was only recently discovered and already corporations are doing weapons research here? Suspicious... +think-tank=I have to admit, having a tracked armored monster by my side is quite reassuring. +think-car=With the vehicle, the disposal of nests will be easier. +think-grenade=I've never been a fan of grenades, but I don't have much choice. +think-done=Train stops are done, now we'll focus on researching the tank. + +[intro-screen] +header=Mission 01 - Return +continue=START MISSION +intro=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. \n\n[font=default-bold]Mission Objectives:[/font]\n- Find the Outer Base \n\n\n[font=default-bold]Mission Intel:[/font]\n- Follow the objectives to get new researches.\n- Scouting your surroundings can be worthwhile. +param=[font=default-bold]Mission Parameters:[/font]\n- New Research: Tank\n\nOn Difficulty Easy:\n- Enemies do not evolve or expand\n- Increased starting items & ammo\n\nOn Difficulty Normal:\n- Enemies do not evolve or expand\n- Decreased starting items & ammo\n\nOn Difficulty Hard:\n- Increased starting evolution factor\n- Enemies evolve and expand \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/paperclips.lua b/fle/env/factorio/scenarios/default_lab_scenario/paperclips.lua new file mode 100644 index 000000000..99edfa4f4 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/paperclips.lua @@ -0,0 +1,81 @@ +require("story") + +local paperclips = {} +--Init the story script +paperclips.on_init = function() + global.story = story_init(player(1)) +end + +--Register the update to all events +local on_event = function(event) + story_update(global.story, event) +end + +--Can also register to specific events, if you want to do multiple things with that event +local on_tick = function(event) + if global.story == nil then + global.story = story_init(player(1)) + end + story_update(global.story, event) + game.print("tick") + --Other things on tick can go here. +end + +paperclips.events = +{ + [defines.events.on_player_created] = on_event, + [defines.events.on_player_respawned] = on_event, + [defines.events.on_cutscene_waypoint_reached] = on_event, + ["crash-site-skip-cutscene"] = on_event, + [defines.events.on_player_display_resolution_changed] = on_event, + [defines.events.on_player_display_scale_changed] = on_event, + [defines.events.on_cutscene_cancelled] = on_event, + [defines.events.on_tick] = on_tick +} + + +story_table = +{ + { + { + init = function(event, story) + game.print("Welcome players") + set_goal({"Enjoy the scenery for 5 seconds"}) + end, + condition = story_elapsed_check(5) + }, + { + init = function(event, story) + game.print("Enough relaxation, time to automate!") + set_goal("Automate it!") + --game.players[1].set_goal_description("Automate!", true) + set_info{text = "Automating things is a lot of fun, trust me"} + for k, player in pairs (game.players) do + player.insert("iron-plate") + end + end, + update = function(event, story) + if event.name == defines.events.on_player_crafted_item then + game.players[event.player_index].print({"", "You crafted a ", game.item_prototypes[event.item_stack.name].localised_name}) + end + end, + condition = function(event) + return event.name == defines.events.on_built_entity + end, + action = function(event) + game.print({"", game.players[event.player_index].name, " built a ", event.created_entity.localised_name}) + end + }, + { + init = function() + set_goal("Thats it guys!") + set_info() + end, + condition = story_elapsed_check(5) + } + } +} + +story_init_helpers(story_table) + +return paperclips \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/planet1.png b/fle/env/factorio/scenarios/default_lab_scenario/planet1.png new file mode 100644 index 0000000000000000000000000000000000000000..0af9eb0eb675c5b114e8438708cea9edec97b866 GIT binary patch literal 505684 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

    enO%K;9r;rR;Je~>hN4e76XN%zxNdt~GR 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

    hNF)LTvoNN#K|iR;fyc{Tv5ee>Ioa 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

    P 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 z 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 GQU3skajW Dict[str, Any]: - services = {} - base_udp_port = 34197 - base_tcp_port = 27000 - - for i in range(num_instances): - service_name = f"factorio_{i}" - services[service_name] = { - "image": "factorio", - "pull_policy": "never", - "platform": "linux/amd64", - "environment": [ - "SAVES=/opt/factorio/saves", - "CONFIG=/opt/factorio/config", - "MODS=/opt/factorio/mods", - "SCENARIOS=/opt/factorio/scenarios", - f"PORT={base_udp_port}", - "RCON_PORT=27015", - ], - "volumes": [ - { - "type": "bind", - "source": "../scenarios/default_lab_scenario", - "target": "/opt/factorio/scenarios/default_lab_scenario", - }, - { - "type": "bind", - "source": "../scenarios/open_world", - "target": "/opt/factorio/scenarios/open_world", - }, - { - "type": "bind", - "source": "~/Applications/Factorio.app/Contents/Resources/mods", - "target": "/opt/factorio/mods", - }, - { - "type": "bind", - "source": "../../data/_screenshots", - "target": "/opt/factorio/script-output", - }, - ], - "ports": [ - f"{base_udp_port + i}:{base_udp_port}/udp", - f"{base_tcp_port + i}:27015/tcp", - ], - "restart": "unless-stopped", - "user": "factorio", - "entrypoint": [], - "command": " ".join( - [ - "/opt/factorio/bin/x64/factorio", - f"--start-server-load-scenario {map}", - f"--port {base_udp_port}", - "--server-settings /opt/factorio/config/server-settings.json", - "--map-gen-settings /opt/factorio/config/map-gen-settings.json", - "--map-settings /opt/factorio/config/map-settings.json", - "--server-banlist /opt/factorio/config/server-banlist.json", - "--rcon-port 27015", - '--rcon-password "factorio"', - "--server-whitelist /opt/factorio/config/server-whitelist.json", - "--use-server-whitelist", - "--server-adminlist /opt/factorio/config/server-adminlist.json", - "--mod-directory /opt/factorio/mods", - "--map-gen-seed 44340", - ] - ), - "deploy": {"resources": {"limits": {"cpus": "1", "memory": "1024m"}}}, - } - - return {"services": services} - - -def setup_docker_compose(num_instances: int, map: str): - """Generate and write docker-compose.yml, then start the containers""" - config = generate_compose_config(num_instances, map) - - # Write the configuration to docker-compose.yml - with open(f"docker-compose-{num_instances}.yml", "w") as f: - yaml.dump(config, f) - - # Start the containers - subprocess.run( - ["docker-compose", "up", "-d", f"docker-compose-{num_instances}.yml"] - ) - - # Wait for containers to be ready - time.sleep(10) - - -if __name__ == "__main__": - num_instances = 33 - map = "open_world" # or default_lab_scenario - if len(sys.argv) != 2: - print("Usage: python create_docker_compose_config.py ") - else: - num_instances = int(sys.argv[1]) - - setup_docker_compose(num_instances, map) - print( - f"Created docker-compose-{num_instances}.yml for {num_instances} Factorio instances." - ) diff --git a/fle/cluster/local/docker-compose-1-example.yml b/fle/cluster/local/docker-compose-1-example.yml deleted file mode 100644 index 5604aadc4..000000000 --- a/fle/cluster/local/docker-compose-1-example.yml +++ /dev/null @@ -1,43 +0,0 @@ -services: - factorio_0: - command: /opt/factorio/bin/x64/factorio --start-server-load-scenario open_world - --port 34197 --server-settings /opt/factorio/config/server-settings.json --map-gen-settings - /opt/factorio/config/map-gen-settings.json --map-settings /opt/factorio/config/map-settings.json - --server-banlist /opt/factorio/config/server-banlist.json --rcon-port 27015 - --rcon-password "factorio" --server-whitelist /opt/factorio/config/server-whitelist.json - --use-server-whitelist --server-adminlist /opt/factorio/config/server-adminlist.json - --mod-directory /opt/factorio/mods --map-gen-seed 44340 - deploy: - resources: - limits: - cpus: '1' - memory: 1024m - entrypoint: [] - environment: - - SAVES=/opt/factorio/saves - - CONFIG=/opt/factorio/config - - MODS=/opt/factorio/mods - - SCENARIOS=/opt/factorio/scenarios - - PORT=34197 - - RCON_PORT=27015 - image: factorio - platform: linux/amd64 - ports: - - 34197:34197/udp - - 27000:27015/tcp - pull_policy: never - restart: unless-stopped - user: factorio - volumes: - - source: ../scenarios/default_lab_scenario - target: /opt/factorio/scenarios/default_lab_scenario - type: bind - - source: ../scenarios/open_world - target: /opt/factorio/scenarios/open_world - type: bind - - source: ~/Applications/Factorio.app/Contents/Resources/mods - target: /opt/factorio/mods - type: bind - - source: ../../data/_screenshots - target: /opt/factorio/script-output - type: bind diff --git a/fle/cluster/local/factorio_server_login.py b/fle/cluster/local/factorio_server_login.py deleted file mode 100644 index 818336a77..000000000 --- a/fle/cluster/local/factorio_server_login.py +++ /dev/null @@ -1,195 +0,0 @@ -import cv2 -import numpy as np -from typing import Tuple, Optional -import psutil -import pyautogui -import time -import subprocess -from pathlib import Path -from dataclasses import dataclass - -from fle.commons.cluster_ips import get_local_container_ips -from fle.cluster.remote.factorio_server_login import get_uninitialised_ips -from screeninfo import get_monitors - - -@dataclass -class UIElement: - name: str - image_path: str - confidence: float = 0.7 - timeout: int = 10 - - -def launch_factorio(): - # Check to see if Factorio is open - if not any(process.name() == "factorio" for process in psutil.process_iter()): - # Launch Factorio if it's not running - print("Launching Factorio...") - # Adjust the path to your Factorio executable - subprocess.Popen(["open", "-a", "Factorio"]) - time.sleep(10) # Wait for Factorio to start up - - -def focus_factorio(): - # Use AppleScript to focus Factorio - applescript = """ - tell application "Factorio" to activate - """ - subprocess.run(["osascript", "-e", applescript]) - time.sleep(1) # Wait a moment for the window to come into focus - - -class FactorioAutomation: - def __init__(self, assets_dir: str = "assets", monitor_index: int = 0): - self.assets_dir = Path(assets_dir) - self.assets_dir.mkdir(exist_ok=True) - - self.monitor = get_monitors()[monitor_index] - # Handle negative coordinates - self.monitor_region = ( - max(0, self.monitor.x), # Ensure x is not negative - max(0, self.monitor.y), # Ensure y is not negative - self.monitor.width, - self.monitor.height, - ) - - # Store offset for click adjustment - self.x_offset = self.monitor.x - self.y_offset = self.monitor.y - - # Define UI elements - self.ui_elements = { - "multiplayer": UIElement("Multiplayer", "multiplayer_button.png", 0.9), - "connect_address": UIElement( - "Connect to Address", "connect_to_address_button.png", 0.9 - ), - "ip_field": UIElement("IP Field", "ip_field.png", 0.85), - "connect": UIElement("Connect", "connect_button.png", 0.9), - "quit": UIElement("Quit", "quit_button.png", 0.9), - } - - def verify_assets(self) -> bool: - """Verify all required UI element images exist.""" - missing = [] - for element in self.ui_elements.values(): - if not (self.assets_dir / element.image_path).exists(): - missing.append(element.image_path) - - if missing: - print(f"Missing required assets: {', '.join(missing)}") - return False - return True - - def locate_element(self, element: UIElement) -> Optional[Tuple[int, int]]: - """ - Locate UI element using template matching with OpenCV. - Returns center coordinates if found, None otherwise. - """ - try: - # Take screenshot - screenshot = pyautogui.screenshot() - screenshot_np = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR) - - # Load template - template = cv2.imread(str(self.assets_dir / element.image_path)) - - # Template matching - result = cv2.matchTemplate(screenshot_np, template, cv2.TM_CCOEFF_NORMED) - min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) - - if max_val >= element.confidence: - h, w = template.shape[:2] - center = (max_loc[0] + w // 2, max_loc[1] + h // 2) - return center - - except Exception as e: - print(f"Error locating {element.name}: {str(e)}") - - return None - - def locate_and_click(self, element: UIElement) -> bool: - start_time = time.time() - while time.time() - start_time < element.timeout: - try: - location = pyautogui.locateOnWindow( # ).locateCenterOnScreen( - str(self.assets_dir / element.image_path), - "Factorio 1.1.110", - confidence=element.confidence, - region=self.monitor_region, - ) - if location: - # Adjust click coordinates for monitor offset - click_x = location[0] + (self.x_offset if self.x_offset < 0 else 0) - click_y = location[1] + (self.y_offset if self.y_offset < 0 else 0) - pyautogui.click(click_x, click_y) - time.sleep(0.5) - return True - except pyautogui.ImageNotFoundException: - pass - time.sleep(0.5) - - print(f"Could not find {element.name} after {element.timeout} seconds") - return False - - def connect_to_server(self, ip_address: str, udp: int = 34197) -> bool: - """Connect to Factorio server using image recognition.""" - if not self.verify_assets(): - return False - - focus_factorio() - - # Reset to main menu - for _ in range(3): - pyautogui.press("esc") - time.sleep(0.5) - - # Click through menu sequence - for element in ["multiplayer", "connect_address", "ip_field"]: - if not self.locate_and_click(self.ui_elements[element]): - raise Exception(f"Could not find element - {element}") - - # Enter IP address - pyautogui.hotkey("command", "a") - pyautogui.press("delete") - pyautogui.write(f"{ip_address}:{udp}") - - # Connect and wait - if not self.locate_and_click(self.ui_elements["connect"]): - return False - - time.sleep(5) - - # Exit sequence - pyautogui.press("esc") - return self.locate_and_click(self.ui_elements["quit"]) - - -def main(): - # Display available monitors - - ips, udp_ports, tcp_ports = get_local_container_ips() - if not ips: - raise RuntimeError("No local container IPs found") - - ip_addresses = get_uninitialised_ips(ips, tcp_ports) - launch_factorio() - - monitors = get_monitors() - print("Available monitors:") - for i, m in enumerate(monitors): - print(f"{i}: {m.name} ({m.width}x{m.height} at ({m.x}, {m.y}))") - - for monitor_index in [1]: - print(f"\nAttempting to connect on monitor {monitors[monitor_index].name}...") - automation = FactorioAutomation(monitor_index=monitor_index) - - for ip, udp in zip(ip_addresses, udp_ports): - success = automation.connect_to_server(ip, udp) - print( - f"{'Successfully connected to' if success else 'Failed to connect to'} {ip}" - ) - - -if __name__ == "__main__": - main() diff --git a/fle/cluster/local/run-envs.sh b/fle/cluster/local/run-envs.sh deleted file mode 100755 index b03ec20e0..000000000 --- a/fle/cluster/local/run-envs.sh +++ /dev/null @@ -1,373 +0,0 @@ -#!/bin/bash - -# Function to detect and set host architecture -setup_platform() { - ARCH=$(uname -m) - OS=$(uname -s) - if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then - export DOCKER_PLATFORM="linux/amd64" - else - export DOCKER_PLATFORM="linux/amd64" - fi - export SAVES_PATH="../../../.fle/saves" - # Detect OS for mods path - if [[ "$OS" == *"MINGW"* ]] || [[ "$OS" == *"MSYS"* ]] || [[ "$OS" == *"CYGWIN"* ]]; then - # Windows detected - export OS_TYPE="windows" - # Use %APPDATA% which is available in Windows bash environments - export MODS_PATH="${APPDATA}/Factorio/mods" - # Fallback if APPDATA isn't available - if [ -z "$MODS_PATH" ] || [ "$MODS_PATH" == "/Factorio/mods" ]; then - export MODS_PATH="${USERPROFILE}/AppData/Roaming/Factorio/mods" - fi - else - # Assume Unix-like OS (Linux, macOS) - export OS_TYPE="unix" - export MODS_PATH="~/Applications/Factorio.app/Contents/Resources/mods" - fi - echo "Detected architecture: $ARCH, using platform: $DOCKER_PLATFORM" - echo "Using mods path: $MODS_PATH" -} - -# Function to check for docker compose command -setup_compose_cmd() { - if command -v docker &> /dev/null; then - COMPOSE_CMD="docker compose" - else - echo "Error: Docker not found. Please install Docker." - exit 1 - fi -} - -# Function to check for saves and find the latest one -check_saves_and_get_latest() { - # Check if saves directory exists - if [ ! -d "$SAVES_PATH" ]; then - echo "Error: Saves directory not found at $SAVES_PATH" - exit 1 - fi - - # Add the specified number of factorio services - for i in $(seq 0 $(($NUM_INSTANCES - 1))); do - if ! ls "$SAVES_PATH/${i}"/*.zip 1> /dev/null 2>&1; then - echo "Error: No .zip save files found in $SAVES_PATH/${i} for instance $i" - exit 1 - fi - done - - # Find the latest save file based on modification time - LATEST_SAVE=$(ls -t "$SAVES_PATH"/*.zip | head -1) - LATEST_SAVE_NAME=$(basename "$LATEST_SAVE") - - echo "Found latest save: $LATEST_SAVE_NAME" - echo "Using save path: $SAVES_PATH" -} - -# Generate the dynamic docker-compose.yml file -generate_compose_file() { - NUM_INSTANCES=${1:-1} - SCENARIO=${2:-"default_lab_scenario"} - USE_LATEST_SAVE=${3:-false} - - # Validate scenario if not using latest save - if [ "$USE_LATEST_SAVE" = "false" ]; then - if [ "$SCENARIO" != "open_world" ] && [ "$SCENARIO" != "default_lab_scenario" ]; then - echo "Error: Scenario must be either 'open_world' or 'default_lab_scenario'." - exit 1 - fi - fi - - # Validate input - if ! [[ "$NUM_INSTANCES" =~ ^[0-9]+$ ]]; then - echo "Error: Number of instances must be a positive integer." - exit 1 - fi - - if [ "$NUM_INSTANCES" -lt 1 ] || [ "$NUM_INSTANCES" -gt 33 ]; then - echo "Error: Number of instances must be between 1 and 33." - exit 1 - fi - - # Determine the command based on whether to use latest save or scenario - if [ "$USE_LATEST_SAVE" = "true" ]; then - # Add the specified number of factorio services - for i in $(seq 0 $(($NUM_INSTANCES - 1))); do - mkdir -p ${SAVES_PATH}/${i} - done - - START_COMMAND="--start-server-load-latest" - else - START_COMMAND="--start-server-load-scenario ${SCENARIO}" - fi - - # Create the docker-compose file - cat > docker-compose.yml << EOF -version: '3' - -services: -EOF - - # Add the specified number of factorio services - for i in $(seq 0 $(($NUM_INSTANCES - 1))); do - UDP_PORT=$((34197 + i)) - TCP_PORT=$((27000 + i)) - - cat >> docker-compose.yml << EOF - factorio_${i}: - image: factorio - platform: \${DOCKER_PLATFORM:-linux/amd64} - command: /opt/factorio/bin/x64/factorio ${START_COMMAND} - --port 34197 --server-settings /opt/factorio/config/server-settings.json --map-gen-settings - /opt/factorio/config/map-gen-settings.json --map-settings /opt/factorio/config/map-settings.json - --server-banlist /opt/factorio/config/server-banlist.json --rcon-port 27015 - --rcon-password "factorio" --server-whitelist /opt/factorio/config/server-whitelist.json - --use-server-whitelist --server-adminlist /opt/factorio/config/server-adminlist.json - --mod-directory /opt/factorio/mods --map-gen-seed 44340 - deploy: - resources: - limits: - cpus: '1' - memory: 1024m - entrypoint: [] - environment: - - SAVES=/opt/factorio/saves - - CONFIG=/opt/factorio/config - - MODS=/opt/factorio/mods - - SCENARIOS=/opt/factorio/scenarios - - PORT=34197 - - RCON_PORT=27015 - ports: - - ${UDP_PORT}:34197/udp - - ${TCP_PORT}:27015/tcp - pull_policy: never - restart: unless-stopped - user: factorio - volumes: - - source: ../scenarios/default_lab_scenario - target: /opt/factorio/scenarios/default_lab_scenario - type: bind - - source: ../scenarios/open_world - target: /opt/factorio/scenarios/open_world - type: bind - - source: ${MODS_PATH} - target: /opt/factorio/mods - type: bind - - source: ../../data/_screenshots - target: /opt/factorio/script-output - type: bind - - source: ${SAVES_PATH}/${i} - target: /opt/factorio/saves - type: bind - -EOF - done - - if [ "$USE_LATEST_SAVE" = "true" ]; then - echo "Generated docker-compose.yml with $NUM_INSTANCES Factorio instance(s) using latest save" - else - echo "Generated docker-compose.yml with $NUM_INSTANCES Factorio instance(s) using scenario $SCENARIO" - fi -} - -# Function to start Factorio cluster -start_cluster() { - NUM_INSTANCES=$1 - SCENARIO=$2 - USE_LATEST_SAVE=$3 - - setup_platform - setup_compose_cmd - - # Check for saves if using latest save mode - if [ "$USE_LATEST_SAVE" = "true" ]; then - check_saves_and_get_latest - fi - - # Generate the docker-compose file - generate_compose_file "$NUM_INSTANCES" "$SCENARIO" "$USE_LATEST_SAVE" - - # Run the docker-compose file - if [ "$USE_LATEST_SAVE" = "true" ]; then - echo "Starting $NUM_INSTANCES Factorio instance(s) with latest save..." - else - echo "Starting $NUM_INSTANCES Factorio instance(s) with scenario $SCENARIO..." - fi - export NUM_INSTANCES # Make it available to docker-compose - $COMPOSE_CMD -f docker-compose.yml up -d - - if [ "$USE_LATEST_SAVE" = "true" ]; then - echo "Factorio cluster started with $NUM_INSTANCES instance(s) using platform $DOCKER_PLATFORM and latest save" - else - echo "Factorio cluster started with $NUM_INSTANCES instance(s) using platform $DOCKER_PLATFORM and scenario $SCENARIO" - fi -} - -# Function to stop Factorio cluster -stop_cluster() { - setup_compose_cmd - - if [ -f "docker-compose.yml" ]; then - echo "Stopping Factorio cluster..." - $COMPOSE_CMD -f docker-compose.yml down - echo "Cluster stopped." - else - echo "Error: docker-compose.yml not found. No cluster to stop." - exit 1 - fi -} - -# Function to restart Factorio cluster -restart_cluster() { - setup_compose_cmd - - if [ ! -f "docker-compose.yml" ]; then - echo "Error: docker-compose.yml not found. No cluster to restart." - exit 1 - fi - - echo "Extracting current configuration..." - - # Extract the number of instances - CURRENT_INSTANCES=$(grep -c "factorio_" docker-compose.yml) - - # Check if using latest save or scenario - if grep -q "start-server-load-latest" docker-compose.yml; then - CURRENT_MODE="latest_save" - echo "Found cluster using latest save mode" - else - CURRENT_MODE="scenario" - # Extract the scenario from the first instance - CURRENT_SCENARIO=$(grep -A1 "command:" docker-compose.yml | grep "start-server-load-scenario" | head -1 | sed -E 's/.*start-server-load-scenario ([^ ]+).*/\1/') - - if [ -z "$CURRENT_SCENARIO" ]; then - CURRENT_SCENARIO="default_lab_scenario" - echo "Warning: Could not determine current scenario, using default: $CURRENT_SCENARIO" - fi - echo "Found cluster with scenario: $CURRENT_SCENARIO" - fi - - echo "Found cluster with $CURRENT_INSTANCES instances" - - # Stop the current cluster - echo "Stopping current cluster..." - $COMPOSE_CMD -f docker-compose.yml down - - # Start with the same configuration - echo "Restarting cluster..." - if [ "$CURRENT_MODE" = "latest_save" ]; then - start_cluster "$CURRENT_INSTANCES" "" "true" - else - start_cluster "$CURRENT_INSTANCES" "$CURRENT_SCENARIO" "false" - fi - - echo "Factorio cluster restarted successfully." -} - -# Show usage information -show_help() { - echo "Usage: $0 [COMMAND] [OPTIONS]" - echo "" - echo "Commands:" - echo " start Start Factorio instances (default command)" - echo " stop Stop all running instances" - echo " restart Restart the current cluster with the same configuration" - echo " help Show this help message" - echo "" - echo "Options:" - echo " -n NUMBER Number of Factorio instances to run (1-33, default: 1)" - echo " -s SCENARIO Scenario to run (open_world or default_lab_scenario, default: default_lab_scenario)" - echo " -l Use latest save instead of scenario (loads most recent .zip save from ../../../.fle/saves/0..n)" - echo "" - echo "Examples:" - echo " $0 Start 1 instance with default_lab_scenario" - echo " $0 -n 5 Start 5 instances with default_lab_scenario" - echo " $0 -n 3 -s open_world Start 3 instances with open_world" - echo " $0 -l Start 1 instance with latest save" - echo " $0 -n 5 -l Start 5 instances with latest save" - echo " $0 start -n 10 -s open_world Start 10 instances with open_world" - echo " $0 stop Stop all running instances" - echo " $0 restart Restart the current cluster" - echo "" - echo "Note: When using -l (latest save), the system will:" - echo " 1. Check for .zip save files in ../../../.fle/saves/0..n" - echo " 2. Finds the most recently modified save file" - echo " 3. Start the server with --start-server-load-latest" -} - -# Main script execution -COMMAND="start" -NUM_INSTANCES=1 -SCENARIO="default_lab_scenario" -USE_LATEST_SAVE=false - -# Check if first arg is a command -if [[ "$1" == "start" || "$1" == "stop" || "$1" == "restart" || "$1" == "help" ]]; then - COMMAND="$1" - shift -fi - -# Parse options with getopts -while getopts ":n:s:lh" opt; do - case ${opt} in - n ) - if ! [[ "$OPTARG" =~ ^[0-9]+$ ]]; then - echo "Error: Number of instances must be a positive integer." - exit 1 - fi - NUM_INSTANCES=$OPTARG - ;; - s ) - if [ "$OPTARG" != "open_world" ] && [ "$OPTARG" != "default_lab_scenario" ]; then - echo "Error: Scenario must be either 'open_world' or 'default_lab_scenario'." - exit 1 - fi - SCENARIO=$OPTARG - ;; - l ) - USE_LATEST_SAVE=true - ;; - h ) - show_help - exit 0 - ;; - \? ) - echo "Error: Invalid option: -$OPTARG" - show_help - exit 1 - ;; - : ) - echo "Error: Option -$OPTARG requires an argument." - show_help - exit 1 - ;; - esac -done -shift $((OPTIND -1)) - -# Validate that scenario and latest save are not used together -if [ "$USE_LATEST_SAVE" = "true" ] && [ "$SCENARIO" != "default_lab_scenario" ]; then - echo "Error: Cannot use both -l (latest save) and -s (scenario) options together." - echo "When using -l, the scenario option is ignored as the server loads the latest save." - exit 1 -fi - -# Execute the appropriate command -case "$COMMAND" in - start) - start_cluster "$NUM_INSTANCES" "$SCENARIO" "$USE_LATEST_SAVE" - ;; - stop) - stop_cluster - ;; - restart) - restart_cluster - ;; - help) - show_help - ;; - *) - echo "Error: Unknown command '$COMMAND'" - show_help - exit 1 - ;; -esac \ No newline at end of file diff --git a/fle/cluster/scenarios/default_lab_scenario/blueprint.zip b/fle/cluster/scenarios/default_lab_scenario/blueprint.zip deleted file mode 100644 index e603e83157500bd58d9179383dfdfae913250b94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 215969 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

  • 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%0DsMtiB(J>)()%m5H{WrvJ~FmCg)x-K?+GaD_m! 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%0DNAYwh!`pA|m1KDSQY>q3REd9Q(vu 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 z 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 diff --git a/fle/cluster/scenarios/default_lab_scenario/control.lua b/fle/cluster/scenarios/default_lab_scenario/control.lua deleted file mode 100644 index 8e30a5fed..000000000 --- a/fle/cluster/scenarios/default_lab_scenario/control.lua +++ /dev/null @@ -1,446 +0,0 @@ -require "util" ---local intro_screen = require("intro_screen") -require "story" - -global.paths = global.paths or {} - -local think = function(thought) - game.players[1].print({"","[img=entity/character][color=orange]",{"engineer-title"},": [/color]",{"think-"..thought}}) -end - -function check_for_player_death(event) - if event.name == defines.events.on_player_died then - game.set_game_state({game_finished=true, player_won=false, can_continue=false}) - end -end - -local function on_configuration_changed() - game.reload_script() -end - -local on_player_created = function(event) - - local player = game.players[1] - - player.force.enable_research() - game.forces.player.disable_all_prototypes() - game.forces.player.reset_technology_effects() - -- game.forces.player.reset() - game.forces.player.research_queue_enabled = true - game.map_settings.enemy_expansion.enabled = false - game.forces.enemy.evolution_factor = 0 - game.map_settings.enemy_evolution.enabled = false - --game.disable_tips_and_tricks() - -- game.players[1].force.disable_all_prototypes() - global.storage_tank = game.get_entity_by_tag("storage-tank") - local entities = - { - global.storage_tank - } - for index, entity in pairs(entities) do - entity.minable = false - entity.destructible = false - end - global.intro_entities = entities - - - global.car1 = game.get_entity_by_tag("car1") - local entities2 = - { - global.car1 - } - for index, entity in pairs(entities2) do - entity.minable = false - end - global.intro_entities2 = entities2 - - - local recipe_list = player.force.recipes - -- game.players[1].clear_recipe_notifications() - recipe_list["chemical-plant"].enabled = false - - local technology_list = game.forces.player.technologies - technology_list["automation"].researched = true - technology_list["stone-wall"].researched = true - - technology_list["optics"].enabled = true - technology_list["gun-turret"].researched = true - technology_list["logistics"].enabled = true - technology_list["military"].enabled = true - technology_list["logistic-science-pack"].researched = true - technology_list["steel-processing"].enabled = true - technology_list["electronics"].researched = true - technology_list["fast-inserter"].enabled = true - technology_list["automation-2"].enabled = true - technology_list["electric-energy-distribution-1"].enabled = true - technology_list["engine"].enabled = true - technology_list["fluid-handling"].enabled = true - technology_list["oil-processing"].researched = true - technology_list["logistics"].enabled = true - technology_list["heavy-armor"].enabled = true - technology_list["steel-axe"].enabled = true - technology_list["military-2"].enabled = true - technology_list["chemical-science-pack"].enabled = true - technology_list["military-science-pack"].enabled = true - technology_list["logistics-2"].enabled = true - technology_list["advanced-material-processing"].enabled = true - technology_list["solar-energy"].enabled = true - technology_list["laser"].enabled = false - technology_list["flamethrower"].enabled = true - technology_list["flammables"].researched = true - technology_list["modules"].enabled = false - technology_list["speed-module"].enabled = false - technology_list["effectivity-module"].enabled = false - technology_list["productivity-module"].enabled = false - technology_list["sulfur-processing"].researched = true - technology_list["toolbelt"].enabled = true - technology_list["advanced-electronics"].researched = true - technology_list["explosives"].enabled = true - technology_list["land-mine"].enabled = true - technology_list["electric-energy-accumulators"].enabled = true - technology_list["laser-turret"].enabled = false - technology_list["tank"].enabled = false - technology_list["circuit-network"].enabled = true - technology_list["night-vision-equipment"].enabled = false - technology_list["solar-panel-equipment"].enabled = false - technology_list["energy-shield-equipment"].enabled = false - technology_list["battery-equipment"].enabled = false - technology_list["modular-armor"].enabled = false - technology_list["military-3"].enabled = true - technology_list["railway"].enabled = true - technology_list["automated-rail-transportation"].enabled = false - technology_list["rail-signals"].enabled = true - technology_list["gate"].enabled = true - technology_list["rocketry"].enabled = false - technology_list["battery"].researched = true - technology_list["plastics"].researched = true - technology_list["fluid-wagon"].enabled = true - technology_list["stack-inserter"].enabled = true - - - --blocked tech - technology_list["automation-3"].enabled = false - technology_list["automobilism"].enabled = false -- only from start - technology_list["explosive-rocketry"].enabled = false - technology_list["braking-force-2"].enabled = false - technology_list["cliff-explosives"].enabled = false - technology_list["construction-robotics"].enabled = false - technology_list["energy-weapons-damage-3"].enabled = false - technology_list["exoskeleton-equipment"].enabled = false - technology_list["landfill"].enabled = false - technology_list["utility-science-pack"].enabled = false - technology_list["production-science-pack"].enabled = false - technology_list["advanced-material-processing-2"].enabled = false - technology_list["personal-roboport-equipment"].enabled = false - technology_list["personal-laser-defense-equipment"].enabled = false - technology_list["nuclear-fuel-reprocessing"].enabled = false - technology_list["nuclear-power"].enabled = false - technology_list["military-4"].enabled = false - technology_list["destroyer"].enabled = false - technology_list["defender"].enabled = false - technology_list["discharge-defense-equipment"].enabled = false - technology_list["distractor"].enabled = false - technology_list["artillery"].enabled = false - technology_list["atomic-bomb"].enabled = false - technology_list["robotics"].enabled = false - technology_list["automation-3"].enabled = false - technology_list["rocket-control-unit"].enabled = false - technology_list["logistic-system"].enabled = false - technology_list["logistic-robotics"].enabled = false - technology_list["electric-engine"].enabled = false - technology_list["uranium-processing"].enabled = false - technology_list["spidertron"].enabled = false - technology_list["kovarex-enrichment-process"].enabled = false - technology_list["lubricant"].enabled = false - technology_list["low-density-structure"].enabled = false - technology_list["rocket-fuel"].enabled = false - - - - --blocked tech upgrades - technology_list["braking-force-1"].enabled = true - technology_list["energy-weapons-damage-1"].enabled = false - technology_list["energy-weapons-damage-2"].enabled = false - technology_list["energy-weapons-damage-3"].enabled = false - technology_list["inserter-capacity-bonus-1"].enabled = true - technology_list["inserter-capacity-bonus-2"].enabled = true - technology_list["inserter-capacity-bonus-3"].enabled = true - technology_list["laser-shooting-speed-1"].enabled = false - technology_list["laser-shooting-speed-2"].enabled = false - technology_list["laser-shooting-speed-3"].enabled = false - technology_list["stronger-explosives-1"].enabled = true - technology_list["stronger-explosives-2"].enabled = false - technology_list["stronger-explosives-3"].enabled = false - technology_list["physical-projectile-damage-1"].enabled = true - technology_list["physical-projectile-damage-2"].enabled = true - technology_list["physical-projectile-damage-3"].enabled = true - technology_list["physical-projectile-damage-4"].enabled = true - technology_list["physical-projectile-damage-5"].enabled = false - technology_list["refined-flammables-1"].enabled = true - technology_list["refined-flammables-2"].enabled = true - technology_list["refined-flammables-3"].enabled = false - technology_list["research-speed-1"].enabled = true - technology_list["research-speed-2"].enabled = true - technology_list["research-speed-3"].enabled = true - technology_list["research-speed-4"].enabled = false - technology_list["mining-productivity-1"].enabled = true - technology_list["mining-productivity-2"].enabled = false - technology_list["weapon-shooting-speed-1"].enabled = true - technology_list["weapon-shooting-speed-2"].enabled = true - technology_list["weapon-shooting-speed-3"].enabled = true - technology_list["weapon-shooting-speed-4"].enabled = false - technology_list["worker-robots-speed-1"].enabled = false - technology_list["worker-robots-speed-2"].enabled = false - technology_list["worker-robots-storage-1"].enabled = false - technology_list["worker-robots-storage-2"].enabled = false - technology_list["artillery-shell-range-1"].enabled = false - technology_list["artillery-shell-speed-1"].enabled = false - technology_list["follower-robot-count-1"].enabled = false - technology_list["follower-robot-count-2"].enabled = false - - - -end - - -script.on_event(defines.events.on_script_path_request_finished, function(event) - local request_data = global.path_requests[event.id] - if not request_data then - log("No request data found for ID: " .. event.id) - return - end - - local player = game.get_player(request_data) - if not player then - log("Player not found for request ID: " .. event.id) - return - end - - if event.path then - -- Path found successfully - player.print("Path found with " .. #event.path .. " waypoints") - global.paths[event.id] = event.path - log("Path found for request ID: " .. event.id) - elseif event.try_again_later then - player.print("Pathfinder is busy, try again later") - global.paths[event.id] = "busy" - log("Pathfinder busy for request ID: " .. event.id) - else - player.print("Path not found" .. serpent.block(event)) - global.paths[event.id] = "not_found" - log("Path not found for request ID: " .. event.id) - - end -end) - - -local story_table = -{ - { - { - init = function(event, story) - game.players[1].teleport({0, 0}) - game.print("Welcome players") - set_goal({"Enjoy the scenery for 5 seconds"}) - end, - condition = story_elapsed_check(5) - }, - { - action = function() - local character = game.players[1] - character.insert{name = "iron-gear-wheel", count = 47} - character.insert{name = "pistol", count = 1} - character.insert{name = "firearm-magazine", count = 1} - character.insert{name = "electronic-circuit", count = 90} - character.insert{name = "copper-cable", count = 38} - character.insert{name = "iron-plate", count = 10} - character.insert{name = "steam-engine", count = 1} - set_info() - end - }, - { - init = function(event, story) - game.print("Collect 10 iron ore") - set_goal({"goal-iron-ore"}) - end, - condition = function(event, story) - return game.players[1].get_item_count("iron-ore") >= 10 - end, - action = function(event, story) - game.print("Well done") - set_goal({"completed-iron-ore"}) - end - }, - { - init = function(event, story) - game.print("Build a stone furnace") - set_goal({"goal-build-furnace"}) - end, - condition = function(event, story) - local has_furnace = game.players[1].get_item_count("stone-furnace") >= 1 - local furnace_on_ground = game.players[1].surface.count_entities_filtered{position = game.players[1].position, radius=100, name = "stone-furnace"} >= 1 - return has_furnace or furnace_on_ground - end, - action = function(event, story) - game.print("Great! Now place the furnace on the ground.") - set_goal({"goal-place-furnace"}) - end - }, - { - init = function(event, story) - game.print("Smelt the iron ore in the furnace and collect 10 iron plates") - set_goal({"goal-smelt-and-collect-10-iron-plates"}) - end, - condition = function(event, story) - return game.players[1].get_item_count("iron-plate") >= 10 - end, - action = function(event, story) - game.print("Well done") - set_goal({"completed-iron-plate"}) - end - }, - { - init = function(event, story) - game.print("Collect 10 copper ore") - set_goal({"goal-copper-ore"}) - end, - condition = function(event, story) - return game.players[1].get_item_count("copper-ore") >= 10 - end, - action = function(event, story) - game.print("Well done") - set_goal({"completed-copper-ore"}) - end - }, - { - init = function(event, story) - game.print("Smelt the copper ore in the furnace and collect 10 copper plates") - set_goal({"goal-smelt-and-collect-10-copper-plates"}) - end, - condition = function(event, story) - return game.players[1].get_item_count("copper-plate") >= 10 - end, - action = function(event, story) - game.print("Well done") - set_goal({"completed-smelt-and-collect-10-copper-plates"}) - end - }, - { - init = function(event, story) - game.print("Craft a burner mining drill") - set_goal({"goal-craft-burner-mining-drill"}) - end, - condition = function(event, story) - return game.players[1].get_item_count("burner-mining-drill") >= 1 - end, - action = function(event, story) - game.print("Great! Now place the burner mining drill on the iron ore.") - set_goal({"completed-craft-burner-mining-drill"}) - end - }, - { - init = function(event, story) - game.print("Place the burner mining drill on the iron ore") - set_goal({"goal-place-burner-mining-drill"}) - end, - condition = function(event, story) - return game.players[1].get_item_count("burner-mining-drill") == 0 - end, - action = function(event, story) - game.print("Well done") - set_goal({"completed-place-burner-mining-drill"}) - end - }, - { - init = function(event, story) - game.print("Build an assembling machine") - set_goal({"goal-build-assembling-machine"}) - end, - condition = function(event, story) - return game.players[1].get_item_count("assembling-machine-1") >= 1 - end, - action = function(event, story) - game.print("Great! Now place the assembling machine on the ground.") - set_goal({"completed-place-assembling-machine"}) - end - }, - { - init = function(event, story) - game.print("Automate the production of iron gears") - set_goal({"goal-automate-iron-gears"}) - end, - condition = function(event, story) - return game.players[1].get_item_count("iron-gear-wheel") >= 100 - end, - action = function(event, story) - game.print("Well done") - set_goal({"completed-iron-gears"}) - end - }, - { - init = function(event, story) - game.print("Build a transport belt") - set_goal({ "goal-build-transport-belt" }) - end, - condition = function(event, story) - return game.players[1].get_item_count("transport-belt") >= 1 - end, - action = function(event, story) - game.print("Great! Now place the transport belt on the ground.") - set_goal({ "goal-place-transport-belt" }) - end - } - } -} -story_init_helpers(story_table) - - -local init = function() - global.story = story_init() - - game.map_settings.enemy_expansion.enabled = false - game.forces.enemy.evolution_factor = 0 - game.map_settings.enemy_evolution.enabled = false - --game.disable_tips_and_tricks() - -- game.players[1].force.disable_all_prototypes() -end - - -local story_events = -{ - defines.events.on_tick, - defines.events.on_entity_died, - defines.events.on_built_entity, - defines.events.on_player_mined_item, - defines.events.on_player_mined_entity, - defines.events.on_sector_scanned, - defines.events.on_entity_died, - defines.events.on_entity_damaged, - defines.events.on_player_died -} - -local function on_configuration_changed() - --game.reload_script() - init() -end - -script.on_event(story_events, function(event) - if #game.players > 0 and game.players[1].character then - check_for_player_death(event) - story_update(global.story, event) - end -end) - -script.on_event(defines.events.on_player_created, function(event) - on_player_created(event) -end) - -script.on_event(defines.events.on_player_crafted_item, check_automate_science_packs_advice) -script.on_event(defines.events.on_player_created, on_player_created) -script.on_event(defines.events.on_unit_group_finished_gathering, on_unit_group_finished_gathering) ---script.on_event(defines.events.on_gui_click, intro_screen.on_gui_click) -script.on_init(on_configuration_changed) -script.on_configuration_changed(on_configuration_changed) - - - diff --git a/fle/cluster/scenarios/default_lab_scenario/control2.lua b/fle/cluster/scenarios/default_lab_scenario/control2.lua deleted file mode 100644 index 60adec886..000000000 --- a/fle/cluster/scenarios/default_lab_scenario/control2.lua +++ /dev/null @@ -1,6 +0,0 @@ -local handler = require("event_handler") -handler.add_lib(require("freeplay")) ---handler.add_lib(require("silo-script")) ---handler.add_lib(require("paperclips")) ---require("story") ---require "story" diff --git a/fle/cluster/scenarios/default_lab_scenario/description.json b/fle/cluster/scenarios/default_lab_scenario/description.json deleted file mode 100644 index d217d9b78..000000000 --- a/fle/cluster/scenarios/default_lab_scenario/description.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "order": "a", - "multiplayer-compatible": true, - "is-main-game": true -} diff --git a/fle/cluster/scenarios/default_lab_scenario/freeplay.lua b/fle/cluster/scenarios/default_lab_scenario/freeplay.lua deleted file mode 100644 index 5afa12dfe..000000000 --- a/fle/cluster/scenarios/default_lab_scenario/freeplay.lua +++ /dev/null @@ -1,299 +0,0 @@ -local util = require("util") -local crash_site = require("crash-site") -require('story') - -local created_items = function() - return - { - ["iron-plate"] = 8, - ["wood"] = 1, - ["pistol"] = 1, - ["firearm-magazine"] = 10, - ["burner-mining-drill"] = 1, - ["stone-furnace"] = 1 - } -end - -local respawn_items = function() - return - { - ["pistol"] = 1, - ["firearm-magazine"] = 10 - } -end - -local ship_items = function() - return - { - ["firearm-magazine"] = 8 - } -end - -local debris_items = function() - return - { - ["iron-plate"] = 8 - } -end - -local ship_parts = function() - return crash_site.default_ship_parts() -end - -local chart_starting_area = function() - local r = global.chart_distance or 200 - local force = game.forces.player - local surface = game.surfaces[1] - local origin = force.get_spawn_position(surface) - force.chart(surface, {{origin.x - r, origin.y - r}, {origin.x + r, origin.y + r}}) -end - - -local on_player_created = function(event) - local player = game.get_player(event.player_index) - util.insert_safe(player, global.created_items) - - if not global.init_ran then - - --This is so that other mods and scripts have a chance to do remote calls before we do things like charting the starting area, creating the crash site, etc. - global.init_ran = true - - chart_starting_area() - - if not global.disable_crashsite then - local surface = player.surface - surface.daytime = 0.7 - crash_site.create_crash_site(surface, {-5,-6}, util.copy(global.crashed_ship_items), util.copy(global.crashed_debris_items), util.copy(global.crashed_ship_parts)) - util.remove_safe(player, global.crashed_ship_items) - util.remove_safe(player, global.crashed_debris_items) - player.get_main_inventory().sort_and_merge() - if player.character then - player.character.destructible = false - end - global.crash_site_cutscene_active = true - crash_site.create_cutscene(player, {-5, -4}) - return - end - - end - - if not global.skip_intro then - if game.is_multiplayer() then - player.print(global.custom_intro_message or {"msg-intro"}) - else - game.show_message_dialog{text = global.custom_intro_message or {"msg-intro"}} - end - end - -end - -local on_player_respawned = function(event) - local player = game.get_player(event.player_index) - util.insert_safe(player, global.respawn_items) -end - -local on_tick = function(event) - story_update(global.story, event) - game.print("tick!") -end - -local on_cutscene_waypoint_reached = function(event) - if not global.crash_site_cutscene_active then return end - if not crash_site.is_crash_site_cutscene(event) then return end - - local player = game.get_player(event.player_index) - - player.exit_cutscene() - - if not global.skip_intro then - if game.is_multiplayer() then - player.print(global.custom_intro_message or {"msg-intro"}) - else - game.show_message_dialog{text = global.custom_intro_message or {"msg-intro"}} - end - end -end - -local skip_crash_site_cutscene = function(event) - if not global.crash_site_cutscene_active then return end - if event.player_index ~= 1 then return end - local player = game.get_player(event.player_index) - if player.controller_type == defines.controllers.cutscene then - player.exit_cutscene() - end -end - -local on_cutscene_cancelled = function(event) - if not global.crash_site_cutscene_active then return end - if event.player_index ~= 1 then return end - global.crash_site_cutscene_active = nil - local player = game.get_player(event.player_index) - if player.gui.screen.skip_cutscene_label then - player.gui.screen.skip_cutscene_label.destroy() - end - if player.character then - player.character.destructible = true - end - player.zoom = 1.5 -end - -local on_player_display_refresh = function(event) - crash_site.on_player_display_refresh(event) -end - -local freeplay_interface = -{ - get_created_items = function() - return global.created_items - end, - set_created_items = function(map) - global.created_items = map or error("Remote call parameter to freeplay set created items can't be nil.") - end, - get_respawn_items = function() - return global.respawn_items - end, - set_respawn_items = function(map) - global.respawn_items = map or error("Remote call parameter to freeplay set respawn items can't be nil.") - end, - set_skip_intro = function(bool) - global.skip_intro = bool - end, - get_skip_intro = function() - return global.skip_intro - end, - set_custom_intro_message = function(message) - global.custom_intro_message = message - end, - get_custom_intro_message = function() - return global.custom_intro_message - end, - set_chart_distance = function(value) - global.chart_distance = tonumber(value) or error("Remote call parameter to freeplay set chart distance must be a number") - end, - get_disable_crashsite = function() - return global.disable_crashsite - end, - set_disable_crashsite = function(bool) - global.disable_crashsite = bool - end, - get_init_ran = function() - return global.init_ran - end, - get_ship_items = function() - return global.crashed_ship_items - end, - set_ship_items = function(map) - global.crashed_ship_items = map or error("Remote call parameter to freeplay set created items can't be nil.") - end, - get_debris_items = function() - return global.crashed_debris_items - end, - set_debris_items = function(map) - global.crashed_debris_items = map or error("Remote call parameter to freeplay set respawn items can't be nil.") - end, - get_ship_parts = function() - return global.crashed_ship_parts - end, - set_ship_parts = function(parts) - global.crashed_ship_parts = parts or error("Remote call parameter to freeplay set ship parts can't be nil.") - end -} - -if not remote.interfaces["freeplay"] then - remote.add_interface("freeplay", freeplay_interface) -end - -local is_debug = function() - local surface = game.surfaces.nauvis - local map_gen_settings = surface.map_gen_settings - return map_gen_settings.width == 50 and map_gen_settings.height == 50 -end - -local freeplay = {} - -freeplay.events = -{ - [defines.events.on_player_created] = on_player_created, - [defines.events.on_player_respawned] = on_player_respawned, - [defines.events.on_cutscene_waypoint_reached] = on_cutscene_waypoint_reached, - ["crash-site-skip-cutscene"] = skip_crash_site_cutscene, - [defines.events.on_player_display_resolution_changed] = on_player_display_refresh, - [defines.events.on_player_display_scale_changed] = on_player_display_refresh, - [defines.events.on_cutscene_cancelled] = on_cutscene_cancelled, - [defines.events.on_tick] = on_tick -} - -freeplay.on_configuration_changed = function() - global.created_items = global.created_items or created_items() - global.respawn_items = global.respawn_items or respawn_items() - global.crashed_ship_items = global.crashed_ship_items or ship_items() - global.crashed_debris_items = global.crashed_debris_items or debris_items() - global.crashed_ship_parts = global.crashed_ship_parts or ship_parts() - - if not global.init_ran then - -- migrating old saves. - global.init_ran = #game.players > 0 - end -end - -global.story_table = -{ - { - { - init = function(event, story) - game.print("Welcome players") - set_goal({"Enjoy the scenery for 5 seconds"}) - end, - condition = story_elapsed_check(5) - }, - { - init = function(event, story) - game.print("Enough relaxation, time to automate!") - set_goal("Automate it!") - --game.players[1].set_goal_description("Automate!", true) - set_info{text = "Automating things is a lot of fun, trust me"} - for k, player in pairs (game.players) do - player.insert("iron-plate") - end - end, - update = function(event, story) - if event.name == defines.events.on_player_crafted_item then - game.players[event.player_index].print({"", "You crafted a ", game.item_prototypes[event.item_stack.name].localised_name}) - end - end, - condition = function(event) - return event.name == defines.events.on_built_entity - end, - action = function(event) - game.print({"", game.players[event.player_index].name, " built a ", event.created_entity.localised_name}) - end - }, - { - init = function() - set_goal("Thats it guys!") - set_info() - end, - condition = story_elapsed_check(5) - } - } -} -story_init_helpers(global.story_table) - -freeplay.on_init = function() - global.story = story_init() - global.created_items = created_items() - global.respawn_items = respawn_items() - global.crashed_ship_items = ship_items() - global.crashed_debris_items = debris_items() - global.crashed_ship_parts = ship_parts() - - if is_debug() then - global.skip_intro = true - global.disable_crashsite = true - end - -end - -story_init_helpers(story_table) - -return freeplay diff --git a/fle/cluster/scenarios/default_lab_scenario/info.json b/fle/cluster/scenarios/default_lab_scenario/info.json deleted file mode 100644 index 19765bd50..000000000 --- a/fle/cluster/scenarios/default_lab_scenario/info.json +++ /dev/null @@ -1 +0,0 @@ -null diff --git a/fle/cluster/scenarios/default_lab_scenario/locale/en/freeplay.cfg b/fle/cluster/scenarios/default_lab_scenario/locale/en/freeplay.cfg deleted file mode 100644 index 5f466bfce..000000000 --- a/fle/cluster/scenarios/default_lab_scenario/locale/en/freeplay.cfg +++ /dev/null @@ -1,4 +0,0 @@ -msg-intro=This is the Factorio Freeplay. Your task is to launch a rocket into space. You will need to research advanced technologies in order to unlock the rocket silo. Start small, work your way up with automation, and don't forget to protect yourself from the natives. -scenario-name=Freeplay -description=Your task is to launch a rocket into space. Start from nothing, work your way up with automation, and don't forget to protect yourself from the natives.\n[font=default-bold]This is the intended way of playing Factorio.[/font] - diff --git a/fle/cluster/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg b/fle/cluster/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg deleted file mode 100644 index 2aec677fb..000000000 --- a/fle/cluster/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg +++ /dev/null @@ -1,71 +0,0 @@ -description=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. \n\n[font=default-bold]Mission Parameters:[/font] \n- New Research: Tank\n- Enemies do not evolve or expand\n -engineer-title=Engineer -msg-intro=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. -scenario-name=Mission 01 - -# Goal Parameters Configuration File -# Maps goal parameters to human-readable names - -goal-iron-ore=Collect 10 iron ore -goal-build-furnace=Build a stone furnace -goal-place-furnace=Place the stone furnace on the ground -goal-smelt-iron=Place 10 iron ore in the furnace and start smelting -goal-smelt-and-collect-10-iron-plates=Smelt and collect 10 iron plates from the furnace -goal-smelt-and-collect-10-copper-plates=Smelt and collect 10 copper plates from the furnace -completed-smelt-and-collect-10-copper-plates=Successfully smelted copper ore into copper plates -goal-completed-iron-plate=Successfully smelted iron ore into iron plates -goal-expand-factory=Expand your factory -goal-setup-assembling-machine=Set up the assembling machine to produce iron gear wheels -goal-supply-iron-plates=Supply the assembling machine with iron plates - - -goal-base=- Go to the base\n- Get into a parked vehicle -goal-build-tank=- Build a tank -goal-construct-tank=- Place tank on the ground -goal-ressearch-tank=Tank is now available in the tech tree \n- Research tank -goal-ressearch-military=- Research military -goal-restore-oil=- Restore oil production \n- Repair pumpjacks: __1__ / __2__ -goal-connect-oil=- Repair and connect pipes to the base\n- Oil must flow through the pipeline \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Kill 3 small worms nearby the pipeline -goal-destroy-spawners=- Destroy __1__ / __2__ biter nests -goal-destroy-spawners2=- Destroy __1__ / __2__ biter nests \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Roadkill the nests by tank -goal-grenades=- Construct and keep at least __1__/__2__ grenades in your inventory -goal-chemical=Chemical plant is now available for the construction \n- Build chemical plants: __1__ / __2__ -goal-train-station=Automated rail transportation is now available in the tech tree \n- Research automated rail transportation \n- Build __1__ / __2__ train stops \n\n[color=#ffe6c0][font=default-bold]OPTIONAL[/font][/color] \n- Secure supply line from abandoned bases in the west -msg-base-1=[color=orange]OUTER BASE:[/color] Anyone survived the crash? If you can hear me, our base is located [font=default-bold]NORTHEAST[/font] from your position. -msg-base-2=[color=orange]OUTER BASE:[/color] It was a blast! Those bugs can shoot the plasma really high! Too bad they just hit you. -msg-base-3=[color=orange]OUTER BASE:[/color] I can' t send people to you, our base is under attack! If you arrive unharmed, get in the [font=default-bold]VEHICLE[/font] parked inside! Then I'll send you further instructions. -msg-base-4=[color=orange]OUTER BASE:[/color] The bugs destroyed our pumpjacks and pipelines. We have no fuel supply for our flamethrower turrets. -msg-base-5=[color=orange]OUTER BASE:[/color] The oil facilities lie on [font=default-bold]NORTH[/font]. Can you take a look at this? -msg-base-6=[color=orange]OUTER BASE:[/color] In the [font=default-bold]VEHICLE IS THE EQUIPMENT[/font] for repairing the pumpjacks and pipes. -msg-base-7=[color=orange]OUTER BASE:[/color] I love the smell of running gasoline. Great job! -msg-base-8=[color=orange]OUTER BASE:[/color] I have good news and bad news. The good news is, I've assigned some robots to repair your ship. And the bad news... We need you to solve an insect problem. I think that's fair, right? -msg-base-9=[color=orange]OUTER BASE:[/color] If you want to survive in the wasteland, you should get some better equipment. Use our [font=default-bold]LABORATORIES[/font] to research weapons! \n\nIf I were you, I'd be all over it, because bugs never sleep! -msg-base-10=[color=orange]OUTER BASE:[/color] Well done! It's time to finally get rid of the bugs! As soon as you're ready, head out into the wasteland. -msg-base-11=[color=orange]OUTER BASE:[/color] [font=default-bold]DESTROY[/font] their [font=default-bold]NESTS[/font] around the base. We're not gonna let some insect terrorize us! -msg-base-12=[color=orange]OUTER BASE:[/color] Dead bug, good bug! Finally there will be some peace, but I don't want to jinx it. \n\nI would be quite happy if you could help us construct a box of [font=default-bold]GRENADES[/font]. It's in your interest too. \n\nDon't forget to research advance millitary first. -msg-base-12.1=[color=orange]OUTER BASE:[/color] Just don't throw those grenades under your feet. I've had a couple of individuals here who've... But. What a waste of words. -msg-base-12.2=[color=orange]OUTER BASE:[/color] At least you know how to make explosives. Believe me, bugs really don't like when things explode under their legs. Unfortunately, they're smarter than they look and flattened our [font=default-bold]CHEMICAL PLANT[/font] in the last attack. Will you be so kind and construct a new one? -msg-base-13=[color=orange]OUTER BASE:[/color] You're the one who crashed on this planet years ago, right? You have really bad luck for landing, ha ha. \n\nI think I can trust you. It's classified, but screw it. We're kind of researching new weapons here. At the moment, we're developing a multi-functional [font=default-bold]TANK[/font]. -msg-base-14=[color=orange]OUTER BASE:[/color] However, we are slowly running out of material here. The base is normally supplied by the train line from [font=default-bold]WESTERN STATIONS[/font]. But we lost contact a month ago. -msg-base-15=[color=orange]OUTER BASE:[/color] The bugs probably got them. Either way, it is essential to [font=default-bold]RESTORE SUPPLY LINE[/font] to the base. Make sure we have a link between us and the stations so we can continue with our research. -msg-base-16=[color=orange]OUTER BASE:[/color] Research done, now we need to [font=default-bold]CONSTRUCT THE TANK[/font]. Your automation is more efficient than ours. We'd be happy to have you take care of it. -msg-base-17=[color=orange]OUTER BASE:[/color] We need to do some tests and our good insect friends will be "happy" to help us. [font=default-bold]FILL THE TANK[/font] with the ammo of your choice and [font=default-bold]DESTROY[/font] other insect hatcheries. -msg-base-18=[color=orange]OUTER BASE:[/color] Well done! The test met our expectations. Thank you for your assistance! \n\nWe managed to fix your ship. It's not as good as new, but it'll fly to the nearest dock. Good luck, and if you accidentally fly by, I'll make sure you get to crash... I mean, land safely here. Ha, ha! -think-fall-ship=Ouch, that was a hard landing. What happened? -think-ship-members=Oh, no! My crew is dead... -think-find-base=Okay, those tracks should lead to the base, I should follow them. -think-assist=Great, now I'm a plumber... I'd better get that fuel supply going again, or it's gonna get pretty stuffy in here. -think-assist2=I should handle that vehicle with care. It would be pretty sucky if I had to run around on foot. -think-base=I should go back to base and automate it a little better. -think-base2=There are some coal and iron deposits on the northwestern tip that could be exploited. -think-weapon=Hmm, the planet was only recently discovered and already corporations are doing weapons research here? Suspicious... -think-tank=I have to admit, having a tracked armored monster by my side is quite reassuring. -think-car=With the vehicle, the disposal of nests will be easier. -think-grenade=I've never been a fan of grenades, but I don't have much choice. -think-done=Train stops are done, now we'll focus on researching the tank. - -[intro-screen] -header=Mission 01 - Return -continue=START MISSION -intro=Something hit your ship during atmospheric flight and damaged it. The emergency landing didn't go so smoothly, and while you're rescuing your crew, you hear an unknown voice in your com. \n\n[font=default-bold]Mission Objectives:[/font]\n- Find the Outer Base \n\n\n[font=default-bold]Mission Intel:[/font]\n- Follow the objectives to get new researches.\n- Scouting your surroundings can be worthwhile. -param=[font=default-bold]Mission Parameters:[/font]\n- New Research: Tank\n\nOn Difficulty Easy:\n- Enemies do not evolve or expand\n- Increased starting items & ammo\n\nOn Difficulty Normal:\n- Enemies do not evolve or expand\n- Decreased starting items & ammo\n\nOn Difficulty Hard:\n- Increased starting evolution factor\n- Enemies evolve and expand \ No newline at end of file diff --git a/fle/cluster/scenarios/default_lab_scenario/paperclips.lua b/fle/cluster/scenarios/default_lab_scenario/paperclips.lua deleted file mode 100644 index 99edfa4f4..000000000 --- a/fle/cluster/scenarios/default_lab_scenario/paperclips.lua +++ /dev/null @@ -1,81 +0,0 @@ -require("story") - -local paperclips = {} ---Init the story script -paperclips.on_init = function() - global.story = story_init(player(1)) -end - ---Register the update to all events -local on_event = function(event) - story_update(global.story, event) -end - ---Can also register to specific events, if you want to do multiple things with that event -local on_tick = function(event) - if global.story == nil then - global.story = story_init(player(1)) - end - story_update(global.story, event) - game.print("tick") - --Other things on tick can go here. -end - -paperclips.events = -{ - [defines.events.on_player_created] = on_event, - [defines.events.on_player_respawned] = on_event, - [defines.events.on_cutscene_waypoint_reached] = on_event, - ["crash-site-skip-cutscene"] = on_event, - [defines.events.on_player_display_resolution_changed] = on_event, - [defines.events.on_player_display_scale_changed] = on_event, - [defines.events.on_cutscene_cancelled] = on_event, - [defines.events.on_tick] = on_tick -} - - -story_table = -{ - { - { - init = function(event, story) - game.print("Welcome players") - set_goal({"Enjoy the scenery for 5 seconds"}) - end, - condition = story_elapsed_check(5) - }, - { - init = function(event, story) - game.print("Enough relaxation, time to automate!") - set_goal("Automate it!") - --game.players[1].set_goal_description("Automate!", true) - set_info{text = "Automating things is a lot of fun, trust me"} - for k, player in pairs (game.players) do - player.insert("iron-plate") - end - end, - update = function(event, story) - if event.name == defines.events.on_player_crafted_item then - game.players[event.player_index].print({"", "You crafted a ", game.item_prototypes[event.item_stack.name].localised_name}) - end - end, - condition = function(event) - return event.name == defines.events.on_built_entity - end, - action = function(event) - game.print({"", game.players[event.player_index].name, " built a ", event.created_entity.localised_name}) - end - }, - { - init = function() - set_goal("Thats it guys!") - set_info() - end, - condition = story_elapsed_check(5) - } - } -} - -story_init_helpers(story_table) - -return paperclips \ No newline at end of file diff --git a/fle/cluster/scenarios/default_lab_scenario/planet1.png b/fle/cluster/scenarios/default_lab_scenario/planet1.png deleted file mode 100644 index 0af9eb0eb675c5b114e8438708cea9edec97b866..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 505684 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

    enO%K;9r;rR;Je~>hN4e76XN%zxNdt~GR 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

    hNF)LTvoNN#K|iR;fyc{Tv5ee>Ioa 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

    P z>E4OK!7s2B#PR~Lz`z8!fX%=vhk5|{$68@jfL&l+y9-31Iy<6G`v*qu+Uu_8`!2d% z7l$rp@}ODehg^1)DW9(y+9iqE309Up?++rz1n1w6eR&cMyLB(U4y8{T?LH{I+xRiR z{U`x2P{nX3<`FXvR1ojd3EcicY!mk8t1cG8nA1p-y@g|Tue?nthfjb)0swGMi4?)QuDy&QFs6L)|%#T-+w=K=CqE5MDSW=TL)FpD57#%bS}(+ z(bLdaTK;cQ=~(b-(iakMo;h#3aMp2_Mi=AeM*#qn7@UQ3blNEz^9+-I?yQChS5Sa} zSOby)fG~lG8xV>>B^H$2|9Ep!0LbeJ1aL9|{4*&4kMG}qviI?RzPz9G7Um0Zlz6b^ zMgf zVoz*v8u|Uly~7KG$A|ZR6@9gPFnBL}aQx!M@9jVcaC7?b+V4*6T$_X#_z)}wbO5;i zFHh|JEOvDOb)RMWuIK9hJDusHM;)7DtmwMs7lepbZOc9o0a66Y(Oe z)m2pPjQ-B;d=Rdor~v2&_>|7BSp~OEk$L>+p{D7b|4Qca4g-k%MwZP-uhs{GJZ&&2 z52E*2P1wAaQ89i;Hm_7+R45cmwb0C}s%3MztdQSbk7c{2kzuvbX$+~Ti=mg&g)k3c z;mD!@bauU(t)(eUGlT$a>gp5;UUAiN*{nbyn*}%!8EVh_hul8D&u@_UTvQ69TGgYs z2OzhWsscVEgZ@>Tc+BrrSgHm7374r0*HT-%ZB8MUG6)`(I70jk85v;~fi^W=g`TSYS znbVH5A|{SMmEA;VGeI_D5IdlAPy;eKNDmM*Xl#-VSn&fojrPBi4NMHdD&sT%)!=pp@*X8uf??Cj!!qk#Tn*#dmK)csUv=;%F))924{4R++Y4N4XW*4Nz zk_C4Xih&^3fCzal1Y7{|QBmA^-0Y5IJZ>hCVAP+Tak|>&?mITUBovv^gBa|LOI=Zg z1grul_&&Z;Tw-wbE}{1hM@@(*(nseu4mOStir@tplDS8ZtOtks?9nT?FWMhHE=Cbv zm|Z%WfLaJ_a9bf2%Er(}qsu4Uy-RneAPhRm0r|8N3O-c{9Jqy}k3|78$x#H*16&6v zP>0`DQX;`!b6N{GKA)c=&yS4bZze0YOi3>5JIL?!@8Y;Lg!5v`AN= z5Z-t-Ik3Bo%K7W1rDNhyJUF?#d}V24aPo3#6Fb7m!H*`FE@Kp!zKn#!%ev^hg9th! z1iPrBwfXkY%B8D4b3F(p z?}G_RKZ=08Z)m6iqeKzCKzm<4{$nCV9kLm-!_WL0Dj_Wub(7*C&bUOJG55w;Kgm5udn8HTbLMpJ#Ad7 zJgPTvXw_|z8Iax)wkfayH`G|WyKD4N>4E)cZGxNw-bhx*WuG^Vg6kXIf*#M^=&y21~yn=1rx-&+EIr6kHsGG(LFJ%}cs<@OVKXpHr*;t&yoXHX1CR|`-57yC;nHwFL_DSx(ae54;lz}^q~lb|T?+}zw5JleUo zGzhnX-$h*E<-^IrrQOL%q?%lrn4H8e0H%Mm3GjffkOjaO1Ob2yV4|12ZRoT2%}rdt z*tdevwDx|2`=R(WH1`mbv!RCJn^U*z`m!zAot7cj9H9;NwA7cMT|=Fx6HN=logSG_K2 zl{Sc_V`I{zf*~k2kPe4s^#QsS>!{VLf<2&0s+6itrcnMWzl~ZC^_-B)W(e4FzNl6# zCf57N;6h+8hYD9~g)-8mkTr56oi*%QxiGGmDdTDcfKwbPm~wV>nz~rZfZ1&COXWH4 zklU4_QmPu6X1g!!cS#MtaD5eDWV5nDVUy9U1O^3}pd=C&Q0fsP)FDH>c&EApN4le< zLx%BRHrpxSc6LE~tULAo`)|Mf_WQ4G3~&=)wNbcA>=lLlT1n@usIk70#!_-?s;Nvy zlM#_2umd{RLr-hGYmc5e`aDKcNEtUoIG-0iFXA zNaV;BHqbAYV2A}jAp-wz3-I;*^&jrP{KxgZ)xw%%E%~uCmNx&#wPw&yY#k$O^!b%4Zeu z_Q>WR1)ScDFId19(QC0;(kXZW_)AWdOawQgp!&UN5TYqXHG(-G?{Uh} z_>%?zACKIfLXBu0$5Z*mDM*Av2=M6(x!j>Xv<>}W=k;JwHwD#U46B7w@8<6D@~`@L zR*rX;h{n!k;=7uo|pdZMF!~THXH5Y)EsXYf6;AxX8Y=XYS_p~s&zrOc; z?M*oRV+!cjJlId%c(DE)a-i-6KmaTFK*aMmu~$fr+Me>#F!O~WKkUO0Cs=D0k9urS zzmPONBNcC0?s|?xKEu|%0^<(rL8Uk8&6iUE7MnT@nFN-6>9nSa(grR*vgz(*uJ&CN z!&HN=F}iYBFAlZaGge{_=&P-5@AGRL^EOZk-jD-@8g+QOtrlDN>gv`F@u*6Lj&c+# zL9G_jken-*QQ7!a6be{6Y!CodqBa&WIz~qqJVM6IAt-?ImTI@%XW&!7KL`!^v9yMe)hM7K@5dijIE{LDRDlo- ziBoTqm`%S$sn@Yl?AXW@nALH|x*BVd{LMhxhXCxL#tw$&Z^`k0|EuRVBb9~m&$aMdI6QgqX9z{;4`cyz=Wp3c4|!$~!~lsOf6) z`7_Pl_@DVRj~^!T_ce-*;*iRiGDm%6+u(EO?b&!%BGp3LVP4tA;C+P6+PlNI zzy0Q;yZPH z*ki=7?7K)r01zq>UvdBew=adD2MFP$PfWyG=6c{1(idxK@9S%ahX}dHt{4 zr~rjTfnMfmhjM8XH*^Ep%l~*nI%4D)x9VEhP1xKl5H*&l51||hO ze8rI8ZFXzXmr^Jqt11Lino}r=8sknqI%H^r!zNu-jXIUW)5HygoPTovg9=CvKVbr#s!ReE z43HFp8z)*|JSQk1)&O69|1HFq<+XIOyuX@GuC3cMj)1{q!+yo)bx5SaXcU$zxe$yw zNL0ARYLu81;hC`e(f6PK^qZIQ{@;A}(@#Htn@IbU;P|A2c-%n@ZP18Ff|l^hub(ZZ z49U!GbKc|DN)w3r@Ia;o77+A4_(uj)L~!g0n(c;w(xT1B%z0}t90E7RsnmGQt`hME z3ggR_UUOUx`yYin?+lsY3t~nn9-{S&@lpv5z>A{Fd;LGk`+@wO`y2(_0SzWZl^d|b z@u3;9V|O@|<9l;H=kiB~)0;cjaLta~nplBn2ax|yNjw1Tc+vi24nU_5Z6B!xEtLj< zuzyG34K$7fA$$T2LF-%yK44%Yn)?Ru$pO-#53k${L2P_DO$yN5>~ zeRK>z;=>o{`Hx;4FOj^GBlP>rJ1aMflQ);I{mJE}8MDyYXyCGq#=YPTjykG4;0y(JK{=JCQ`n%-IzPSM9{6_Z~g7_=g(iic~&lD2x=Hj2oV#B+UOjr zPS+vhw$b>VouVfEdGJ^~nnWX#K|EmK(by!;hiw$(A79iz7mj%K?qCo-JqG+pR4-k9mV5crZyfPs!}s4I@jr?6 zh@qSehq3E-coE#-QRVf%>R#9iPEwNCyo*IDTXS7-$Uvw;#S0mLzE~ zvRSiVn@XFbdWjD#K$Rh1R45MI_9C!>Fpj%q)K~0O_z&X}y)QZxa7P1fd#t#z17RYn z1H?&XN93L4qq6_;qj%iCl?8Z~qr^z1J_))GUp88t0|e-H7MDP}`{EcE@Y3YvYlAqw z!1bHQp#vfiKI!`56pS(8?i4;4C#)ZQ?rvWzesLVgKns4_fI*-qJBMyM;1BhW-)aRi zw=yuj`Qb2pykk3r#5-|_0N>@q-#s4eJv@3r0tsGB?@pACKt(#5+{8F=bNR!GNkIJA z{UNK6u!OGQ>L(ABe=sydBQX8$yRyh1d@+pb0^lFLf9nX2Gm$c4kgtRk)!B&>prx*z z_!XlGxEdS3c>hjcDAw9QL<3L?4-8~S`s@%6qJYF2AQ}K`AQqdzC=dd_b;u?42h4NB zP9L3m0E6I&!m*8500e`925bb*2EJdR@U(m0<9M)ENF;;x8l3&T1nCYC@D2NyYE%LZ zAVKg00S90m(DtTPtz#mqhRqZ*+c-+vm;%c|73^*zfxH)CzhX}T2_4Bq6Wg?yOoMos zME*dPsGg=&gf0zLQ+Q0SK+=A7C=ZkZ57}0FRqnjh>mcAmvLFh=ykK<|M`#U6q&J?w zc>^%us79sIbVDNy_a6V^om3`WRYM`^o{d_TfZ3^MQD_ocEwX@Mi2xOl-pvtq$No%TAP5{#& zq9Xz~YSNd~kO@1nq86H*O|{h>%0?>oezMudMka?_BNKF}b@laX9e0+A`Tu=f{?BiW z&Od(>TRb&eR$HUu(R4L5wi?g_tqbwCHSA7WM?F@A5rF*NB3+Y6iJs^*A1#3FG?Px_ zoI8z3AU=42#OecVK;Bu=1v-Uv{}l}2!igau`ElY1dJ11hK)}k&i6v;I0r+1c(B9&o ze)aK_ZFg|*^TO8tS6}UIq!VirLs}0ZU^p(3thvKQb2cOe!A>Z(5&EgnSTMo-3_ z_|1=_(N}Rq$b9=_P9Jo83RlZ+ENMaYK@^zJ<+VKf?&qIi7zkU&^20ip#bDMa-DGPQ zb?f!0ri86!2*Sm}9oW#v72ae<42H0Ng*iHu%8^$p&meQ%_oda@?`*YsN>|28@iL6#z&`l)={l7fz8c!Dm}>5?kl` zeem#h^$(BS8$ety$hs~4E4?uP92j2N1g&>CcJ$HG2S;yR+dV!UK87dRWNi56!5;zP zzc)Epggg)|;G=85H#ssicp!>&9 zdiweST+B@n-g1v#Vn!MDyF#(yp_G;5(+VRS3&8}D0fP_7(PAZ&N~Z$(B;-LP^%G*^ zV7JKX@Fb8Q3|%3dBatj7o= zBemz`^W~(xoCZ!{VKfSjPy$+?7GAE+FN&%JTpmX#^;K1I!aio8ZwL>m0QRAr3&ath zpAULz0X9JV`n?D1s}CMLe@$ZRR_9kw-LNJca@_tJ5)B|V2a?Gg+^58pDhP(CIyMb9 zpCwG!VzC5VCUpAf*c)|V6;RYH zMj+)d1ThN-Q(CDn1Z!Yp)(n^WxD&*bQdAi)G8A1{SOc4mW(9CNWH8uV4xgr=)~Xkv zgJ3`!+}45A@6PjUYwv&c`oV+qqUZCP8=&%W_>wBbl67@dv#=;^WN~LY0TjTdgx16m z(Uitc-dT?DxA+pintr;2$`MdS=P$6%ojrp?&pgMmojNC`GtYLPI?Wf60`Px!0J!wY z;lDu4z$;9^iZ3V*|A`*x1Oz-$0+Iqye*E_P+fP1OUr+s+KT-a8YtIrltQtVwON4{* zu)gS#g!NFqMnKg|MZ@#KZ~&3t$nHqxja#4p_(`OglK8xZbkLIq8|cnMH}Y59=<`WW zpDyhG`18+e*eC`)x%`kFwtn%9DTKX>%8_zMF`L7_4$!Nc}Lx``2Q~6vBv3)rEE5p;h9}bsBR(4ia5dTp+ z-nkVz+_~9%e1zxn+^gfG<7=19#cPK*m(UIjUKyOewsQpJe-giwYgew|_Q&bJa*bq` zkV-HyiKYKUFX}-3!sfldoCKe-zx4~O{F-}U2mrNj&%g@W{;NGBL=S9lxK2U}hFY=+ zJcCHUg}k5%u!3WRFnkBuh3#knHiwam*leEzhA@X_pg-1Q2I6YZ2Zm4)>}CYCYiJv@ zv)S|m52%13){$|qTuug)2@U-zn*3)d3vkxbAo>f(Ha(w2HxJpb#Y(z=>!^kY_{Lh* zZ54LHm)|yzV7VY)H%8?43%W>xf?k1qVcBwEsi)#k@D~_og(KB-)YDDjt1r6-!D4m4I7_jCs%KDDY~s1AyA! zD0KV48O&vZ89x|9DkSYO1OaS~s}SQ2iw=&`EvjW{y#}~}Fl1c|U9%>g4##a~2F9Q> zV-cgghA!Z6X{;cXp<}^4TwZZ;sXT~ou#1rCnNySfXMAnFhJr5aKZy1jv&7TgWtdw073xR0iXh$bO7IeuX_FX z{&rz+f4i{0jb3l{Kmps8h#^??z_<&%|5ZyKGTZ}Gsu;%YACB$lF$KVCUG>#>Kb7?% zz0+rwZXv*9bt`#w#4OQTq(KRKv5+dcmj+MhZASw6|9Rt(ea_&HyNpK2g%cJ(bb#hu z$Z0l&K@5mGT?TC`2$Ut@49(5KgC%FQxF_N&gHjp@Ni3| zMx6S%6ER#^CMjZa?*GUEkorJO?!9Zj6dG}bhDR=jFalh>-QH(Ms6cy&bp7pp&<4Un z6y)I{!h{H?0+~GggPOq$l%%{G^#Vk4UQYo=VA5dFKvV){JcTBT4aNcc1&9XW_5(P8 z&;oIiz1mdLpHj)wRBka>F6{4@%jx^?WxN#i!a_Y)$>8YUS+w_orqE8Djgcp3U-TP-u7JgwNUnO) zwidz0aYA{M2%zsLlB13i;Jp!o=%}M1&l6PH7y>}$)ZNK z6gzgMBq){|Rm@J4QVoeROVP;%Uzb<{5zqA5h_qJ3q}KA~G$WVW*{M{R{8COHy}uvT z4HJyiAgra-$a|b{r^X$qC+0X=XB0eCpnxW$DX!L=>M8Y&Iy4Y;9()3*Y-0^ytB!Ou zHP+HO;s;w80TOSDsWo&lEQ~}(9>1CelVCQ5)(P0Lo-0BPpmhp5*r*$9CWl64(~$lp z)XnOeI%s@d7ytAHDkQ=e5Ic-b0NMbs063?+yWilwK_Q;NgawEfi~{FT4Js($DFOhJ z6<}ozP)P=?SOWjuNg@Er1^jkjE`9I5Kfm`fuv&QF!SZjxQ}C?)EogsW0jE;OaSy`6 z;&AGPF>2{B?mXIwgM({Hl;!I`eJ+J}PbnL+74q$APqHVw<97!E{p*u?Yyv%=Pk#RS z=if;L-B1(-GkrawV7Oqj0LS$i{62jOBH=jLd|_+_Qf{vy2NTh--EImyIX1gN5llrT z5#q-vg(X;QXF<{)lVFNR#t>93Q7i(AaqP0q(N~8hiNX+k^rVnX%)UB4*mOmTJ9;<1 zGlwh)OeDFm8GvmO!tT6&a~hufb%(E(HcN*$R|f8lwBkx7D&LFH`{0!je{kuOUEi%s z%_HLt<0CNlm?FGj@(Y9lKn4Uf1r4Dr-1=_8f~2Q(d8Flodo8h@3RU;;XmEI^_h6;C zfrRoG%aaE$-Z~y0IKK8`^YFzoQ~}F7lZTl6mnSd(9&o?PwLgi|e+6$zD=>&2V0w6x zm;lCWi|-aYC?+*xEF!-`iE8dy!QI|`k?4KU#*;&CZ~m8@bE4%RuG?E_-53|J;h%?Zp5V!g)g#HhYayHF)t7#DqH# zqG40IFm>-2;Hg2cCKEs+xt_)^;RD>c*7?A*|+VyQINP z%3jxQ??V<_CX;db?MOC}=kIvnPX=j$ZB5kVNRN`qV-;3F^Pc(c2MJ(+Rt-n0<*?8Q z_@OpUE(Y^=<|%De9VU~aNx(M84>~%i%z9*onle5uS0+Prh>=0(m|*@V>x?L%24t{DY4e&rX zkI*ih=kYIG;8Wf>;Q$aaz=XAsdUK`?wMsgDql(0TtG{6}IXa895J3cxrqNkO3l9sQ3`f^o|FZ21m z$#$>DAIkawEf;cLKh*y=U_5Kzp#XfIMlQQk@55Zr|#U=wHOoCiB` zy(Rtle)(BCFt!0tev->u#Do^`IEGTWy|gRp$6xX+ia>S;BHKTHXJqMc_;>G>Zr&Rn zz6%>*@Btf$#m65vkKDT3dWnF6Pyk&26&!)ClL#>gfdQ{KkP!he0eXNIypFU$l?y@f zBpf`KdRt<@f>X~D3Gl$UKVDkdIh@|bbM$+I&;Ty)669}bVE0e1K=D7hiDlo!y8sF* z>%T#~#mQf~jNSjz4vN6Qz#x#n&FSI6X#wYN{zyUL%2;ltabz4+qy84eh7l?-n7~(? z8|DB6UThxfAF8{H0R>9If4g(r9~)_~V_O&l4mf1*vy-{Me>mGRg7Xg-;WzGD#H-2~@)=H`CRpb&w7qnVGk)KYbG_0L=KF{ik?FF%ckg z5NiRcOj;DHQNun&|a3ZT$9P-fnM(p&7=2#a!NHMgwP&dy{3@^I={8QiIm&&H0lFY!_0uGgY;Mjfk4d;#70{6gsFBYiL45 z0%&D2r5bWz9B<(CL-~E<1*rd0i2e5+ z;{5M`cW3b=qu>4b674@n4s%1&VNpbf_u`GSL>1@7^EM; zpa}bt8f^hLAXHF*k+e{pQ6wg2jFuA z`a=O2nQEvs2z@yJr~^I282)b#Ob>qm&E7P?e@OqQCwE@_!Der98Z6(V|HQuU=E3we z_yG?Qo4;b~`$GCJJ` z257hk`2BG2IFdpz17Mtwk+=egm+Qta^^f%RPvN3R$DjSL|G4e%A-Mqr@@uy@^dVxH zWQlR}cVHh9YJh^U&py--ZGh654M7u-X`dU2>J==e*`*f3lTGdRK)Pl04dt|+bP^T7 zZ}H5dai5QcX!h8l<5Z!q2IdaK9b&zy zYh+`jjUg3y962<%+4gtdx!s-v3{d9^ zy*h@w5~C%17(bQm3pC5%?3aB0(y4| zM9XT00asFC@BiuBACETHvS<`2fZC7?B9eoQSW8iMf_^LjcUZ^8TR}Zt zM5ogDz*3oV5sj-OlK?zH2?2HPff z{AO=I6EMT^t1kXtxE|Lo4bC#ixvK=j~nGZJ#f zCB%cw2aeK@&%S=&<;f3^FCAk!KZ#ZQy`IgL*xdo__Xv76a;XK7KW=!~ z0izbc0fhXtLIA!<_&|glNW1{XiRnk{3Vw#9p2@)#%>Ezr#HNXH&&np``;&(Q$AcSh zfdRaW#(toP_Y()Z*U$|dEralj)!((r{0D#c_pa4Js z2skhf1bM8euhKckkq87H5{@uVGH%EQB$FZ!sG$WuWpk|~bgR`?bO( z6a?TQpniB(=*_imG}dGi@&5Cx+uKz)m~bz8j^-ab01%Q^2_+b(~-pJ8+5sxrH@D!MQ z3MFcW!ek1&a`|9B2XRYxO%+W7{fQZ*pRhA~k`p3~mlQ0esS92;;Hk}kVNByT)^J2k zat@{oHIzl2v$T#`3Y9AsKX@C@`_JX|)GD^1ng;_An1!h+Y6?5f09TmJV51DMJBVrs z(?3IOy+D_%yJ#RLR_i)CXlJQ(+PTwk73Fb|JQzLu2A$4CE+{&IQ)k5$5Rj|^fC7*T z00^*>2SQc=5CWVSg5Vv=1|dp-6FZ>F^Y#AyJKubI|Lfmb|K!2;_MhhUC;*P2)U7~1 z7qov^(I+&hBk=cz^RQ|EbipzxZl?kVQ@Lp36}?P%J5F~!aq5+|KpYq z|HEw9`xMN2#Ht~g*_<;=(NZ`(Qm+<^z>+fylMfQL5Flf|6v+-AZ@6zxsaJ)r=G8t) z(Ikga#i~VH@Vhg%pgDU`3}E33ftG2m2zx;j`OMDda8_c{{&-Tq%OE4KYerx#tXAQ^KCVSxxfal)C=1ME9fktr3f$S4y5ISk3 zeq6^p;s8GNt4kmN-@1rjI#CAKO`#8;s`&r50*?bVfAAhz)A#mlf(JaYG_ka^w6Zb1 z^y0-4Gyum}u6?j`{L%CVXu*RpBV0xWxVJQk3b6Fn;1wMAr7M^K2<{K=FF^tb3$XVJ zxWU8$0E8lR0q+h+hu?jdMOkQ#^+ND7fW1XSthMecT*?~Is=xzq3iiWwJ%4=h?odPf z^^0=|G3g`DAX#E%)`DeX))$W#l^-;yIy#)=t@f5a_z8^E#Yz;tv5jNk2+VzKgyA@; zRaBNw3+yjb4u9D_Y{|DKA#hdMv*#BlEM;z@I6*9V)-p(HGuBlgZmLu;?Yqm zO~zq{O

    Ab^(%xE(^BdPWy@*;upOY7uuqk#JyRuu9b?pFO<&jQD?k_Aq_6o*j7f z@F4`o)d0Q_t;19*XFAPSQ2+qHyYfLVSgBS=5Gccgt+lPKDr;hU&BITmF-w37h`D6c z!I8rYC~3hS^4UJ(5G}bJ3PG}7&#^Vhr4m<$UscV#^OQ2?W|`D*B7sO>Xk;lBfc|xz zB&r+KeIX&tTN> zdA0mDK1b6=r3ic-uTTK+N7g38WzYNVTDrBnN$xmx&aGSMZ0hLXW1YzqLCydcvyetr zQ2BHv&N+?G=ez-%5O|U|p#YE`fM9^+Iavc#Jb}q88UKL;o@@c%L`TG8Q z@7;g+?eAa$*h=h4{n9mW!mC2tukd2O7Y*mVQAn8)Wo zzVw(g{vW?ZEvslXEl-pMDfp_ZhgI);JAZfT1&1SJl zjS-tgFN8y?fOYkGnydktD!K2fpB8i#jVie#6~H1tA23MlC6pbrDQ}*0MwIXXu!Lud z!?yI*!Xdq!RyefaPy61j%Q zr{DVZ75@S*?f%UVhW~h_f$a9DS}^Tjzl9wjH2;5mX#_3-WGc9eg+PCW9o+v9cgG1E z7{cH2iu%6=2NWLTm;>;@%crF@F)_HbGP1jL_@BRj^y`n1MYw$VV0r1I7t2SW1MME( zoP;m&y(K{Y(**OwIluf1^pCy(HQ@3zXh2{GfmJX~JOFx#2>@!q7hjb4IM4mP{m3T( zXxZP_ez9+E1fKyi(bIZ8munzQ;J&({k?ZZPQ|+h?h!g=rnBANk3JSPgaRxWq`YRWW zEp>C%QkP$wzcrBM4)=o#WTsX(b|@r~tXsm@`nCQ$IWz#k3}Zw2Ou?2w$`BrFx5oR_ z;X%~THuT(9aJN7UAZY=PltF6%rVD+bTq@@RUjyPdtCo#*b#NKNMxKr)k#J^b*fl)b zj2p?qqX}WB9+saF0c8FI{8Moi*PQv`QCS)eNNd|zESU~Fa10GhV~;%$mTS$Hz#=@P zEFJ`rCXh__yxy~oEySty#H#1{gSF=*r$DO}a#R>YRTj_p&(bjRS_B9sbqm{r8aVy1 z{I{_YpwJ$kmW7oDjsn?#LM{hoMyXhcm>PvLMOT~&zb&J3MkeT-L3Nm2cr_-dg4$mS zE+GvRU0kv?q9#6@gDo~Fl<=LHGZO?WswlEbB@lw*M5Z1wK@I>k8Z`c@8Z<$W0a4Wq zs!1l$aXT969Gjf=;474X*RSP_t}z|>J?xH}Djr|iR@J6w%*q7p+Acw3r=VWLw~BfE zQ&k+Brd!h1Ifep2CHMg9Bd1ZsqB0Z0v*V}1wOC4_QVp5bOEFSoM3-*AKfwULeR%)9C!ameeX{oP_S#xP8CXjjwj`#w)*%fhQqbs> z!f}L!A4-Nc=M*sAikc6L(Aen>$Orqg-}#*%zgsKizyJBCFG1RO=N{ew?@5DbAYPRtisCK8bToj$YN-vF8A00g29h=N@M?afe(gS=Kj^4ibpBLZz;oe98dOWdv^wx_3^U~#eBhx_bSMH7f3g?-4 z1C7H50PVn^Pu&H`fA`j}2p;&yqz<%B-MTpUFD)nt*aG&q5EKwi00#Aymf_eiq6lu@ z-0U469t0uyk9MXHjzIZ+fin*ya4i0g4wokmA% znafuurUx%iUY;IA128}oL0`NJ7+?@WAW{GnX4IetG$%tX{dS0fK?H;k0MZHCR~nlC z{O zIndwWQYoGNG3-<7fq@yHWw1CQIj0Tp0QdGHS0j&f4eZ=7yw9HlAIO#f-8+eO5f0*L zDz7CfJgE(jG$?Ls!~Hpn*DZFWB(yyK+5Tw3{|0LjoZ^>jX$G1CR)Vs@;fL(K*OPfe@m)5SE7h+5CAmA8qoWsqtNp^)0FhlXtq zb)Xn6g^l{SnM75f04O5FpjI6>nbcHEil4v1F2sol(HHrG~?FWUp;@W zA)}G5nyOe}*S}HIEvjeZ{F4zt#sytSbc)ZkYA$q(En=Rtu>fK~oxejs6~Hi@$6&)c z3`T(>q>Qr=DFR4mFKISstQ+6Sas9DR3m zXXg;MNQ$&Jv9-1L-A~^=d5L58{llo)Wk#>>NIMGKWiNJp0jECTS38Vat;D*Ove+D} zUSAUF|KUK%tjGB;`b#0A1%{wnn^Qg5viOkP)UBCcOD<*=B~?)GhEP!J_G;a3V#5^0 zHV=y5cxh)2ZCBV8ig#_y9q(+$^P_3s?Zw5y`lJ7T@1^(<4y1Z1VJ^s5fBYH0GZcXz zzfWdxx2i5?s+ zgK)U?R%wY$`pa(}Lg6!sf&a?2%Wo~=?G>E$2{ipTFQX((U!EZN-*hiNg%9xBi^I=C z+XL4jngR>}GNNF>$L$W=vVL6u6~|&Q6yLF<<*#eM`{`$kuAUKK1hJkL(kZYoP|x-B z65s&e+cMnWu+jj>F!+b}>$M!`2!Mnvk_|7}VI=Mvnwyi#4N^WQcr_G?WutSh=G*O9 zfg#s513jk{4a&8TMFf15AGp`a@moxB=EmBkw`l%2|5*^AI{MNGJ&}|=z1}& z#>Wbzt{2pHz*o?y zhNZq7(n3?l;t8e`ya%4G2iw2hxWASbC%WkzHkB5EnY0aN#Nt&fEJmNNCZGn-mCJOB z*AxW;5iYXEW}SC3^v@pCDO{5|%904fJ|jFhSR56H!xSpi3Y0uAz0IhQ0S82-(&_kg zyfCRWIyARb6;sDk8_hnw&zpCF>Yjy;gdA0T}|L4Z0!$v%{$C#0r20 z-k?T&1L}b8nr`4JbR>mxn?&eBcu)p&u@T_&7(^t*ry>^N!Wl7-4ogrz9Zd=wARrL~ zoLv7EGB7B>WD`)K0-Ygiz!Ndx|Du5ZHv)YB^1aWVy!XSmtE+qO7t$V(bU$8|SR@N_ z30VK}XgIJDG?-im8_tz%2)8g855*mG!RdAcZH2ej*1!Dr>#x84;hXZa_F}*n4;V0v zAxoz0mrC&6=Az%`^?EE3quF4?z9tPuWf;C8@DzuLKZ^acGXx4yJZ#XaMm=yB4KXw~ z9=LOGT~*n^6B*q4} ztVc)KP>imcOJUX1&wnOc2q2ZA92$XdNG(} z0$>AxA-`e}2$!&Na@P;wfHf0KP(lYrSRlFg32y)!f&by^T7%L+yR&OH8(0V-%VHE% zL{PkN5k)~E1_1#T5CkDm!Amp-HFYCNlEisLNMH`bQ`I5TLs&TTlu^UX9wi#z? zo0v}OWV_p>J73yPbtaSPpWXi2>7PC?Zu?D(ikE2ilDy|V=Q+;>_pf0#4g;`w8}Szo z^c#j&5K25SU;qrTwK4#_AJ!i`_qJA6n@@M|b)O`FKf3;rlVAaNW8R2;5eLA%Nd` z!h*O^M2|xUFxp&DiR0hY9B32h{7pFZfGDeUm{vdq!fuiLU=_B3WQj>ag+>ELW2Z{j z6tGT1+XP~uLqQ#bjeyTrDo`6Wov{#X98;L|LH^msj2#JrlMxCJd^ z_DB-W;ypAibool;?PIJ)*dATV&1Z0FrKe7Pcj|aeS(mv3&Z0>Di>2msN{aI+8LTX( zW+@Wcm>P@0!uugIJfIGCNGYPS?0l%@DO8FSaoZ5?T2jeKm?-HU%nxU8M}}#PTn3X` zQ^KtGdr+Br8c>H!4Yxzv*eNC_wHX-{Nm5IZl%LI;Q*fX-n3%enpUF;*`jm9JkCBr_ zr{pPR`27LW%K%suGP_P4KZSY!9Hclj;A;5|90Igua+SVvGH$9Yh*zH;!uw! z6x1K!*iZtPF$mPkvDGfk>kybr)i!2^EjuHZUYFZZaSrt)BMUl08bhL_+O&@~6@~Y; z88lk1ka`LWNH~%g*JR|@WS5CJ)RMfK;u8EbHPU=Ie&-|ntAWM=mkM1$?m-rs6v-*D z{@4`mhZGTq3&PStoP1;h(KrYd<& z9~gP8rQitma{cu@SpYhMp_EwI;n382Zo_FOEQiFwY14SDFVv%Ls;arJO&qT~Z*1W? z;k@Z~=-HQlm`WxhlPZ@d40W%|Ij6yxFZ8%9qfVDmAar&quoz5+Z9{kV``_UF`>_cii9bRGfMrKY2GR#ls|Y#3I6@+^yw)u=*%gprU=7$M_tnZRemSTE z5cVKW04|NKW{hB?{_uF@GCKsH`Ipx=p7(6-&B^TJ5qGOu82p=`{`2kgpK$gYZ=n|; z-N4HquP*2i%lGbli@AAZb6~j@T%Sk$qBh+3gBRNP1q25yfC2zbJ`n_3L8eDjfYzV% z2vubLw?6>jaN#}rf#&#ZRaIOEzpe{KE6DloUqSr;NsRn6m)>;GfaDMFzmspycW<_! zVQhyFSw0NDLuFlcFQ8EG+qO@!eBZ-H!9m%K2J5#T9s$9Plq z2}=`>Fdxf5eo=L&u>gGFf+Ax<^<@ikL-8pv0?31a{VC;5avf?_Yby$ePS;%29B6JL z`XC5`AP#QS6`?AC#sp=dJ5W?Ls#1+kLnvr9#wryIwS3fT3?K?mnzFc3h-xD6-|dIV zh%4oA$!#(hzTgN2@iq=}uf~Klm=IR{kf(jp@X@fAU0YgQmxqNLF0C@~3sDj3yGru? z0r#^9H&7`;+sD|aikY6w5)bsJsf~$=wZ)C#!<4B9JR=~@i1^Mq#wr9EUrZ~*F%`2R z1{De+`Ul07nHwHw0Y?|*0k_M`tCwug2Or;l7RiSC(2+`v!3~_7LCdUhNSU>82wPZ^ zrii1!Vp%rbGi?#d@~N4K@Wj4B%-~@TP%=4n3~D(TP~`mAW#@C)GB#9sEH0ZVJa&wu zbSse}BGoMoGI~4(!$kv_G6MQM@I_7GqpVJ3&N5!G5~+p)z_846)+dQ zJ*A{PtL_v%mwp?|K{ys3%a@9avx+O~i&>!l(x^3=Wn6APlMZEYCR1OQ$vp_VFDL(K z&SAC$)*{8+vSJpuxW25ImoK4y2zmel0e}^0rzhLDlh+;u@n5I;I#Mdomn$^ot~rMdDZZ{Q zTWIC|bO0Q=5_`p@d)P#MFQ&6)=1)E;zYK zoMs1{e|tb4(U@*WVu}gA)7t1ZmzK-bx>YkQ_1u25P>-DOa8M(jpVu>-iG;&#u62aW zShpg}s>`f#ILOF_KyfQt6G1J3VAaGOtZ5ZafU8}mpj)L!w)CxM57sjyn=kz0pezCJ zTTl4M|NIGoqbL9yWD@uZ=l{`0!EAKI-5uTg%)1xgj2fz&`VAL|!0*CMwEX5ULEMU*_15Le^O}N+tYK5-J zfb$)|`6qh8Kob!YLkmnYOB+>0JPbk*ECP@yhI|2)h5Q1)6RH6)2V{!!X_yY^%wC^f zfuLV_^KoGp^4+d1E^nnS>l*X>4I>>ekim?G40uHDnfwT!Op~ zbGb6RrmjTFWl><132{y_olT)5NY_?Q)#tLIHUf;F!vgP+@oat`8UZekg6opa)H@c} z^d@PhHfEyco%OlDh0RbTwwAIV%cju0E(@&?6FFhgQUJm;v|36zQz_|yEZ|ff9q|BY z{`GPumVy;obSyDzau`Wrd96TTRJj~Jx7DdMo1sPl4B#^p8~`i;Vnqwn z*j!Fm%gZxz^MEM83y4x*N3E!X?x3*lR0X{TUcVB^19K=m5qv)YnX-!O3Q;5w@R`M> zN%C;Pn{tkQa70Sni(oC9e~1Jc)FHzpw?218M!xpplEouB4L zmoN_+%Z`q)THt@-0mk96?7bP)xdx{NbN)T~w5&%foY&7kvtRqW-+C5ud#|b`pz}I9 z;NR)Bsiu8)c>eeV4&(tkof3}|>A_vn`yPQ%Y)_a%x>cdMQl%D1G%npLKm)nY7STF?5t>bV!2b1NNW=|7)} zVU$emF3oRWI~sfOX0yxhak_k(kn;!9^y77X3D0RfaJ_luUi9>>m+?1)doyjDLl+4|5xo>r76Y~G))=6Uj0qOC~%Dt_T^IHR_0Rwb*4`B8mLJjC9OMroP z!T}xvApj42XlNNh5daB*1cKtlT#nx`D?9MVC`N{Obu|I~V}V2xpN(TEX)S_b0A_}!Q8Wl>40NO+P$9Xjv$Il__6+AwrULQm zMuHktl6)cZOOWE}NY+VfK!G0L5p!r<5W+d^c38QBfaw|6pjq>HA|ewcI6`??7?yrC zJ}>3*hKG9^8njZlbq=$o!zOgVEF@a3*~fA?S;DDEkj5xagc-&4iP(Q2ZYY^rn(Tq2cc#ZougS`9V3u(? zY$}(QUsfjN3QJ2-ryvW0Jg|;N%V$evs5VqOdE`klDOyMA*$%;UqXogjZa3m7oaH_r znOjHIW^(jP@ma0Sr+`a<8t{P4M`xD$7{CB(Anz-Ow_qLA1`r))<)1sp%^@M8^t_xJ zsuJO-=O|DeS5(yGNOEgfum#BG=FyZ|E>gY^Xq0peKO9=_2Mit(1wa!b4rBBzv{YGi zAf;I}35O#A4UkJMqf*c1f1JflGXcpW5RW5xMF}{R1^|g1kR$}B-N4dCfJ^|tr~y#} z{^_UPA5j3dA6TwExCX>Y@k8Nb_C#FZAGmDBRsYHy`(e=M_n`q?MQ_!S@>^{h zrN-eCbd)+F4O)4>xk}~%c`{^Q+TGZIUhUc8uf0$P&I#~uq7a0Q?xmmhTmPT;D*>8- zM@5V4ML+!%54`y67Tuk@e{!?vQB^Cle$e%k)}Len5GUX&P=1~^$EyJcTzE7%SXl*^ zP~?LV{x9x)9l883_kmNyXm?2Qb}HZS9t z6BytODF6d#2L{N!4=upS)5H#-z1@HY;3VF#3LI)BQov@y0{H4_brG;KfjUri9eoOt zO>s^U0zxiwKKL?Ct&jpB2wa7*P}M|bqm>_M?$fEMC~9CS8Hj!mF5!g#+genO^WUr^ zp<%>EXsi+4K>`2KlB;sGs!?TaL|>5pdS@p@I4UDl!bL_80>!jb_%lK#7qGstQ0%k8 zhZBpPa4;E8aPkBSF-st9;0@#PNrTj+FqvdB50Aqmru~#4s0D?vv4t!j`uHyYovdN) zyn^YG<#v>(98TG&3~pV#+Z&6Zp)KL`lUOiQ8FO>Bv=$gIh?|L+Mh~f9L^^-}PzD%= zIV?cm5En^m^NQX;L}|;ABY|M??idUnytR*QXZ%StV3fAx%m}#9$WEg4uL>vHTc|CH9rp z%FQZ^8~BLXYqLSZCcyH_izWaYK_6OCn^Fl*fDc@NQn1atd=wuA+#>)D%DkN7&(GD= zKp9Y4n02Zm0~fuNN2S*1RLH3rv|1{in@_2zE2LM{6iVu9ay~qSD3QbY`9~NsI#540 z;=e!S06(M?asX^Xa;bGCsH#l-TsiGy1PH=J6vm_o665BUWB>q6hk}qT0C`E10n=O{ zydxt3=>mrF5!Qg|0`L#}TY$H>c7Is^&s$$dVqZQ8di=&NkGU(Xzi-#S=#XLBdGGg_ zgK7k7j1~b4hXt2OE7FQ&dN(uj`fq-EaARrvna0r}bU8f$hfo=zFt^(u!UGhFBju?cTxaEfGlr(b4Nn+v>P2l^ZuqfH!%1*5=5cW;39nB> zpns1hEJWGHpQ7d=d|?zWs|4yQ84Zm;vF3WLHR(0suu$fZHZ%+m>t*`;e|RXHfrEyq zD@nal)N!tEP9zGk%Mi*{8;%KtwE?Gsm;KnDT*CPW8E9!6??QH|Q(f*=3J}R>nM?$+ zRGv=-lh2+V%Fn8xB4UeLs?3v=mlo3*xn*J{gP9D{SS$`xkw{HWB4CT5L2QOJ1o^~S zC)K&Jv4%Zn@YN^ztWst!MTEf_&>Cd66mAJkM4`cS%T>sd6vM#p^Rj#(a8e!)KD`3| zg-l6Bv83!6gB=#uUvoh;T;XV$1LRL<3UG6bzM_n## zeaVf^VV56*qY4#AyYAy`I0sfxX>du#e?A>u0BBYm4x$Cwzz9BU;2q&M(7D`$#1JIS z1KO7Y!V!r0fE*?yKvDtloE`zvFJuJxjUU**F5X&SdikF>9&E?Hyy5r5^3)7cgV6Cf z*l|0#N|?1~Ka@Pv;>3l)L3qiTG)(;`!#yIGCi#1R_|rcj#CcmIQaHfb6nbE{h4coH z2i#o<`|>0)Q>Ql~%t@Mt^;Y;+0SFjHw(xoobf(Ju3}K(8n-%|ijOhkf45O+_Be zRDo8nr3{A#f;@O& zrGF1XfbM?e17Q@H!JyxdAdr#nGc7AKXUOnB0KM-DO2H7Z0fQ(2h#<~CKi@gy4F7Qe84@yr#f$s9R1xBYTEouW4Ib9^~ zR9Qe3no4xGwY63cS|=xskBpsGej>q#btoo)zgP;56*R*$v~p^3QPsOZx`@Wgsm=)u z6Hw!-I!VSTANIWpA&)Dzdps^ZB7sPZn?gxkon5eMP*~Jhiz^XO2i%W0+|%QL{7Yd| z$fUI4hMu67^Z3g@J&mh<7~&Cg&(xY+t`1DmN?aC^?X}&P8|zmQ z1B~N}rGUdBD)c#k^mr0e@Bl$ZY1(Nnd1L&O$7QU~5$v6Jyj&@wGfHwql5$uNQdlgu zcyekg6{2OZsbU@ujte2{@>ptPDKvo?!yG2C1Ty(cd0@{Xv5P}tBFvjsh_oG=*i(k% z3D=@*8ZDb8qQEy0aUN9k|8&go8CXm@YvEXLRl}UfMp8nY>BDcEc9O6VWvvru9)-|p z1LP?1=>+D$F@ev=%*do+VSn!PT*^5h35fd0q~$7^r^;%mpC7N!$t$O1!Q&IFK*T|3 zR~#=nMyWks0b$PvWi&C3DmzL$2R1=*T~-q`oZxVZ+&DEs3wXZ1o4xIfaZ8#yzfF)U*JMfb1M#1ybtDo z@&2ldZAHz0RMpn1BLPDw4J5XI2+w^11Ly@f|69>`v{|6f?S?^fp$iIspKTsZ<4>Q=N^*O5lO+sj&$J@`duX;gm?NZwT5QA-l^d z7Ab<@-oSxJY_rMB#hiSe77f2rCW5siTj&rqXoq=PrS#~b@Bc0Xohz$BQ~q5}nR4}b zE{BEa-fXHf97+Z~-cos!Q@Zi&*^O=Ff2J1K|6`Hl0B=Bjm=NbsWL>3lr+P90l?i;w z!2Q9Z|G}Tlf1Ob$ua`h;SX^34&oAULOO#=H!Mq!?b{*v@g-2=-@VM-Mkc=5bi` z)nrpCB41ZW2aJ@*rt-s$3G4&aIQep?Lhgh~xJaNbx7EUp0RAB5PKmH~G>_$ns}(&1 zKp^Z#v*g9OnGCiJ%>4!qAd6fo^pPb4O~ z=&vNJq9QlzP<}RtlV5`+0md9IDwzbk6=(z5A0rQhLCeRMB$vj`t*OW$u|TBzComvR zKDGe=;|8{$4TujuNizcf{1-9cFFc^jx7P3f`7dD!yqNmKx38X=k}j~eV@Z3=4__eIcLe8? z8j|_vbQ^U}jV1x=@+5~H|)2cEp0!(jSbU8pz^Y= zs|iT+E3|=e|`N&RDu0=;K#-87KHD|;Q@d<6KWrF;)w*9Q2qKa zY!l4w1iuJ}9|>ZHn{9Dy1PpOp_f>60h5)dB&<8=YZyUsq$BX)x$$`Dw+}Aof)JzHh zEdI8ZhYYLD{a>`S-vjVJfIVM#H^KfkqoDny>A$%3ap@C#Aiw}424sK{4lw?C*eHy~ zG4HoMt*UOt=MeI;%L+@B5=OE2QDr4+0zi8#0xP|Q59nvG@~G2jQTQljw$3~i#}w;? ztd?P|yb6|GXAB*Er>ilPij7qw>t-r8H4#EE&;*XRl}Lxe3ze7*Iy(zGdnb*(v8l1K z$rzGFRe0Zt0B?J~BjvETxO&)kCPCR2X?mp69<4(xW#-8Q0_1XunJ``qgAQoZ=wS^c zg1gW-m-qPgm)Q+c2;3XWPnFRWU38kv$(7}mF;lO%x06&@>s$4aSKAM6CsWV|xfY*1 zTH07orPdJ;G9}W~mUe+v?oOl-CJGWlgt&nww;w+^n4|2#&JI1hQj~*so}pw@fQhq} zN{MjnYATt{%U~f*mnY&;Q)^2mbQ2Udg9S}pNj?|aTCn@*xkq{8JgDtz5VZ+q9wv@l z7GO0@7m()AnCR4TJn6-y6<{SZl-Y&U8jT+-E{noq5ks2@blHpk5?38$6E?S^+y+!o zj@!vMU8|r_%xbFNRxXBBXg)$h=veVlS=kIaGnD(o0nh`GHQq`0Zs-?M7j+ z)#NwY5K;yExrxf&69B4>qh9A|{T=fQuMGg8RW0vOo3VPbM_^Iq6hh-?2KriLhCz`V ze{mFi&17geh2Z*Y&&D?*({r7j$4_-(w(uJf>1Goo-&X31PCOwP!0yW*U+$6ukgf*{ zCtAAWgKg-r4e|8lj}ma=5uaFoVchQ<1kCqG1x2lY9B;)X4bX2ijzZCF=tKK$h~v1= z5)*)x3!_-s{cd$&0Mec4a%+5~wRJXtNMAS(ueSdQ&Nw3fp!!*9A0T^v;{0(wil;L} zXW9`4l9mCW_lJ@gV1S|BfR+F-A|6#_oG?`3K|ZfCJZ&@jj@3!%$0dW8q45DJ7)Axv z`~?47?<$K87mC`p(V|Wx_Id2`PE#UL$$#>MmzY|cm{3h62p&+)N51A&m62ayH1?{D zC>&rD_;f{`5EoodfKD(LYiwMA5;w6hwlEP>jj7NejE!AgT$?J)fr*c;7NR@&gF+NA z+yLnhTd0w-8)(c@8iiqVPr*$@sC2r-Jdueb)eHjz(B2-upTU*XmDFbx;yrtk zAcacck*LL=y6z`FZ<%(gZ&c zdnzb3jc3Dih@MvgYJD~rD47T|G00NFa;60K0ujYxW+og)Gjn0c!RFP`SyT$GL=01- z+;fG6V&d~%OU)@n<6mE!lfmIgY55I8K`l6W7_d34d_4t5L4euM6%#6N9uNmc1|=7J zjI;hxM1^;{u?uh|h&6eEJB||h+fmFb_wyLOdA9OHn~gykQ?4WWl9DEVSY3v zvxZg9D$B}cXLBHvtS_r5t)YW~RF{=oT6{b+1F1n}h(loESBTe_R4QR}=vlO4a04Wi zkEQg3G`fU_mCE6K+JTR`dM=C0fe|lyEv7tibU>X0T8Q%&yv7<%>g*^fWH4&iQ3CW2OHn6&!ynxMn!@sT1K_9>7M z=7F;_u~T+nFDP)2&dD`U)!poCYR3BA>8*4d&2C$`x3%wbHAL)Quzv5n0P5HwS3plN zH|LcLWnw^pj_@RGEZr!-qe!=M$%3hgSZ$R+?l8}pp1)cPCbvT;I@R^{`1d~RHKW+f znFZmsg{iTE_ySn~yd0Yfe7ipa;ECTF#iiSP*ASn@l#k8dU?u-A+KMg{6d+9uzHs@( zXv=5Dl@$~KzS-7N#8>Y2ui%%^(&J^oFgqBS1&x1Xa|js! za`g0!0c*hXE0Fo&#>av02JnB9>;Ss){b&m?{}1&OBhc2?c6>O5B7jF5jy+I501S9; zBf3MPMO1(}S0^x6U2ZG_Mu8B}>VkyoFPJ%5mJ@9>wyv_$(&^;8!$=Re8UwoCN0X`v zOXG>om=2U5T{VJC8jU~#2QAYG4fNrEG+*UyGr~W#>I51A%YxMi8;(Z{Mm|=a1rX!$ z$?;#D7@MMH9J>8bBktizZ{O}Ar=N#QzFdf#K%^8}Ok#!2saA>|PNh(U{Qz=0wMS?i z_;c%R$MPLA)^S!1fUZIsh&A(QTOVtWd_Nzy7#&QdCy|^F&Ie5%kw^$4@Y+T!G`TUc zHo3SsNnj<4$YqVqgV}%MS~4|$JNW9FJ*kPTCp_ zQQ6`De6H0Ddxl!LhC1c$#NxzdK7T3=0KQ!7U%C1VB55CGI{W(UT*y{hLGaq4xFxxPFq;-%UCILixes zAHV>690hkL3WHU$z#FVQVtY)*0_<}dJ{>AUA zTU)^#C_2$-fuR8KKL7#=6@O|f#)sK(W90&tffHj_#}*f-7N@isoZHttJ>$dL;U3KQ zQt*A1rAmodCRDf+jo^OE@Dj!rNLn~<0uca;9|(15+>bO&?z{$i1o&z&p?5JwZhUg= zL)<~*!KKblvDgP3NrM0=jZ7Q{pKxk&YCD-sPOc#*9O$E~A%mvyO{VPINUA_+={!h; z^H3zfmt3ni<&;nyQG8-!VsU9|a_zzWrR_)xZUr||p(jh+ zjJmTO#h-H-a!PS+eW{?ZE>GS8x=ywT+cqrxcvK#GX%Xufmn|vA`VTL#GhhP^U{NI5 z$m?;qKKD<<VgM+Q(!@YD7<5S< zoeQw?kXXsdt0*hymQXpQ?n$^b#*PNBvLQtg{^ zliy^Lj?W3<$!m4N%+rH>KX?a*T$b(0WXQ8kZqx11jbIqLlJYs&`MPY#kMID%7tG%_ zVK3aH33mDHA=m=;5Zb5(t_kL8;}=h7zldL`x;S{TjbLwW@s*1g!N$FLcd$(@hsR*o z>YF<bLve%8heH0>{p}@%9)_%1Avm?zbZLI{V!%t#xkFOY3;{5>h zTcfz!H(R17+u=CSkIi3n7MDKGJ)!>*3J@-Qd>fhkaoWi;fPjF61lU5}h*1!(f3z5e zW`ZhW6d(uCb$kBp7zd>OO6K2K6#EWLg0li|aMKl*6gG#}guhpETAL^iAzHIR+QQUCy_ zmex=KQWF~+V}L29>^vTfeq0G8h;G|W;Hf_dzIrfkYA^-0b}@&6H617d4L#hPoLmYr zhlMcgtC55!m(~_17coqLV?43Ae(T0|1pg_SS$AOZ$&|24DfM~ByUeug(o(fT$jB}P zh|b|^u@IEz(0DZL2=H$TJCoyS0Dz8KP*}q7HrlXpqGvJOB&m8u6YD9H#&g1d7oeMxF;;r&TaYCt{%HHl2im?6%`IT!$G}X z_QPN9ZvR41_H*j}5Bv+sUv8Oapf! ze)q!cS2x=S0lih3aSE63w)MtGtE+D=J8LaQuNO=obDDDP@~Y`lp&PF3+bO5t4E;~n z8>a@$|BXqDvB28bJP-$^-YgV3#IZ;4lAiV>njU6j5NEo;AZXq!TDrBn^yC&UeUyNq zp|-0(zFc|V9v$g!SsrM)7{9pO*H=VXfYrT^i0fC?1zf?zoOkffyY|mE-~aX=6n((# z(c%NFj|Xr*(@)C=B=#)hIgSkPXbXb<`oZ?Oi>n-)|9->DY|BXW{7Uz)alR4zLqq|v z{=m7%<=>t@@dOFPwcn3pPd0$~C=CJP@RJY#Fn!1>-_TZ#2jLpDRnZIKTu}?OzYQE8 zREVa6sTAu3miWX#pC%}p{bRlgKc~a@7WML*RK_`^2wlcF01aW=3F9as5rf`GUfZlh zFi@p})=|~lIXMRF@XpFke&xj2#KKj4w1Cq;F>&?D;v~*Lfa;O&6VPxm$XyGkN9D<7h37`vr6%QOe&XylL z2HPEna}>KjwYAX&05u^L84+@?uz4+R0di~74IDht7#;L{PzI%JUK!^@Dh+!=P6jAI1Og;Ppd*9^L_7h= z)Q=}@0O1IT2Pp!7`UyGz|0@RkgC|dRUq0D=`+OyMgOp=;CZG1Ok9XtHvFu)hF#J0GI(3kdP@9 zAdxCi=3(Ng^vrejjNeZ7AXwzAJKXq37p^bgY@NM%_hOaYZ1`%l?`G9y3Ft!A3PE6c z)a#XNyk_0>TrG^lWkDB+g?2d9x)M$_8Poa@SRZQ>F^g9hZ<#T)d^WTa@XHYZsIoQ> zwb!4zgAoF7p?3v*yn8d9j~171O|59GTsc(j{tyR|Mz}d z)%I0i^F?I*;6M*X$u58I-8b*{_C^rz8$Agb-|Q$B0C)Szl0IM{hJbfRR+ifgLxlY^ z5JgonL{Dx43jiDNG%Wtk&;0u28H9d((R~k>KaM>)?YQ*`;|J2fk<%wJ|6>?<3Lszv zUo|w7gH4D=82wd62>nJ8Xf^=$52#1WYf8$MSzZh*RisJys*hjfOK2Jw7A!G1R;hZ8 za0~=fFhFWR>)%5nFio<7R8R|5s+!__a08~H0McQZIU0bGkgl(-z*vP?l&PsofP#Ds z0*#$xo&16cyl*UkG2hzS)y1VrS_T*Hnq@@@n-)*w@ZEg1R^jmV_QEIx$-5KL`6 zTZ(9d8=?7(V)xqOGXz5|zI}zGpITaj?ZD(3T+Ok|%s@Ip?eTNix|IUnr)1!fNppyh z{4dBqhTLGT7BTx+6EMmptmEa!i)+%~QA3cxa~*1K2EYb^Z`ACA|8FPufCvf!#^n^d zKv)IeO(s*S)esGX7$Nq_00#JMHh>s1A(N3=ckUc3xA0U&aaJy~xP*$cf9_n}cUjo* zW61~g0oVX_RD=K=!yG^alr%14OF1w^fLfX-$zubB1USK#P&mxP44LMrl*iyvcsw41 zf*rvJM-UuzFqd+ufy>io9NZ@Y5+xAcPz0a_Bw~Og|FZ+Y^$#5I|L}mY2KdXX@B_jX z`F!a?#N&|9eQCL^c&z9E00*<}7jv?XE}>#v@yX-yj@rkeH8*G(_W4vOA@rn70`=?E z#!uTY?R3i&^Li5qqFtg$cpi(}kOK@L4H%@J5WrL90Cosva88E7A7rHaJlS`ezL1~2EqIRv-9&N6DYf@N+4Wk?7+P*cJylfi>D)@k%&C2&WTT)PE? z+Y*{+tb+Vrw>cY#_BZ(&Ez>S57_Y+b-fW_h+%b=??3|govNsc7zItnF=<@FF%Ue}j zXRiEp_dRIzFu%9X4z*kcz((Bu+W`LewYFXCH(WHVL=COWqbq0k?!AAv_5QE7_IA!} zos1$!XnD3DuAc_5fZ~YVgdI@p>`4Ded=^H3AoPRY*N*A_ZvPi6?S$6?EjM^!2TfuzK#H(J}e-iFvjg%0M((c3TK}Pke@aOa?8pY znQTW9fKq^iP3AyBB`g{i5|^JOVx87N)u67qsk*hg$=qDjI^BFZ&}*D-8y&nnrmAY< zW8CLYEU3)7cp#uN!0x92g#Qy&m5nOQ`;&YUHrzM{lOffK#>xU!W#^QYzp$|I2g@`+gGvFtrjAvc zna^OrmxBcekbd~n^micDAg{bjk&(|TF3WS3n_&d*v{oj<3J~8MGN%xp}J zp%y+E;3%M@R(h2pD?6_WZpC7>JE96x*NyDS)hW7)_$#|A*i zqQVOJSWRIa`i%;%wuCL^;gpNmY_^gi&4;@h6)6A|T=O#F;g8uk{}fOGF|Cx>e^>?# z6FR~}2egd5v?(amK>L(Dzp95(T==o6)%eLp-)Gy zIS;o#JML;z2;xb37ix$-2*ZDc+ShTm++%w)SBnB*$E#SOPAFE8qaR-$X`h|#ySZ$p zcht*Ao6uqz&z?2&iL%A65ZL_4^YuVTj4)f`O=I@Xp*5JBTiAepG-mNn56uqEwqLIv z_-ZJA_hxInf6y#UY=jklo&R(B++0(1Yo~o=>;2B`iK3yEd)>M=>*h(~%y(~q?Bx6U z2di42Ug&F!6I0MONB{!WeVFJCEqm|Uzc57O@88{D~m^*G$^t~<_LjhZ0dw9|sHhG{6hB(k|(U#dg`iEj=xxlFxYXJh>h=i6lQc0e2c1QQv(#cLg)i$Ll2j3-2x&OO+6TL-(9&dT;t8V4mzID4j7?}f4z>kB+&(wA;K|G%>jZlBdgfa%Zo5Xs3H%xqH@dcNt5)<@lUM z71sAyFuBaF==OG^E&sl?g%kJ2fPC(IpY82@^UdDOmG17HnIXe0%mBgZyU|QHLvdd^~^n>1n*$#eY)lS&Xp@$m(IMnvbQtP^4ZEv{4N^pl>kZY9V}|a zp@yRec7g5D<&{51BOgUOK#uw5%snW5&UfEyf$s-u0kFRQnfCOZPnrPS`~>v_9AJPQ zz|-b-1HOXMAJ;t|=gyO`c}PD+>4y)-$I=;iZa z76e%@$qm)D>6-hX{4s!H0b5XhXQQr_=>LnRo4wUK$OWu@CoI!#ZG2TD$bS(!w{N6e9?Zp5g-m2pFeSAjx(uA41{${a!Jgq^(?iGvhj|SL4uAA`_(5t7`23{Z z6!x028CHs=!{a7w`-o%*@ZI({;6RX&am6BL-X?H{Q38UdBoaiW%-9r4e=4<|+_vW| znv?>CGwb;0O3(NKu5e>xcPX^AwnTnD-2`ke;VlAMO2T5LP_s{A7lHL&eJx$`;L``R z@lRh-7+rI9ne4-#KIba^=dz#%IDY)Q3RXR&0Qg$rgHNAP@+1ODMj4I90?3vpTjO?*xui^qsvtt<2dt2IQU*stSAs{(fgb2% zwg!_u2Vo%`U;s3zo5&I~my09MVSGqAfDr&M_!hh#A=$trIdmTg#7Dna0pRHuOTfR{ z*8-scEUaIByMC4b7r;0!vqeEYpM52^6aA{}rbPN1_mS^q;qRUt$w2$ z@7<+$r@Oo1&lT<8>>k*=_qX44Zw;*=bqAnabNgKY;_+q%|uT}mw&wjcaI^Edbef`ZGqW%f3&%O5C*^aO@RRd*?ZIe2JS*F zco-l77{JX=x{OQraN1D_Nc-PT!>D$U8)Q!<5d=G-Zs&#FBy1>d% zV6@NJsKb{K2FM3EP*e%IV5h}7y$n|n$b%b^EefV@LIrm5ARigxBy4256|zB+AzTEG zG4B6~F>ry9+Le#=eHtw2#vRCRjf6$)kV%UnA78KQak4DuqW1Y*56 z7T0lbmnNnrz+6VQEc73iSZC*CikJ}T#aAabrj~HOM^YPFnkUO2D`VDGbS~ZCQp#iN zyU$;xaQ;IOr{HyWDVbVdS_`@+v>q&m%5vZWTwaO_pw5$_0BBJF%IeO}eV$Q53c%d) zy4>S11f46)J%ycJ9llof;L}&E<4_$P%g^UPeb2~JrXpY_$jn}2!U<~-wc7=QP`A%X zR)TIBo9qK^VtiGjFbYu&G)$Nd$(6@43xP2(%5#fZnDr%j9mUuHRrciY$Ns0k2|IfJ4lc~G zRUzKY%5g+ExnCdy)MG9cc_bpe+Ywp#wy)_iPBS8<(F|3t_-c^R)K+`ABKR4QWNyVBGM#aM2r_#T4+n*alp< zIBMy;_;jSL&7d>-%rc)-)6)~Sg*2h5L|9-Jbf^=d*qpa>dkH;=U2WLeIk_^^4iLV1 z6*&6La?6UL`SSGUXn^nZxh!v%?~SzYz2Dlq)bj2{GLv^%M574d@963*S|}XxY6OKfsj3TN)}jJ$t6{Xi3e5k}(b4D-a>GY45L7NK zPE2;fM-09oXcCINSPuew8O6^A;;mKu-rlQI3s=V`QprpXnbmo@Wt>!yxVErq7EeMS zOlpN*8rLM!3u8i9HwyJZxIlV(93~oF#ALErN{2)#q75U(dz?4S!~4e{JwA*(=-RWT zwInw~Di)z#bkQ30c74Ruqrp`jo=h!XU7XxVix3<#sKw=0qiQT6@_@!X6*k2-CfC-M zAQ{?@*cGD4tG9o{b#6c2c$}FhpImzW{Q1)Jw{N$%U+uoyUBB_l4t3DRRG2^E@mamH z%v?!1wH_8>$IWsL3V;X&AUmT5Y``2Gc8Hb%`V}58%Q%*igJ8bm{Q~feMZmzE%xvq{?YwpaN-AuHNY>nU}OgP zpAldm1H|c1zx~rK_yOKpd-YGxeTwfr9)LEU4ySd_Y|`IP1Ym!LSf&?Z|0$+8`2FL^ zA~=(KtM~#=?)L!b4JjI_@@-JEM3_0K_)zc<^y z6@CBPUtF45X+AlFeLlMIt#3|l{q@e>Jy33z4gD9Z4b8v+E@07)vv^^3YpZ<*7T)ml z+JUS0&eoOg_uq71x%BI;y>~l%cMZ|*E1>obVe1!dZkZW?nrG&8n&KD5jlMDh1ORy6 z5X^@Af$t&gmvs6-`=aT6U)uEx$Nx;)5&UGD{tGX#FWRyD-**7R1G7IW5`cap0>T4} zKvV?c1BzGd?BMXhaG}E#j5Zlz7@!M)vJ6pQAOM{r$qRuV2&6w_b>mn{)i*fN$%pet z<8%Oa0Q^cEbSwbl$TPGw2Eh4Sz|Mr+_2$-AOJ{YJ+1$r>4K_`;>aa(^qZb_jKmdgK zXLB?Tp&$%BkXEB~INZWS7@lhipUtSQ0S8Fwae(11(k5vPvI`JXcnIB;WGNxt*6X2v zC(&U&XaK%HFg*PIkwd}InXtVrnfH2jb~0=be0IRF!ji$DM-r#dG!Hm6zO zA9YzAWjPeeyk6=6RS+%y()xHXB26tldwuN?bz*V%hLpjMK=k?i_1ovK-mbrX`)qA@ z_xX)rXe~53p-LFNsxaH8tSPQ9rk*RVt?e*dNC9A@0381GR@tpjf%kp-bLK9dpXa{% zbobMrkL9u$C;%C^KE2O+MZTPQ_-FDhpO9}n4EH}Z5Nu}{Z%(gJj({7&cvaej3a<@3 z_Z&4<0atvd&_-h$g^C^|YH7sf#PJI?85EeuSltNtR5(dl3Da(bbx2~?dA}c=! zi@+?|*(|EKt`rOe3R?_SwY1@oh)vHwpyho8o$>(&jy`?~4q^GFd9VZjfX0Fp;3)0r zhli+)GzdtfKx6=bDR7z!c!cNx)3HG0nN$J9f{;{@UmyTHegF+%aeeI74}baW+5^`$ z=eFaTACB6td6B0xVD1u&Tt?aB{!+OtI2`nx=)0)~u!n&>90`W!?DnDIh`?V2 zd(y2@L==hy1O-@-gu@CuT)@P7o89Da!#!D1s(^ipNgs5}-4=O=g+Dmk*LHE?KOTJ> z|0Aey?)J?Bj++f!IMEgW)k5uwM4YQ;C@+D%e6H@0J1y49gr`e4nOcjHt7vHXi)BD= zv;FsgNW&urZtiQff@@5JCgUqnX_nBc-2ICt&+`pgd8ym8^rpncffe9`^x9#DXtGtrT) znfDk8qQ6=hYOh)yg8g3Leg z0o5OhPYa$fe!wSyH~>A38_1*8kY@c5DlpDH1j(cb;K_h@m zR=0u1TU|8RX>J~Eg?cfd8*Jp`2A^)~y&AKC2UM9rTmYd4Ax>;`1j^p4>^9-EYfY57>-SVH*IWaEyXQo-+4 z8dhk0g23o;wlJuHEqFp9>=7#2Y!O>5f{87&R+d-p^2jJMG4G>!@QAdJKN%iAF#O4v z*XBcui%&vAt_KnFCY*hjM%17e3r$l~zENLHHSvTH`LMlrLU_wI>nya)GKwY~3hH}8 za17dA+n!Ht+}>V$wf-O@^PI=-m~TietuHOT-NnoH;^M2NwOxF$k;2#zi&2HX{L`1Oj{W@d`OiNedxazZ>0u~sNL4@+aO~%w_rDB8 zaQxY)1UY0Q1k7huSrl%!0}(h@u>J)?Gh*-9a;ine6WRa=3WYwG5EG10sOC7cG7qvi zB@B`hmRmtDE@9BKPy({@7+F3y5`P)xWjr>uB9}?Y$;{1V%JX~1bI(#GoI?#F(IKgb zc9f#C(u=~w{b2!JWz z(7qJ_4*w^l6p;RZ9|inZygXmuz4dnYYU+o7`WslZAq8ESw78uKv;DRYmU5-Trm(T$ zd$Zf^gltP}ZM&()w5JK82qYc$l-nZ$VF0`bg+}>x(x>*gVmlw}i{>SJZYj4Pps@;nvIl_>Z>VorS^nY1D&}^P?vQi$*P0mBwTD znEg2QE^qngrAQTXSyTzXd2T8MjvA;T>NKflW@~oy{J=={i=Dkom#&{YgOmHW%fH&h zUA?z6wDZl(`H?eQoA3Yqw|83JpWc~ifk{^jQ2F<9So5NGK->?l{oQ|VU5NtZZ#P7N zC%)Nv_YT*6`=y=J{uk%l-@M;>cV(}8d1mVj5UC{9-te5|GwKtKW=+40dUY!eDKLYKZgI2k&~zbX!(Z>K>zUp@&5nH zfHKgESTcwJ`FuEe%)-CZC!1`A38pRb2zjBPf!3RFzJ>G1WveqnluhjF+vKrq(c~fh`!%uRfK{u zrkI+ZOmGSpTk$Ci^r$tahGx^(glsEuSHw zAUl{-c9g<|#u;LOj}Z+98X#WC{7*o@eHlPH3WS`0kbr)(1Ry+Mk_<+&L7uO#um9!Z z`t}e16mo~Iftxw+c@{#LrrHy3t*n%3uT6)`6CNV>wVS?A4c@dE)nIUw;BUw6NYFIz z!2>{n5J0XM_6~AK!f(Mc)a8MQI0>ePQ{}O!5io2M$Q)rLC@6$lVFCux{*J(1L;vz4 zFo|DYc-q!_y?u5N=l`qf);8T}peo?>;1Nc6rP+T@D0D%Bu{uhU1X2rAi3M2FthNm7 z42|sE!@6zYi`mhWD{lt6&!3-}0aSmfd$~Qj(~s5r=FZIDp5A#s)4g?O3n)JN@~yu< ze`d4)1)zf!oPYG*C_P^VRw3X&4aW{F{IBe6U3#;#gH^%ooxK?(2=2^aDwy4BIrAQ5 zUZ4Xz(bjt{d*{#0!0d16*U|213rPR%n=AOuhoYz3ySstGjRIx%3_M~dH%CcpXCArz<0d*^j3CR{(b={!(UuTTvZSP zTYL$((2aGV+b6KW2+OOm0V1$iu>@dvofI9kZ5RMiWXoQW=W^D_|B^dM#Va08>f805~Akia$QYqlrqXhdEMm`C}qL1)${v z3y^A$A}TEHXv6>y@Q9L&a8R6mq6Q?M;KUFFrof*(PNRVP;o$qRK>LP3r~#w}AVi?I zPoAtVEnI!}<6mxD;rxPvo=o!ltf7$CSnzG1OAzt8PBsO!C^haQSQ@(#mpcsnPJbh`;WR?P)1;p3kjs%E<@Nh(HPT^` zdsQy?L(f0hR`e1YohKKs|8D=t(=Wbyis^Sbj@8%n=(JVg^d#Jt7#!Cz{?7q;k;9V7 zF{$FaI%5b5T)DJ^6Mm)T($+|Hq{=X|a{0RGKqGpA=-R<_y)UhM6hzqHr- zW@}*Q-utco=*$)ny=Bn)+DBSvTQ0OhXS+PKY?wLO{Nh(I^8;cy^X>{_J(27GtChWb zC-?3}cdoqa-g&n(^kSfUU<=E{t%0rci2ON!<@_GniSuXnS~lC42cl7!eFN~jha3>J z{M`@%4FGc>06`1TK*9nbv%z(E{s9?)DB$UJGWCCju0Q^CByPBlPts+8T=$K5LN9<0 zAD>_+z^@+U0~H8(y_Js&RNyQSeJ_3mN8L;8ew%ez_QOD=3JZSNg*0LTIF`@_VEkJ& z*sFu!7g?dBm;~lp4QTp{jAm=4YRpopN_6)2#@q!uZ}nxPyGrNXtio6lXas2xF#`)2 z04AUap3sUw1cuc|xW^75PQ&BJybWQVQ>|8NQelTXA*AUQb{k0#K^d?q*$PvSQYHoC zEekL0q5u%950r%0X!r5KF0j9)-Stmm(fZod?u{Q`$J`H`u}IhyvR=MfP~@I2dh+c}6=ETb z221e%wRwmA_Se^pPoEkwoQGnmq(k=g{QbxCIGr}fH3-ISk!$leLLNx_g%M~aH9^AH zx5d1c(bc&(9X8AcPQ(BQWzu;)5CIRU#-n|gi!NUr{Ezr@^Jh2+*GJ;GlTUwTs8qpn zO@)v_tJg9qcPJ)T?|j#lm<0F}QLi)`=ceWU)#iBn&iS2|d$_5u4{e@oKeKc13R?Vu zndXy2J3E)q8Jq{4KeY88SNj%3ISB3D8@U%Xtm?LQ{`Ot_7L)+@41Y3!T+r(SVE6ng zu%dgvgLc1r>#qU+M`yk{Khpi?^d>&Mw>7i8)%`{EJD7aDN7#4Ei>;k4*!{G?>T6`L zd#h#T*Z^XS z*!(no{Bf=VE6&) zhhYKi!Z0mV_Cnwf+W?CW=pQx$MQ{vlL}%bdAy5N5XljD`&vJrq<3mYcf&VwZm&Aok z_isj9s@f_$!9ZSs5NL4>V*s{+hzowGZ_o$F=YtP>Knu+GkOH6q_={mqjJ{tZba=uV zu@GDs4Gk1wp;)9>AnHXtuJ6&C#1t*Lf{|;d^LKJ{wzrg@V2S8f>=dTJ; z0J3s(b7&&68pL-HXaJxOT{C;(VAe3H*G>EQ&dJ;i}1A=W&SMpj= z2+u@M(&X|%0_=juGwBJtK-e?S+2+pLkew!gpPUS)0N4nEDs2Js-#ijN^}hXdd;iuyzWeu)^M)v>{f59^ z%TD)9TT4rGb-a6|KiV?*;tTJK{=MIxdH?Rr*1duESI|qG85y~Q{vQ|py?6IePEZK^ z{hRTX7x!BFSGHP_?mq&AevT0CTbhK30BsC7S>~AIBJn-%9GjShX&l3Z(0UuHW4RB?64S zas6)sjwB8t-c}u4eT$~6EmPl)PFM4R=(l-C(LPib^d?kAIQDLSrOHy>SOw3oMoXnd zHI0?O&R7Imz_c03K?qtz0C8KR3Wfl<{4xK-BlK$L*psm}c!&M+u#aQ3C)-_|1A!~% z(j)gth#UyqfnufH!9$>2&p1bnm0W}m+_OEuFyYr&!&YihCr;zMc#Z`ck7LlaY;*ivfB;tGQT2v1W>ekLwlCWBsj>IDh^Ig4N! z!UM?t%@%+Zfd9n+#-Rqx&!gh={qMk+Q2;W=fBy87{ShD|TgnqUr!k?p6CONUi6RiS zz^eARg)%o&CUaX|R*M+4AeTb!h6j#Y!JuSkW{Yw`;blpcc_OBa!(_uOtQ1BBCD;#| z8nQ)FiYya`CC_g6JowPgJ<7?0Oqr%JX%Enj9MEcc2M&lJ?Zp_N6@7A4f3V>Ql|zMM zxGbMRJH!J|kjXj3&VUy1e>{Q7_)ney1ETNWHwDHk9uQ;^761l-v>yQP4Hi72xNLRnoe-8f%;e<;?xv`z@;% znM|!M_ddhU4gB1_*(grz3{F} z>;-1}o7;897tx(|pp46JzzJ%P!|s=~_R#?YPBOZHZq%hA00C#xzTYQNB8c3VM1i9j zAn||r!pL=O4GAXj)G(61_~YpTK@$#HEx28H z1oPD*p%Avqu5gk#KY2VF@M0`B@Po$>^g!{Wy&qI4Wu|c{Pj8pfFf2YC*M~MlyOPu$&xav~?`+)kB=ytCgLo9N8bQ}? zH8jC0k;3Pax>X6Qo2hc!L?WfsYE`@0xmLBpCAYYJ3Wh~Z3Z z!+0#OoC>R8N!jCEF?>;V=hHz!AWI+K;rupY$9$ z)KJcZ6A0jcDx!fIN1ze_9KfYZ_r*Y@^G`Pb=l~9pRY?Gv=}b8>00hSJ>BxFU(L2$udc)_{8@bDZX0lHo!1dbx!jW;_1x-<)u`9K z+4bsyY%(O!O@T;0RduQRfX4<1sqVyjj*ZujEC?}GqUIQM;o z+*e(RlSr=r-h!e6k_AAbz^jV@00uzQQB_+B`7baXuTouJX^unKv|_02RDs}QbYuE& z#t@+c7H!d(xyR7Zu;> zI!k|X_D;uHxY7E{1xN`NY9_BPLD!$n@+_`n0$AHv+}PcHb?w$JXaT4Kq#``q-F?0N z3P#rZ5SpGZI51i;0VZsXb~D97^X28M`@+JLmcTL z3bQz!3vlqrVekO{2Lw2j&IcnafPGiMG!<|^8yv6yx=#c`Fz}PHFTeb|jcvGkgW`)& zpXw7A`wC)DuHqdm@l|bYQE$P-}~}f$RnR#b-)_Hg*$)VV^U~f z-X>R%TAhmdX9@KAuCT?6!;efzsQSAUU0vYvhKV@H=@Ni4ERbm|mJ^SvnlHAG%%1sG zd&_Kf^XZ${VZC|N0Jj>I+ZhbGZAO=Ey6fzm(T!Mgcu6MLLe|*!q`P&X`^=TUJ&)a5 z_rU3Um(FyL>|DaC{~xgpyfX03uW*9@aVGly%$0Bc{mRyW;cvHwaEqVso&hAVH*)gL zzjq_5CprQJ4={V2>!({+{^ZU1t?s{JK3ToyS+m&Ywj85<&oe z`8xRy&;+ly7)EgWUnj4){;$WMR+ARsI#55-{e%32i=U6g!V~=70@dZpMr;T0X>(Jf zie&l`qA)*D6%S0ewgpy^0JPc`Xnxem=j%WWC?M&;O-%u;2(Zvofhk1NgbOb7-A#N~ z0SG|>Z_|kh*J!213On$rDHRL?EmcMYHNu#z2?l}?0^;-&A|OsZkp(Y~p$-)Ar#cs) z*iB5@H5nM;L}pLO1lP@QxJNtbN@z5ogynp4dz_~iqf^yt?GMI14j_W~d)xzgV8i2{ z;iK3H+|E4w^#l7x$Rtc994HJCJ;(wGfYpeBK(p~z3R!|@OMwHxT~i`CN3WA4Fzi39 zk$9GH<-bJ%*Z>^3`)n=!*aZXV`TE=E&$rit8n^=IoH}*(^SUg>;$mzplnlkf5aiZ( zK?8L5)bX=-zC%jkoD(cS^0=Sb`S9m#6oBFyDy1R+@cwr|Hb}nXS(*ZL zpM3nFqP8sK0}fTFfTOKjZA1_bmVj7$!4DlPL6OVJQ#3**=*3<@E?2t*00fUQ1!pU2 zb28b;9cSbL!UqsQ&6U(5#7LAS%9MS;rctt(;P+?OxP`~q-vz&nA*0E>itXNi({^T2ao=qn$3$C3#(BrkAz>Nz zeJ4QJm%y-ueVJjCZ3xUvSduUd0R#>doB@Gh1cb&WB=R;145u|VCO|}+!%<>QJdtBf zEf=YpgmZFYZ}iR!&++pcV%2;72Zk+a`?l}z`906~`7V8F*7mBqRr&4x)1P*A0Y#IQ zIn-A&^!-vo?QnHNHC%^^uYdV@+oOt#`MrgO`M`>bfb{f&rSZ7PrS!PcdEDV~e1N;J zuAW-txm^-K*=w@qeJ7nbDen=nlWp z&d~E8U49Df51yX+fa_apexfpuY3nvhIXz zv-^4-t3Nv`z+?Iks{lS|#kFsAECWTEFj5bg0*q9EC&vUJpa41mUg!p_P?H_JANas@ z^wU{qeLJ#<0mPmqdw(Q5$t((UN`uli<5hc`){X`e%4rzlF%q5fD-8&L zAWU>HnT-K$fLQC##RCR5yzRB&$YxhR{N|^vk`3fAsT7*vsN?@*jCjYe& zE`9p{5wHNF0w$9uRt6#1sw=D0V`9`v^>g_4EzGepjue&;Tmj=JAK!w&f{2Bc75>1u z1hm~MxU`fzoJ#=ErITmQoPrU2;^X0A!o0K7x%+c#$FmEl16(gyN2+VfO4FKpN1v0p z6;M*E2+N2G%w0g!6YcIg|CVe1SnK!Ww?90%2N)=jK$?Lc|N6)6XPotkp(LTaCq_#m zn`>X0n1{2F>*LhZ+S8l)^7h4-qn!VjJ-GFF{@q^#`Gva;^ZM7JE?@uk-+6w`cZMab z0PqcgwHN-^;&+_$`u4BC{`Rl^z4`s$hMseLDEg9Sslfye+SNo$mAxZFQhp32OiiVem5WVkMp2)ic7{mpLv-KAW z_4*CHzRIs~v%ISa3Q#u^;u6mASwMd_1tXI%3LKumoc|F_gMG8o&X$z4w71kG`<3KP zLlvgV;3ArrlRU6kq_wpwoDNKp|3W{ZSvdN>Zhg)1l$)AW|j4 zVJ_h^A0j|y=Y@tMH~aCGwo~Y-V%knV7Nou_!!eQPBhLqX{A4W*U# zuZVz*-S+Eb4NNb%MEI|o z{B&mgiY0uLAp(H^H^o)d6b1m5lIoEXSji8oOA@ev4yaE{#}=SE0Qgs5yng-ewYy{J zVQuSmckUXyrU3yVz%?K37v(gBZ7#;oH!UXAG?Z*MeKtO~^{gaIZ@16X)y>#BMXcad z`=&%|r>fh=(Km|?z1B4I@W7xkby{Xjn$06XeeE{~b~}V%`~Knky2JRr8SQM{f!1!Y z2#LQ<#Q35b(%5fo?vDw;@1WSY51*iaPg7?&WRs{d@7ZgQ43fK@=NPz2IvrsPc-~u4|u)U$bsjf`FWB^ z3&1CdPZD`31o4C<00CxY4Uz+%phur}oy#Bh*{VDFapI z7jl{z9U$}Ml@Js}d}w=1a(rSw1H-gX_6+)s^qtLuM2L>?Y<@dBAp$}Z6Z6{J2b1vu z&PNWEn3WfFHY-b<|J{j^b{c?Pe8Eu*GX$p3=dWZ@F!FE=NJw|7nOuMyT&uGN&b3(vm(_Q&l4q%ptv{>pED^x5Zg4tF1x z2~YwdjE^s!_Zc3Zn~Vt?ZCWgIlgZtJ-7>wTYPS7-p66=sJplT@5AL_U{PxGszulU{ z_Y>LAO(;Om9&CO7YA0vS5 z0*<+WSb~0_0@DH8b+7h}Lm#~0VSg4Ac( zb>x13t8J*dVxbQsn2G9?n$n?5@cl03E`48-edX3vaxn}w#bAEgdpj|6@}&^n+g@LQG+M2YJI(9x=LlR*;GXr zHV*x!Hi2z((&}sCs>@32(S?sp_^C8@-Mz< zPQJxH``*loe-^@zXFJVt)sU9y1qY`Ptv zZ$tp>6Wf1Up39ovytnrNC7*D) zQi-}x!r#Qr`SfXjNvM;5WPr2MSt@mjj&_NbWn-3bB{wiTCbA7fwYKV$kC#3=CES@) zTmpXc*R!KtPDW$3mYt2+FFXJq0sVpd0|7XU032dqp$8H$fCCG3OaOnu9`J(}=x^8l zZTG?M?|ePCHU7(QKd*%AIl0@_b>l`?|BX*O+OzuGJG!O`%~22bR}Ex89xkXET3D#z zjOQl-`V`<5{TYM7t%56$FBP)c;g-h(lq6YJg|Imq(pno!SQk|e0Ru#(u=`sYt^gfH z{fSK=`61YY73K%2jMw6;Y_|2Tc-x&0l~Hs3iRp>eI%{s;+*|BPOP;DOYiexWOjegB zujJTGb=QrZ_F0R;!bQHWGZx=zkH>ny^WEy|k;vc z@7A`Gp#l5(K?Pp%8=1(@E6EBvJB^+IFQEMRAXJ2Tqz&p~+suF7DaHWO821 zSuoiWdXVJO+EV<5=jQ{n7cTikR@OF@l*W~12P&n3IDg!FT;Y=&{+ro8{DL%;U9nWa z=|-5h@as-4NP4&W(xbh9?es^tek1&=@xju_NFai?;Y-mjXTXpEjcWRGD<=Y7TnOsU ztxT%j2IYGeS$Y4_0}lEcXC23nWsz4ju`)8TeQW%uAMQ~N*7}C~5<=vmZUL+#+XyZ% zASOIi8dajkQ!^ujYrGc%(953DUg|*a%U)M$L{CPr00l>bapsH-3vF!72y4xx9Q5>N zkO;z$GKucN5YSNpltcvy^xihjT zFQ;f_gu*4lT_WN9M@FB_%?44;g;o9WM+sL7P*fLWf1H~Q7bq&);}?-I0)0NZ()Reu zsmE8GK6n9u064sWj)4G{nwS182xscS69Ll%N=SfP}i$e zcjCKjRkkV|I;?f;Xya+_RT}-Fd2=zXWWJt%w*jr2Q?;AT@%U*9mu?R6Qeo@QuN4N5Ze0**Dqu0rGXPiuaCmomwL!yHM%B zIxueRj~(f)Fn|xV0^8HFC^Va`w~LSV>-3BdN3Zwy5f+0oQUhk}`*sUAJI*~Dx>=(M z81fbKzF7nS34x!%Ai9BKq4mW8fC0k9;TK~;Hy_yjKLI%eD%6O@AIIPL1YhA}EdWmu zZw^8L15g#LeuwzaFL?z?Uq=uSzTX8A3m(+6;yZx&M<{{IZxqJc^k5bGjg5^vt(zUw z)#~`JplQ@JHP)OUHvN7Vx{wtSeE8j9sUNd^KJp<Ewj|fgmnNmP@Xx^3u)Sq|K7amE-+Zn+X8vJI1@Mg6M$A^z=s|xseGT;oc|PDz=`vIJs1U^f~7ShENlJ&FR#a6YcXZ z-1-oK?`^rC@WE_0eW=f*~6{Nk)055Ofem?p# zBeWMvKnA3s!Qm43{ysg2{Yi)Q6_`f%-gv2$z>U&`e#PLA|NP0CWE?9CCQK% zdZoq&#}NAEoZb@0=?+L}C<$N{2=>>2H())7J}xi-6D^atN+O~p!NFt$g-2$d2v5pT%ELp@3(9`slbt=B>yvvjB)g!k z?POaUr}qlpdnY3+KmJ8XnDfWKh*X4ewO@JMb_%t%)A0Zx)Bwk^Ajk3GFaf9mVigDm zc#Hw?2p-UJ1)u=DxViJ}cRv66vx%Xlz?iMctm&?Gs|J>5jY8H$+a3`dY{8 z-ICngT9P>uM7hq5{%mOI()e%%!7gRF1&?plREp^I^zp{Z%K6sn`n0C%%D$$?hBS=h zYvXDQ--iWAl>k2loc$2UWW$%z+X|2pW)n0pP@dRf)2MFPimS2`>+TeF6U$dsb-iw{ z9!5uLHMa2yO^d`cZm7mo#Rqns%6w<Pk)u~$0#rWRhXX~S@E`J0DF z2bT}D2No0nN4j?hmZQ#Ddw11s^E>AuOVH5|=aj%X+}p#MiRha3By{Lww5>MFipT7w1g5rE$-yWti0JVk)x59&87j=3=Z zrvmV$Pyr$Sfd-9P9Kdsda$qG za0sm!|FTSc@OHc9?3^FBeAe_x7JvX#e^otN~)fu^B`R+fb6+(o(IEMZ<=Pc8M%td$hFV^Z3eVxx+4P1;bE4 zbFqf6z(0C8JKR6|(ZmSe+CI_eouje41O9h9{c*+QL_u`Y+}eY-@#XRG6S)%;_r`z2 zC)YK--~>s-@=3slh3J@)<>l?SYs~-8`nJa_R+c9xumyX|>;KqIyQT<$L2&T#_!n3`iPfw@4GfDA4PEvGx5+{CI9Zza1QWk!C+O zGf9yYkx3y3ksZCkQu)P*6g>VDe&<)5&Yeqdh?{Dt zZ-}dDh^wzpNJs7IddJ+J3 zC0wS!^Bd5QB=T453*HGPOwu!w{{kiN1ZhEP8vN1HvSw?MYhT4qgJjm273XRXr}AELl#C!l-x&~jk3GplRt4~sVrUcIZcA87Uu zR`rMW_r;elUp^3Mq5fzF6mOrVUBycO;OK}+;0T8y{@4+IfMe!;oxM(X;GhA50Bkhv zo6YOpSPbCtXXA{Ib=JMIAB@=zsDCW9{Q{7DC3JoUAVCxDzlZ~+EI2g z1_&j7lCxRVdT6%koAnzwk&`G|Us%*!n3l6sI&dRs+Bd!DPk6>S#l*%&$xagp)*2u^|7*XNox619qrg+S zOM=7u`0?=Y6&n8N?B6&at@w30A0!Gn^k6Xnh!Mb{24oOm59kmB^L}gx!W6(q#sG&M z0QWyqfa4)Rr~w5N=>C_#{QOzPmqTCu&LpS5|HgDy*3NX*r(GmvPT%P7x_a%&)Qzr= zst!c?z8yJblQE~}eA9615s^(5)qS;>3Wl+3hv!>xt0GXDa48)nZ^1a~fwIcDfT7I! z?TVDv6ooRTa)|;^5IFn@DdBKMWG<4iVO++C>f=>a#doYM$!u1Yqic?Tg0#+39yAxH zuEkJ%v82?jnKhbc9vXDz*PHDc-79A8U4IFls-#&PHcTs;hD~@?HD}4SjX!Bpy&Dvkt>$EKW1<_CVeeqc*eLt4JqW`Du z2kqm~bM%df0C-gui^9eR;KLTMQ^ox6Py&GsFaq#B9Kh-SpIC+dU+{e(0%oxb5CtF~ zDL`{!P*=&>c;x%#wyE;ODSi3Wo@RpzU~aCmmNZYdR~P2gZ{8`w9E1enJV7W(&T4Pj z-4)8-k>&Qm5kHRq%E({~6kibnz|N0_Ba;Baz?MnB!4@2XS6W6^R^~?dLxXuO6VtO1PqAB ze{Jo&`ZQUb*=;_;O;A!XG1%wfe`#(m*IE7I=KLIV|GtrtwP(ZOPD3L;PW};PlM^p) z_C>#V@Yk^aDFT>%KU$dFouD91j^OzJ77NinG(YOC^O^ZI+$(G(Ln0H6Jo+C`K|!`taJbY?Lxmqn%uvE@rxNzq<#@2sH_p$s}w^i8R9j(2X?( z;A4jSZ|0r=DwMlq$fA@10TNh!xmOBKT}l6_jb-4iE4iZ8KXs}g_csv)DSTw81LaB4 zzc?SlcZaWt4uHeY2q4@5coz-8Nf^Nn7(ifv$7?{ufWi`p9pDEP@W-1kcD~#F>ia+L z`;)(_{Jyeuy1&1QA~4!3LlYOtjI?byI>6@RL=l+NE zQxi}bS9vM7wj#Y2$|ugC3xSs^0~`gQrl~A0Auu6ezU5x*W}{S5P@6y+Og7n}HR-ny z66NMrEOA@(4b73#jk~_Bs=Y&{v07~u9?L^3ETo))`qHw9m+uz`^vG}z>g?|fV=7hW znAu`5y+V_3wAd||pFaG1)4}TlJ4aqSYd`qron33!&6*D`>-YCxYmY3Qh9k?q1{klC zdVoFvtq_X8;*A-xu7l+F&AS=xk$si?9(sQ&z~0f!?2H+}pEkV{NWgB_+zHwM1+evR zlIOW$B+*L@{U$AFzgA19->L0>Wq{~sDuxfh8{i*DUwHo;AO@MP^Eu(a!uO8>fYVPQ z5a3@oH$Gh8<23+NfJgwO8dNa{2tklI_#O1;%mQeFl7o0y{xKL_sN(i--t0gMd?#mb zqqToti75etL4VbhtxDZ&+q33SMto8Jaa^-@mzP_zc9tjoRz})K{IbL?+RhFD#xIfJ z9Q~}beAO~Y4?q;CAPn$3Ci*4@;s1_oP1fLoPtt>WzNLhk6vP|9=uLw7XX;9Dl0@bv zq>~hMY0IN+n59$!;Qw4j8+So&`q0+gz3n;7zDM93&UwHB@b;wp#bC2?vs95D$x@tr ziRER2%lSLsgL5>IKCzbV`1uSV^rW(JtAo~u+Bobc~?8H@99EV|$%Ig3U&=LCNVrU1dp)YRVC zU=|E+UhWbUgT#S_jb=)f68~R;Pw{cvaVU;Yz|)SsPF1;-)Z;t?W+1TtLpC6Dtds9`cFyoo#sf^G?qWuBJHfX{LaSv&78$ejcr4Vwx8~Peb#Q; zw?Ohmu4jH_e0TZP%+sgZv9az~2T!#&0|FrXyIDJJfEfXR#Z0WvyCYLMP99A7Ms&Ti zTGigZR)hFoH~aKR`+kPOo&vDnX+c^DS-^Hk6L3%mkHFl?iof}=bFXuiYM>qK(n0^- zU)R(faJ-@Wo5baB7hFGRzRdf${jdV8GwIk8vKi3uJwvg$?P>jm28aa!Re>hJs8Km| zzz#Cd@kjAE1OXHS+Idbtir}DlT7W#FI>6ssrU&O+a;9?hcRHG_Sb)L(WAwjMRK78A zEx&c6xqd*uw`o?z`{pcmmKP0Jt3c}n$!}$6d3U8{nip}ix3ur@#Yp=gP#d{+bR3uL3TBBB+uK)KXK?gJDciTL>Z z$w+asDFEF6lmI~jfCV5x0A#=pB_M-bfhY*C!2k)L9*k8|H)?(6QUp3FXGuaQ04(t})zJA@j zUu7)<4>aQTBdP+}AwFNl4?*#Y%od^UfnL0C*gQHwX{3`1a*tz+3BCo?YYiE$4A1-$X!GE`f-M?V@`(?F@ z0HAv-euM2JyDQxND+qws#IKdL-L=VOKI4x}g1rMDh(k{kGSCd=C})7gf$|FjkO;Hp z!G2`2=(#qZg6!}>62b#(S@Vu>^-U1Zpv9UKIDh$eRALrlS!^PD(mM1tEN1o8wRCllNP>G9| zE1BZ#_1*nt=e%8=L*2bxJ!Iej=OkW`0NuUu@QMv}mAbl~Ip^sraShIphPt}B;}!rS z;OZ6YMp{Q%@bkqBCDjmt%ZLSN0C=d5kOdSbG^Ak?R01OaWS}fvovTh5Dv+JX%ydhY zL^)?Dyh-`+N|H#z6*9a+qcVC^X$&%?xDOFOk||dJ2gs!n6oOP2myb_nxAB}h)waZ( zpG^U1i~K0tC*@?+dAY*p7d|dZ*)Lpt2>j)76adHhM-BuP@HiS2AmC>)(D5gK^w9?< zFvnjMfFE!2?BrK`^+(@-^+$g-PXniJ=-?qcZwj;!n(9d|pXJ8m>15uVQK?XfL#5lm&Xp6#mQBy}2#x-F|kT22wOe%(4f zIa@sw-Sum>@lUL?Gj`hVnVFe&TUYr*n*4{l%iqkr!}rs!?>1u#L=7lj=Xl#t0?p7Q zyqoR3vtjMF7Hz6D!p>9QiLLnlH+BSt%|%^Xr##hdnLMlwEiX&NFbxX&S3z|SOEIpDcr!X0~~1J0Cqbt z0U(ZU2k2KWPCcI-BY<@O;)7TVbkOq)1|K*eyc{0H{N=ublPy(MK}F5+iN3Y<`sPII z=3;S2nr~OrCP4;^Rpm{IMYi|r&5gxHQ_WVZZ$5UR9e#t$FV;%n`K^4%@fXHgYn=Z< z?qrTScYR(~yYC?Qz2!ZK`1k(E_Ta?GgTBa;WIq7FwHM!h zOD+J%AJ;Fi$hp2Yr8`P1BG42+$WzbpLPjUM8u6k>j4uW zkh?eKj9jA3NRh>cg`o@xcJ`M!$0>;KQbq*SC!Fv;9pQNv0sj%S*1EUrZS=B zd?iN8Ax^2I5@nbh#3O%X!5(csPOkD4Hh?0B)Hyh~*WWe#Tx{rxOl*M&0}EEV#zaL) ze~pCYE4kTFe=5`A@SzT*-T!DfI>|>s06rg+8TjkJai0FbQ2>6`kqaai0i1q_z!U+j z!9S3JIQqOe^neZ~@Xu?&zx{C+Ti_QDzWn}=zW=K~`nu*bQeOjWtEPR~|6l9BaiaqS zfC6weQ4KZ-&9vl;u`e3i>hp%S=7&DpQl}N-_>(qN9XEtWAbMclNPCWmiixY8Zw(7j zw+5)y0Zp)>;-CivW)h_=uP9*pPcOJd0jN;t(kT{{3j3BIQoN^aMvcZ+)m^83YPb4! zSr6E`uUnpeW74S5_gW5)%;@hdc24EHu_CQSJM(mwWgWM&1~=dtEjpgd2Gh(tI)pL2 zeBV7RYVQ8#)qw^oKsXfB)Ukfo{pQI4mL4V1HMyuZ# zgEh2o+t|3U*{wYiy1sRq65E}P*;S6U)-Y>9d!R8HGLFRb913};6igw&h5qSsi_NkjUGy%wW|cK@KoZ?Kv9>t z^pm;mIqS$>tLrw*QgSmYR36+<~_nRbD*eznRy``i! zFrN(Vl4<&h^R+)suv)~0`pOBX@W?YK5bYuFSzCtU1J)0=w|sAMa_yP>_EOm70xq9# zF@SDazBe~MH!0LTZ!!KBxgbB>+ZGla$zXut{sAqb|F2G%uOUec&Uru>TEc)KY519u zPd-`mcTNvbD#LnS_V#ARX7*%;WrU`taL01G!g^TxD_zcbctiopJIh<^YYHXjPCL7i zxGl{0d|XmSpU0-k<#LHc;UXodQ_hN>J_6caFgrv4sEZd1Sp=3=bM|Y>fcxM8;cZry zMC%)pUR_oe=pq6%Se4XHWRngc4~@Y7gB<_~;GWsu=b{qM$=qC|nHkQKR1y!*#mb%B zBm6yGl}cr-vx_7t@Hc*uojaVJ`w=?8+F@SWBClk_`fCf3E6yt*0aFEBe$(YVZtJK8 z=l}=?5ZC`P06-Uj8W0$O34mV?G61gthQblh(F6R`&s%^8FJAoe&;R7BFTek*ilOu` zm(nXQOlM7J{pmIMfE0k~yZv3)+6%Hi?eFrfwyut?UQc@V*M(_?G2izss%z$_HX;1B z#&Ps3)2RW~g|+BPnwn?~QqqS2M^nleAnVT`#yV_)a&Z0uW%ow)xI*N?h3{>U_Ic-2k6e+R=4 z?YoChFJs4NnW0L+1vat6Gt5{H4s7PjzxzsShuc46J!gj6;hh?qYx@jo!@=saw*7RG!A z|4yopX3S8`kuT-}5#-D}R)DN0Km&yVkeCE81`z9io$fl2fv^D)9e=k21LWv43>?3S za1g-)s!~;TWu3hsvcAuf>{pV9A{vk zo)xD5X?`r8n`_%^SjEzx3r!F|qhts-05KwP`UP${GKe#I{Zw-f;{bC33WAnMC&li< z$PkFZ*5KS$j50Ma#Km8h9)j98(jz>364;AW?xdf5U6Y1XDIzfoA zK$T)7jfW8zThkz)sADQUy#M)=Plo-^2L=R&5eF6wonIN;nA+3h*RDg!z(qIm><@F zxP75P3QP)>D?Hqy+@z`gN-w$GU3xk+@`M|U0eOam5KxcbIGNj4kjvcO_P79)Zz(c_ zB_O||73}_G9Mj}Zrm0>A3nbtJ5%^<40{kyAkYfV)uMS}1 zffxbW|L)KK{Fh(;)k1ysXJ39+u{7V_-`@TQU01t0aPIE{1nlZxDy=H*h#y{E9lN`V zRHb#pxRKjXTUr`KMDAf>!qBD4m|FY-N;apO2ZD@-y|gr#QE6&n23#*Zqbk)E=_x~T zfq}|0;S(8%df?IUm0QD?*cr1Wn#kWBY~Qi!G{uY^RaWgQi`Hs2nx1sNGg!Vj;Lz;f z(WCRXSn$=bbnCSii~UZy0qZ_(CvKekf8WWY1^DB}w_BhB6c>{T^7NaBmn{bj3`g23 zsQsOWLtwc5P7Tt3V)|GE&K%fP?={8iM+U=;$#}GGp4D|6zB`!RvvrzO_4{?@+I{jp zx{EZd9<(OFM>Ea8N$7j+7P@=8p#Bv*_JiZH@G7`Kbk!>Bu{lr`-9K4<$g%PCsIS5i3^hPb&PGvrRo6hX z-l{L}?hmSupDG{Nv~C!S3VV*s&$Y(&ra$y+f*oM>%bHI1i$9wM<;QoK0K!Lk7{wp*AJ1?LO+~gw-mbr;Z_1v=X0si6dCMIhx08t9J zXmvU<9A2Z1Icjcyu9(WG)C{`(m+vpvcc-JU5KlQzzndJJ3CY&Pg45h4eXl2 z?;BOdN@cE<0g}_sfnJg%i9F(BMBzZviBMOCl5kF$Yj70o-zXW>|FBd*xnQ|GL&{t( zjgmv#j}k1wUhx+ytWIk1D`EQ(gCi1A0Hq~t1F9j%CZL_jEv=+X%_9t&s|NT8hLtRF zQ@TYX2#S*u0~7__Cm5_D*3DfmlVA((=I$Em+8YbtK-pAy{stR>m>4tw1-IA$KBg38 zXGeb&LhQOM`lIvC6cPpbfN+XL1ArI^H6SHGzyQJ!#IXtF;ENf60w5*;hb;KY2QlD> z2HyVe@Vv97>Z??M zv8$`E^;fTs)gfjnH`wZ$o6?pRDry@x3lq{dHuP8ZzQsC2Yfl^-5~^Q8To0DU`fZngYgl-52v7BbGzaq## zo!t?y%WZlbLC6zA+Xu;)27vpWYh0`Y81IGH7o88g zz_p1L(e(@MA5gt7XWEZ1kOBYn;st2?y)~W*l(!sx3IKl%@zar(K`OwCr~y=wxsn?4 z#7dKMa`OBFr7I&S1_NW{{$5^gWrJvry0Vflu!*1swa?GHp9pmeYo6X6>6_S+sfm5A zeM_6~@cUj~M(wx7-wx8xC8wAL`j7h)l!@B>tOphN{7x+2hYLu2U{EP(-xDK!Llb?G z31w;0(VhYJerXdd|7r0V>R;a8jFNbIW&{77^yItB2zh2}>*&Rc?p~5~7ykr>(lr7< zK9WLXUA)3lWd7&Qg%Y?ElhhcCdhLvR6rtQwCFi?GDpdf+MFk@o;=T`7W)Sfmi+JFo zJgOIdFvOjJ-qF_j>cHf*KoqaZX=O$Kze^(Py_SJu?F}c1NaaTOab_x z72qG1=>T@ue*EPxzxti8PJQ*&Qf+HR+Ejbj-ToW>(^dU9u2Kj(;051EZ@Yxk8s~rY z?%3GshexLy<67I$$CXts?j05`6{*%WtEx|pM%}CDaW$>AwEqc}jWOeDfZPJwOX%VFD}Nymu;BtDONv5fIXBq zPmac#k@>80p0&1aE_~Ag6n@jYnL5oZ6TjWgeqWdY9~Eo4-3|8ZwEf!IG2*#Rstwq_ zcmN!jH`e!!`*s`ywNK1u?Q2MYvj)Q#2gQ5P{thAf;`24tedGN@TQ|f5L$|6tr&FtE z3qVU!#~)j-v&_C?8EBZ*7);2DE%^TMHcKgh{zo|wn*mCdUKpb@dNr2b{!USe8;Dh)H23;45*p2QCm7oc%Z!RCWRz-g3Kw)A~(NuZ& zhBd2xL#5v=>i2EjGw!|LFRt<((DVl_+KXRWXKh{epmW!J^UwMb@;A--1p^S$UNPdY z@fo)|{q-^reF5JQBY_A49Bkj69N{!V4#jX|83!&-=Y4VGAA1AwCmqiu1%Ve{Cnxet zl9Ow4a;DCzPsDz=QX`Lzit|pDM&_NJCw&0>!TNmkhslF2$@zWAbE8wIHd2AxL!JAc zEuisRTYL75@&D!nti9jfpSXE{d+X8Gy|;k>+kk&*?Q72_w}t%g-jDaUO8hGG{U)&o z>cem{9pBA}#@5!H0C=LMbGcDou04Y9+Zf>K>i&rH@6K{Et}#rK64u&?=Ev0^3az^= z!rfbz+vXXK^|qAqG1`;ipYGz~rHn|)REDLvpOb|sU94z1OrLkB;h_Xi6Y5ODmH)!1lT>GeUb%dgr0B@#Wy_GNfzs#ks6iju|JS2K^sdv9jpOtigSeImkNw#h3mg3z?bv>u!TyAs-J;zWcQNz4-qgKc zX9gWGU)X$QB(|rU!T#v##vX;j4h_(xAA|m@-CGr5A~y5-bvl34LpCi+K>~q|wyOs^ z{(7CWz>94Agyg@@WEc~DfKmJ%csIfM5#)azvw(;IG;!~XhCm#8fIn_}VFoTL0F%G? zQb>S5jQ-^u`C~!=ssO9-1t`zSf%;b;G~HfPl4$K{PK@6i00XSLfUm!ANzPPLSJkGu z*sL$#*!W_qux_tY-&1^43=hD(c)Ob{;G7F_@!$EK^=*N%$9V7Cva`IiwkxIpIQ}cU zJCp*rfjeT+CyD}0yg^WQamNcB5D4(aTizZ#;OzmIv#{l-R~S6T16t@Npy5&l?u!yI z*)oS&$Ux3Y`k8>d0N0d=!nnc&n9YHRK7j z{M&u^Zt|CR2>!<(+^=}{6XM^6wPo-^_27f;`I~RQUBmPLhi82yJM*P2bCdT_l2lU;Y0z?jyklmJ;7FhGRY2~U^vE@2W` z2-M&ZkMr4{iZVw0i&8J=^XU?WvllVIt*NfDpnav&yPc#6m0857yHJ}nhk@({l#HdTuA?n(| zN`=RXix?Gzhss^TT?0J)m5LN#23L37OV9_zW}I;f4<|P`*!_%qct$L~WZAhP=TQNk z`Uq{sA3f%2qxX-_CKDhs3W)MIq9YX^&@dFN147!a{Ly1D0PcUsB9Lf6Du5^g4o9$K zUtqxl7O#KwvmXd=bO3+$VCThmYd?J6_dCDy)$lJb-TJC_yt-+zs_RFI9X#Tv3|~1x38Pn)9>5B60Lifd%_exIKbT#JKzI7n4eAyEm(sQ zm}-{wov~ZBVW$F+0Aewj_s6u3HlLGkqSF^mzY$(B7eCn))W59%G>!-`Bj~>o z@-GX)yFvoUkFJYoAOQg!F9Co4KuzG)p$X(eGiu(vlG5g)_N;*cqC7ehTPD=1qJid( z&YY>lj;R5JKq@npN?+VH(7I>tj9)B#-g&rJbRi_eTvfi=KNr-h z^1`-I6oqBJH*!xzd9~!{qcSRSij6A+9xlzv3#z$rp|qI@Q#b%E?UOVO{0WANn>R7^ z`tc_a{){@lcaw_76rXM3(CjZCvV zJV|jR4VPXV9pzR|36S`ohRJ{WOla)I#^=wU2gjZe@c*1U$s=)Pa_EL3WE4cWoe7nI zth)li$M}=4f6_TD6M?YQ)jOMD-3XbRGD_K#;i-U|A1YBul}W)|#$+7I!@&KOQL&j} zsZvF1Bi14)1`!EgyvQ!V)uo}g&@Vw=+E87VR+Hwb5WZjy7~WUoZH6-*@?;zi)FN)D zvdksiOBNZfjPZ|h@$^U!4^@T97cuxn&K3CAi)H5$Zi)%TzGg?)94-0z{q`2K6tpH5#z0+`k1+uqf& zbFCkd;1^e~zFyVQ1MqA0wa#X`e%u9^L?gh5x^CUoHzt$L^4ef{{pNKBo-Y?GlXCG2 zOp`a6wY@QEfl+crQV4kwr-mbQvxf^F-CFt*683};ix(wkk=CgJowI`O9lU$}&{}MS zdt<31&}&Bfjn-(_UVlF`wk|?ApV|&)4C{NV`#^9Qfpo+D-S5Wjmz|$w_VQ&0hllSp zM(t(nzTS80i&gs?YQ_4Yz7xfddB0AzcX(hx;%|R%=|18&7QZ))Ass{;teLg$X^k-c zXS?^zR+7t&2C&CHyVg?o#SF+l*dYmm8oL1ofL)_uGSLD1(fI4M6acaSjC=v+$0vNa z4%c79=|=hk4xr%$H=yIOzxep9Qve7C{22x0n^cDjQx`78Bmc?KBf-k4D#gsV-EXQZ zF*!%CFK?Ox^y5cGd!3ydW?NN5lfHXVm8dl9n+g7#x_E>W+MUux)j;yD5E0-hbXGY1 z!tHMtn4cr@j|#v!=bh(1mEa~|KS0pr@)}<-6krnb;wDFb?Jci2vEF=3oj{h$ALpTv zI6T9a>EHqJSjy+2BFxWgfy(b!niu3(T9RB6L}~$c=6T7H%s{NwE3+*)4S;+s?yaQ@}Bo68l|XY2F%r{?Z|wnhumH?p-wnGvZUmDM!`Ct|t( zM|))99z(nT0p9l?J6kU9QfxnZ&bb5sKk^QV0cci8BLgF&VjzmN!uZEXK75h zhmS&*8FBHY(%sA1Q{mxEK%YWEd+y~dOOngNT@*=~O!*A{q;<3Pi^@>CMFhKn0b~TD zA0A~1KqnBJ+3OXqxS08Tlc?43hM4@C@Um((M7M^7B-#PBx*BM5zB;!iAw25(He&xx0tQdb>q6CjI&^6h0rH zs)Wk8^gSiu`^RlWgyIP*V~P-_B9dO2(&sl6KGE=nDFDAJPChk2^#5G{f(USI2nY`F z!4VuX0TsZ}0{koo{{3Y}fZh8)eofBL_|{Uz(tK5czJzreu-G=h=jI^M-e`8Z?wHB zaR-gqF(f3N941yGj^$yxr4!B1L)`&%-C3P>_AYDz)$ELsKD_(Dc<_233&6`WvA^`9mKw$B{-7vdvR_&D^a=^QT7F8Q!$6z*T zS^NPUm<$5x)0!wVT0V;C9}{HX*Sv!Q!uMwc0Pi283D6i(2l1<0g{qNj9}XbR0g51T z+Qn_pJBRDyJjWmamcA-|RaJ9hbGLOMUSD2yVJfj`s;0hZlfR(Ktl#7CcN;hK zo87ln>m7Q%e*dtj@VUK66|b*vdcRj(Xzku>K08wCm)A8tg3RB~7k)2i9-nt{?~e#H z-$C`^eizkX4Mae!=cy5#Y5^7A7a+h*pg@j4pOGN%Fy#;xfKnlR!jESIzRk40ke(m! zhY8}cDj!IQb!cgg}nR=)9m*1N2n7t8ej^{6hFLu0QTSZ(+gqpeG}3zExV)zy_g|66PbGZ zdEtfnq=cA~8Wxe&C4q{X65!34vdTc*vP;zkBxlqPHB^V3h)88(=#fN9Wo}_|%*Rf< zM}|a(bN*c=q0R~)Wmrf^_LWF)fAosyTxkw6Jb&|~;^Xt#OOK(;QvrCe1B+IKKvIy( zTzq^|gI$7yUC&2Hwmk+dJmoM2J&p#W0MH6J1i@kp=zsu@3jiMk5fBjII2nj=&_4t6 z*+p>mJAd`%FMm0q{^inVX@z}-In&eqz<>Q6S(pQ5b*%_y;Efxrt9Mt$1i&Idw|dZcZ`HVe~m zi>#=e!@xOhF{s?yb*I0ps`FYuy>pfAu106q88tBLA6j%z4Hg4%JF|fugn;2+D>pnW_G?-!ZYQ&+p*RAi*jJ?ue z3_3fr-)+bBk0k&@fu2>M*-7LJ2UU5Txm6M|f5DgsYzmpHhH=nNN@5e(wHmjyexDpj&){?>cUMQ4c=c{#1Y< zw)^GXMO8HXQ2H)hDALDs<--EpB!#s6&}@4ZQ4ZI@GH4 zDsy*v+@Wf3U^*+PsKal1cSURhR_OS5Uz`+aS_c8Z#%V%Vc zMrBAPQBtoAjy?}IK~#lUzR$@I#ueydW@=_H!~0Y#8*f6vd8g>4SI^g|t1D}82#JZA z$4M~(k5IL`L3ScE_FPOxuUr}C9Gc>h6ec|#&F~T8qi_jj3E-?q$_Dgni*fOnq&kJB zvJdd`0N;y-2l#mSG3Wo5C;-rbL!xEkVV-ewb`6$EZNI^{?PLJI7#2IT0| z1e_GMfR1c%e(|6M5?tW_Z3yz)KT-hrFY)!~Tfg%=_iC1IRW2^77xxeaUhDs&zpH)v zMmye8pSDu~uHNAGX9*x6z|}F{IsZRnfwcUh7HHnQHobYn7aZ6(mdr{_!`ss13Onnr zzShk)rVl0PEt)s0uYmSo$~Eib%Mylaf=Y2vZcWRt3hKJkr6TjMc+8}+SZ1CY?S^8z z<>}>vu>*t7GBc)y4QROwbstX9x=Fiknf>N6TRzLaX~wS8KD>&#hw0$q!QFk~=Vw>V zBKZ^ky>N8)HWHtd*L9mab=ppq*>Hf8N3&mttC)RXM=xO7f4$LpjeZ*H@L2bqJuRoc zZU(T=Hiq+$)}ou$S;mUjNdrXi3r1lwXoYB*BBVtCh!+sGhHNks$6c6#F~{fx`3K04 zyPwYl-{-E%ICi}H6I(y-ees0@{o`B5Dxh*zSBFZ(d>2s%swe|F#C3xC0|GSTk7itl z0?^z9U9u=Xt$Aa!r~9_PTUFIx(iGpnv3K<>na3&Sjk+NDB1Ei>qQvGYy0`WgU*?BF zU;s|PAN~Kzh+`QD&gVG)$5;MO5AO4U0YM;uqChYLaPD~*9~|f3anwZ%z)yJq^hA;1 zp_T8Oug@380?8{i&dDf?CiA)ba|SMy=Ugbs$=VsY|A6+NGrvavzxMV4c|P?0r1}5! zVtb2%u-*4|djjnd_Tv-x03vYxYWZora_zwn_ny_9&EH{RiDGa96+mrmT0Q1uF#!R+ z&Zn2b`X8M+<0+9xIlG@O_zC8(fdBo^#iax!#b8QQMqZ&TDTx^#Ck|(~04e-msU%M6 z>J~QIlPZ&O`@5VWeLpgUvy>6xCJiWy6ow$dFQX)x7nRb8$T%hgNCZ-W{*5X_gWZBN z0Usa&QV?RnA!7T^6*i<0YsfCLI)5lPp|T!&0)FB1)eQ;=!uVf;2Bf8_&qzk){!z*d zX>X>NYlsU;0WR$Eok{(6bt5@k65ZBDfLd-g<;cq^G!`(xO&O5=i|T^&9~E2~Mh$%H zv7-V!&i09x5_Kj;px}mtK&E6@@R-&>fPfB9VDTpGfOw8O0LLDH7XUzN098N~fDdvY z3cz1Z!ui-0dw_drrW+%Z)QdHR)%ECpK>c6>4du zAi(oli(&np!ET(j6UPJGrq$seU{g^YXpO=1bYpg_2J-KQO+!E!_r2Z1A0l7;aMohq ze7B10N3jjfad!}rL7m1U>pIaN-9}J9lZBqe;K2QH05-q?5_W%%7N5Ic#|QBSvM3OL znE#?K6yN209Df+V#^X~jUd5=-J5}L$4S1{xIMxL6DJSNvZ+uX>_+C8j|K&)2FEk7= z0$7WRi#MN}4{z(rvlv~t6Rja+DAINFlD{tK>8aOmx-PD`cv-}3)iV~CS zt8&Wo+Oq@*f0m=)-XfBHgu=I-GtYnXWeUJsK?x9F`~(m$JitH-Q0zE<&gIVWr%W6d z0IqII0S6!KPrTsN!_W9FZ1B+h&Xv%y^Y=os07fuTntUNAuk=D`a$fyZ@^^p*V$<)K z`ESAjVz3Z1$M(Yg7t8DcM)>sJSJv7}cDBD=_=*2d*7(h}tj*n9 z6X(BgsJekjuCy3QPpjK$?)~fQUtT>ZtS#+8@FG^@?arktW3ighezq$_e94 z32;@ObB*?ijF5-I=}*mc4Qs_7NaCf4^!7aO;>li3=Bkj%6}TA0HOL|wBT_SZrNJ^; zNK90tl+U7M!AcHpSd_C=9DTeOVVl}CVTj*=S4DL zLP#ZAnM%?^1DvFpP?E!9T_dD&i9*s7<`#1%+{eX75$-H;OLdElyh5CMV9ZC^As(lH z?ciyoluyblFH8SLteIhn#o8gk_^YM?f>0EhvY z0WbyTamWFMAE zpGRQ-{m25pyCw?2Du@5Wb36d>evAV+UihFfy?(>Z&tr1DTK34+djn8*bw;n?zte1{ z(sHBtwaI?fW~*Z5*FJ5nx@#(~8t}_mY#O*=>q=C0*o?q{FnKuS{6SK8?Do~fy7le@ z4O-oYbv8{oAiWM)o>e?`K(&E)r?uOBFa`wpjlnpJD&S6?eGH6HYhn?IeAigCv0ry& z(~oJj>-x_1ZZySaW3!PQ4`Bp+WFpnqVrGZYz5agPe6>?MJ8LXY+%tCTI#)Fs&_5gq zX&mar5Mb)=G#Kr5j>eyn1Fj#%!11P|B+voiQYv}_a6jX*0Jsp8)>=6zCORJos7w{tsXq|0Ene`J*H3A}0lyI6paZ z@6p80*4*yRg0-LCjt^n_H#8zxz!UHdw(mhBP$!VgpO)l4Ds|$*Kj|&+i3IkFgkr&0O$bc z&pS(H!Pp2>H=q+_e{_Wh41nRE=ASOW5ewkp0f-L{6@X&~xFR-z$H5?!fS*SIa+P*} zx=9)SZYS?stY4by3lA5Y%D?FEp9Tn+7TEyPpTY<3q5uf=pI6=L|0)3Dc{8SaV|oMj z&tnikz~7tRfCAL%-u(R=oyla+4BLCJt$V$BUTc4&10plo$NO~QN5O56e@$!KL$Rn(P9bN!MaR2x$F{nc&QvZuk^3<1<23-g$>d$JK zQW5;2(i=%W(C-m%bVsl6JS^9PV>aBL)vk9ai9y}iEZ?&xUYN?VS>s6woeC;Vo4OF+ z5|L9i5Z~^L^mj#|0E0k*D=>Ya^Z^7oM*C&1^cUhX7l$0%T~0M9AoDsMz=s0C$#>v@ ze8N#bE&}4m{2|%{@dL-X=0P&tk}Nnw#0ZF3VOW6(1yKR=J+UNPXn@j zZV1hezf!IxuJ&20Pk~W z);Ry6nWGmQ15z4W8@%YuZ})h4|Hmi0;TUx&GZ(S(mCK#o&xb_x#L7c`xavL9dzzx$ zQZj>k`K^req5hsS)`Ak^b)hcHCFf+>Aa8g+H+m7+0(VVvPA^&mzmHyVpSZ!@vIBv)zBD6&v}k|Ll0dUwu7ZSiiVflhe^5PykT?rgz%En?@YmF*XJO zc%A!S^#3#gJReX1UH}0(_y($gN%O|Q)?7y~AliVZCd+F}4uGOg296rh*`ke**##rZa$y{TsGL>%org9-ch}JI&-J< z3yr?2W6#d^-)5vD=wBabI_*cO2Dtm{%DZjG_|7pxfB9!A+B2FgD29L@Ua<#w#Xw`w zjM48C`}GQ(Pkb?k2#C+b_|H?v&Vd~TbH9-OI}|>}9C1D{`>O!|`1EH1AWgtA_D2ce z#VUM2kpJfUT_|12Zw@L&Y1@#Kn1e|$$^X|?Rr;s~++N0V&F1-Qp(hN75RY}2YX zHxIitvwPTq6lssl`YAnQL(y~)Zb4NoP0a%v4sSp#MUnmE4?GB)p9`KBHb2LWJuFam zZiy%*B82*<>DUgta+5$mfREc9!SyBbcilBCv3G zeD|N9ZH=sLvl$rNCIpDSWO8B%ca2&#>e{wXK8X%}IU1IEyK!{1u_-K=XYqOH$xl8R zcWDfe1oxz5rX(p`+_M!iNlK}wd$>omk1|uKK-AOQLs$^8AYSNgoL!ws+V$|^f+o$# z+e6{(nVTR_RVYJ36%oO7{87D8Zo%>>sZ<#ulZ)-26fhrJK?J@X8T=}DbyI~5^EGj@A=Nu6W=NEiY zm;vEO94k4WjhAxdNuQWsm!xJ!Nt8+$43U_BX2*z?=EpuKPqlH;W2K4nnoJ3QDA+SK zR-yrb8=jHyP~~jah5d%)YU|fqu_Qjt#z%Mt$A>@Ul%+Y*x+MjCGc$ zc7}e3^X3@L9Q!*EL@j2(=2>eIe4wXbcQgSehG~d6`!wmD4-aPa`xg6?qs}6uW}~j? zYWdiHcXv_zz!#f!=Bpb<_I=>}^b7n=uUcp{44TeYT3AZxeO5X8-1_2wrhot$2#h!Z zAM^f;Rl4h?Lm@uz6`i;t0h5ez^@a8UmoLLdhM2=-r2&!1B|P+BS$Xk>RL4yfn{ zx|=sQ_1#w6hCZlfK#wTlux@W}!`d|ZQhQXaAKkOtyUX>5keBi3Y9f))RyD9$-my`R zbKs_Vb2F!C;6h+f6~-b$5R{dN7m#QH90)$Jk2t(E`hxR=An%3X{c*8_0J0Lmg`e)= zxB$HU&1O`=CxoBva&lqKG%W$dFlcX0hi!y+PX!v^l@iCb)Zc`>80bE(J_7K!1$yys#gjNz9Md4l~FfikJ53+LZO7VUpNGEgB4SA>U#L?=1B#5gx*a_`4x z$}<~esc?bhQn_#k_UdJVS4!Pn1KgEx0dEKI%{zIg)vTx!0#AgLo$<%k*CSyrB;VUf zQnHf}SrZdd8rk4|I>1$$;v|u#Kr6)WFDZ#CAXfw{xxz6D0%A-JmPV8A73C7?ktBC_ ziOP^iAPV+~2~5E8OGcz{?nlwl=eg;#K?NuPtSe=yo^Ecbt}>sKe48iT$q@^JAecdb zN9X|10l@@vtO4KveNY29wg41>V;&#{VC^5Cef#a;{kf5AyNPk>>9q|ti;J7JhlR=g z{ZN0z6wu!Or>KEee}2RQV3jiu>F)yzh?i9(2Os#K$HY4;0O|n)fJJ8*yHmH{V1gxU ze0mhwe5YJTQ80D@FyJ=?>~Fi%-_iA57s((U9UbKyD-M|vOJ5DD0ImJu-y2ZhnqHCU z|MZPT)5!{+EkN<{S$tL3RovNq2j34i_?*-#yG7szN9!007;KnP+Fb*p-!f^F-Y8)N#bi&u;Gz946Ws9{WpH4Oh8bFJX~Sj_8U zFWJeZPxs$>TmTFJfI5Sg^N%coPmMzHgX|xu51?Oj_pSr$|8V_-0XV)CKPvhV1c>~X zFGS*RP;+T%Ngkh&*?qRTTrh$+I=T8)=3-liy4y@zp=y9{U;sY4N9gMK zOwDTCf4<+TFW)TJmp9jESvPaK01%sf&o<>$O^YzEJe2<{%fPv`|0^6e0Kes*#Ccv5 z#~i}n2b}L1|Kn4~!Dkcb_}sDY<19J_dXW06Jd4fC2M9YwRz9fX3-ZNFHjB0FZ9c{z|h$?U~RTk5fat0QF*JviqyEHZ;!QIn2 z4!r&%Z|-yg61g1qzsLz2{ZFRO{i(^cz2nxNl9Hxrv!DXuAaXJy1Vj=LPz)>(4!eOM zP)=DPU(;7U+B!^M-0jlYGF95r8G6t%v%~lg z^tXQQ=b^Sw&K%_3nfJc$>-t{b!|I%BU+VGH_y`8-G0F^`R*|94%gWNj{>yLGXrl7d z^D^in2<%cF(G+U3iV9VlD0K!S0ab>I*&rsKS)!LHsN1_c0xNK3^sXrFKFyJX(cgcd zIF)@Ue&4?H^%2^0>Y9iTV|E>F(MSD8J`$pIl@_V5CPS}_iy;iW5LO{$2c;q_^6>H0 zLzzN^tk@fEO3~+`6pTG_$NAGa4-P_zy*+~fT9VI!vX*5yTAMO<-1@14-XEZ>Z1{Wv}fTEcG!cKkZ<1Q^0{j*Hfa2~^Up*1pR@~) z{z~7xbs^Md7T{gb7iQ}3-y%(TD!AeEZuqGbHtnu}dkTl}z_OP;LPUOtUsGy0{16Mm zuVEnr0Zusx0P-;Z6EN^_8^+6*=YO71iSoSZ{WkyJJ(qM0{>$l!77020Bf@P^XEnnUVCqBPX^FkQU@4--Y{cG zPdS@^EU%*m8?d)~^t!XABK6V8WAS?<{&2^{BE4w-emaqrzO}KVxkPSeL03{Bv zbdza7J5OBR&@3>Z1poU56X;bBPobV!AVFK$>)G)8U)h%PG|P1O{d5Nb&);J>MC<@} z@nn7bveQZxP&w6QMr!9^(7$4LlM-U~1>yg!yn)mEi{D8B?sjyCZDI8I><+9>8gPia z^R}SH?VEKrb`QARldj-`WwY`&_BaUWU;&#eG3pCV1-mW$taHk|u{jmO?8j#rT66TQ zEw6;#i)PoF_rB$Jw`A>M4|DQwV*0)5=&||3;l|e%3<|v9fs+20e?^1<*(C@-^#2_E z4mU*xJAi{Jpw!<7JFj*223hA)jelR@0l@!!H1koa{-FZ}x~$;;R-}8@l4_han4QyhVP*l>xUhku&F6`wEv;Z4taI3UfB>v* z!Pb>KfB6Tv#5?hj*noh9eEL}`5TxJbaAy;cHUOrLPaz3&6B*ujfchBwF)83*z4HX> zF95;VbXoiLXPEsw868=AIQsP&n2ArW&<~7Ue+gd54PHR~*w-xnk&&mY|EE+I6aoMQ zR~QAH-~Zlw2Y08dq7LYrlT4t1t*u3AQIW?Ej20;Oy!YPkYOvMTL9$h7qEDnAYe}c; zZYfODAnEH9t3Sg8k645I4;w(-35J7(eX7`~Q%Y@(YW*~zg6eD*HFgGy;S5#Q0j&-# zP?Qo!fhdZ0ohmktq#%`sk|94fEqcEq6A+)M0j-{Ruo^5u(f=OIJ`i^%Q5ms2Cht&P zO_Dm#sM>AHh{`&flAeOG5dU@({J%ZXk&%=HSrinJshOGIf0P=pR7a*ps`e|3^3N7V zDHWNTh=`9A`!NeROa$QZ6DJPQC~)4#$15-@i;;SOn1g?Cg7zR%PcDEk24~%~0SG-X zY#&YY`_~U{{o_whd)>iv7n2C~n`3nX!Z@cJ;7kl_G*WI zds)Bn*yFYReWQaQ@UJi$7{q$K_Y!R5uCBivFb8`>U4fvJqkUx-No{3rZN zAV)vu%9U0tn?e@_KynGL8rIy3+^#k}0Wb_C& zHP5$b5BN6kkq`;?KflGK5d2TP{`5r10>KE#7;0boZgdnl;naWL$?O~%t{$Bp{%I`n z*)?j7hY!cj!xZAomkNNV%m?Wc#HZ=8C+DXRz0WZpw@ z<-Vo&F2*T7$Wt9WoT^aljfpvi4o#iWp^xJ1hs{%D(&x3bKm_V2+7(OrAEBS?L)&vS z`u!MH`i0Z0r!TamXJkYjip^B()OrnvzCMZA&~%+r_yM5{jwKv0O50bc&?z$_DIa#B z^NpoCsHv|?CMd8bsmT~qUmthTSZBZh{8+_!(os!CM3jky!7NoFXZ`;8NWI>qiP#%| zVt=ImC?+Fk^HNV{qW)nd068gg?_TBEsQi4rCNC1IkRnc{fTo~6A!V-93B&^eki?v? z<)+*p8*`Ab!3a&uuHXAD*8lHu2|EyB3P=ecj(>>@C|E$^MZEi83;+~R3I_P;kH5M5 zFaP*$)y+V#wYl$N`6()Z4?p|xgSG@#=)?piz_WpVXuw`^yUDA3=Fjesf5az{z&E^M z?KAVEBPI{5o_9Nl+1S?WbxCigdiveo#@C$f^YatEpZ5yuL7ws$u!~Av71KgC+ zeZqg-v;w^0?04@J{-OxDFOLLa{MDLElC9)`Sk3q0{fHp1lHA_55;L}dZB}!)tGjj? zCf%eC-KrVaERtr44`$0T5uC@dECE)AbS4 z?>*V8+!dc$(^SxGGNfGSOV7i+Qx$nA^`nCq-y7K*eJD?HP_2h{tJ3b(=IQdX`sPIN zA3MNlaCho}6vD#y9^K7aj6BC^4>8LcNGA1*6zb0dUew4oG(85nhp`1MbZhN?%BTMHh2R#3OG)zoo-gzxO$ z!NCcc4gdrwwg7Md83>Fz!IovQGh_f~B!Fn**xxVlmL4>)E-Mwxo^?=Tx_uLSQ+}Twvyc z#kIa1TIle(+JffA8I;0-@o}8NLkUeKX7jl9QesuYvral_W9PFaFnM@9!W>va@@1fp zF8Q4R2I#sfPmpNC`=BEf@veD}f+h#~+CK=Kh6iou`x@ZB|*y!7LY z85kf^JfP`K9KJJlB@wy6%P$^IFa7*R-oo}f$?$RC@< z*TONFSt6(cv7qhK_ye7fyYt?egZuU#{?Js=RGx0==+Ji*?9xQUb2grg{^-4zdlZ@s zjY3C#UDv?`Uwc$92)ttWSH!3w395gQN?+JlsM!_ON9ZKwHbMQd$5btSYyh!Q`KG9S z7y})QXUXB;tuLf^&tg)iEm9R;xS%^)q&l0SiB)8v3s@{_9%|0q6}P9NCaIyK4mH4m ziqf>EvWmP+QnYH+5QH-_3QUIGI&el68$h9o-%`cq$3~?q^CI>`IU;-T_?K5trmADL zYIR{$N}5W$FLqaqYEM+NB2O22a(6_kN*$+)1@}8a4M6(RZyt|S$0*eZ24WS8*w_eK z9<@fZ>koem5P&&=Fb0&Uz?}Y)9#}#EBtHQ8nF2r-1j{AB*st$i{_)YTFP=YDHr;yu z*12;9i{*>u-<+R0HPkye(K$#DFhDF2t6ngHWgVb&mykb?&;UtofEV!3Cg6Wwx%}K; zJG}E^P8jx7Vutm4?G54kfaUkRuFy@}d}v@EvLRbc@5JYw#Ym2DAMOg6-Sa`K$GHj( z*!AkM-S2V`uj5&peI0(`o%BqKRYwNErpNmNbAI8s%ZdxogZJhJfCB*sp&z(W;oHa$ zdT#M_yF+GYWks&5(Y)YBH|%pc*4%z)=>}v$-@V1x&aT?fSIZu=?|xg5+;JO@KXe87 z|IyL#S3IzQ7(8xTL=-r?8;hTvfJYCTn7H8nauglM@1!B{{wK&DecxZ_wjg11$_X;THQ`w!XIT!oo)A zdeczH6gtNh=f)f~rrMHnr>CuBwxnZwZH*y8Yuj?pUsepYD`jCoWAe znHwt|MiR6nX@PM77nFZ#9lzaa<|S@FU*rRF9Rx$q+OqyPYbG3o{*Povz&8tV5yd+!6q{@pr*zOQwr1N(Wsp?p_b z4AVR{TDkZBb9ZDquD>Q$ZBly1+pVIDYl&gD>+m%KZHNbdg^mA_=0h_U}KU&dbnc?#Wl=tD=^z!w-LQ z`R^`XJ=5^xXGe|@>)%jbzR}vc+R#xvc$*nuap%N6u>r_dAl}#a-yQ$b0PqdZ4$%#` z#SoCMPzO*#hdr+V11SgG@BlZRo*?8s|6@UnklEWNg46)LgP#k>p_f+5jAs+( zfm|lm=6UDizafHy)qT?k>E2?0?6Khc8z#J$X;`Bdmq6C!3r{#;uC#^S%nPvlxJ140 zejeW31ly}*)#p542?ZAhpLY*fyL&qc)FO1)h53)uzs4jnxzX1b;LA6J%eB z{_oMb|Sn%wG{6e{2U(1}Xo4ekue( zIQ@;*=O&IVjY0CqO{hNM?H8QztZ+<9Nh2$z&#%5dfXPE|E?9~hNP!)-F=|$@$rS+sGA3R)D-CU0+U{z*QLDLc4kpoS224z!4c}uw= zBTu=PVT5j%G7{ns+(I^-7{#HOOr(e^r7rc82j4vS;ADIuq(Hujd9W%)sZCSui9V#s zQ!7~hSOy;^P%S1(5s4>QOuR-Fr&N<{5*4AqP)w1cN>`g|c1gkD^7@w`K#2z+X9Mnp z0`LT9fEM5#4?r*h{`pTXfAi~KKfUqoD**Dz(E{qj()ehS0oBn+Gw@yW~=b$zdjtaO$9B!V56lt$7=P~_6CBk&ebOVG%QI5Y`{F4*m>#8EQb5I{Sv(V{^b~5amBW zATK?_aNT&i~e8a{#YUYR&+hDOs*6t z;6%XO+G}2DtL(09o2fOMYD3)?$bie+9QLm67V?Ro_u00|M_QiqZM&A&SC$zG*rzV=>a;5u*Qa1Q0s_Xg!DY!|&d* zu&Dr8;_{;A;th>H-<3Cjot=Rn+36DaD6;@~7OyfAzz0Y|OTyU~?oPW@UtlLFDh;4U zdW!GvjPa$fYf6fTr-w(O306G-~fMh>+a=$G2S}!S9dRdxVnDz zTA0HOh4<@3ayApkF&^d5&?1Bg~YycI-1E_nvnGYbpwO*rhe zGOhPIU(ecJ*l8FX_yl;(K_Il=!GW&;0S5k^9$=uSc7W9aAPCVaUp_o3l?LS}cM5&T^c2P*>rYXJ+Km|%D+ z)Kyzsi^4GE4h2GCyJgMRxZ?02657C6%xBx^WFWI) z@Zf<#8>k{4V9WWUXj#xsja-ba>2Jz8!*)_EN{VDB&cX(#ZwF6XL)sN zW!W<0vMg_JyM3#D8&f`4pKVaFZG+OZ`ohz(VA%+^E&5#65_3*{~DZ$rOl%e}{*x`r}1j%q(SQcog+J=pf&Dw*gS?@00~QlK?I; z1e72|E>OguhK1e;Z83&nKo1ayfBvtFj0T^uC5$#DEIoxFgz?W93_zTTYnJZd``Pep z`kXOwOlVSBd$S7+E1Gu>7U<+ye5ehsf)p{mvNL=#f{k8Aa=bdRw{Rk!~{ds)GF!IA1im;@ilJYXnjT5N4;F(kE?>-Qhnz2~SVg$1rXopn}sAq^l< zr)fzmRAe0f{>ulf|KsW`JU|Nhb7TV%7|4K+)eh4yDpG$SdQY?>??5WTK}9q+zzQ~i ztf-inSY@mdA^^HTQ|1wa)B=c6AaX!y3LvEd2?ijSzyM`iaG3`n&jJ5Ky8h(R-MiE0 zE}#4HhZ_$ret7KBXXhKrXUgl&@l-90z~BH4_A>>LFP6W=0(yr5g7*mkKoEgB8EO4N z`&jrA7T}q6*c{%+o_?E~C=HPH`9=pUfPPmX&={P!#2}z|pm=bw7rUX)pM`p#O>~(< zRti$ zA#u{>gF>?EdDCc{gjI|s$yasQIwKxO16@&iPVi^0DH z0m{ez|04s+Z#eOB_>gu3G=a&<0i?SDiUXrb$erOr8Z|{aG)V!pt!27WPHF{ zSCBkp?Iau!4{*4)*^C1~0ifzp0tnj=@^{7hmbJga1VkbPfc2Bt@$)tLl$XCF?+rU5 zAbAJiiHBtNkCB&b6Yw3mz2NpEB#yut7!_^D&kskI-lF{f>zL+v^vKvb0xH=K9iA>+7)cG5#~fX6cJEG`N0b9gT|y2c!>BsfzMcDLO@3Uc?Co zgamw;isn+X^hq5vP1PmU*c8OZkP}u`NB4iEh7ACh{G^T{gQ2x`ydx?#dRJQdY4HyV zOYzgf@rPe!mY@3Rmk)k;@Xe=@`NS8hNCeTURE78uQv>WPjLP4uh|Jey9);78m#T;p zzJ|JpI4DF)s{eyg+L#!1Or$O{Iz2*@AzA=#vM;C{4I){BfCR|FKyp0zC!zw7)qpz~ zz#sqW?ybwWKKtq7AK%TqapQwW=LilZClYJTBNR0+9Q_VBzHW19em*qV&)n~Gw8mXb z8rf@*0>6Fc^0?vo^!O(|l~9Nn#!*4cuT(nMLLB`T0s-#@px>hdc7|;(4+=kL7-69~ z0mNSKpuC3OOKWI%}IH{%!;KLJ|bCHOL!) zbg}N)DfltjC4jHWTfq)p0Ll*p0Kxj@f#oL}0H*g$?#hZMo`DLEh-9+!4cVfi#{rmZ za#(-YIPkyt;!9;`Hi=_Z+3kpb_#Y;6{I9U&r8A$Mg2>Y8YNNCVuq%{RANzy2G6lKv}3&M=}%C>GVY~g;(cTkiZCa zhJp>C<-%N4*6H=Rv*l$&b^7Gwf?d1LRYaE^ZYVpFS8ya9QalgK}Snu#%^N2 zp%z9Joz6%RZGbjQpAj3Kdg#l4`i2eQpVSl=_!6N%B7*>PVj#iYOec1$VF&ACBDLC# z%$TFG>O$IsIQYMq1}cw6a48hTj4F=9AQFZkk{(zd0@4Fu3M8Ho1F-gGLjbu3kSYMN z{<*OMTz>HDyLX!(-M#!RSi}eCXU;dz6qGltZp_pfCxk1&#PxxBua`04J2y~He7WzJ zb^w4rUL_Y00RT|>^TxqjR+Pj4!r}P%jo0b7O}%~-_Pl;ID|%_l>Cf-opT8t*2VcJI zDyGvIKpB7-;B#~1ybT|)Nju8_kj?%$X!AUFVDllSZ*IN=CeJ}^54y<-aM?EL#^DKv z2F>&M?LytN2iOOY4^IK+p*U>o9as*z?crYYtkdH2HCDQO&)v4KngaNKn^!H&({B#J z7Q#NjhW!sSO8-5((}C@ejPdzrUxW7BrM_Pr{T_~fAp>Bm;FFCF0MRcyfZJgf2Eg3p zIlrX&ktTogI^P-nOFKZBdkXrGXUMW}dTM2vbEexp<(nx<>~qhoI(_9sx$pp! ztH!YyY8qNxtRJeJzy*LrFk@mThd)^WqOt~sM+Ep^kb#-uiGy9P^o0X3pPx#efJ_SC zjR1FM1@CqQkiB=9pcwRvO@JQY>5|w_MgbWm@?KR5T13=;BTG+5uZ@jh{6GBg>tVRP zU;Nuo55E|Diyq+sU_evCboIH<5EDWD1N@st_Jhfwi~_(C63l^VKH@sSd+2^A_T)vp zUy$CXJT*Ojp=Etlr_dP;>4mB|NPJ6PXE zpnb6^`8rKRTtw>0Z@&5F#)EIJrb0hd9*R=IGSmP9Mkut}w4%cNC}m81RFpzZN=f8z zVOL0npp3x{48t%Mzgi&}fqQ@JWU4}?*!9i~M1lbRP!0zD{ofJ6K=K|S=Yo79w*c=v z!T#g&uP#sDz5C$@k8b_(4?jG(@noj%v-#3ZL#=yNOAu>#20x;N+7dT8kj|Y02i2_X~Xp+$1qOhZgEpZb!$n<;^K1K;_4LL zybm+f#RZpd#kaf`#`(}`3ubLMZZEGopAU98SGT9ux)&EhL#tEaK(C%OQ*)wWVPj*c zTsa;n5nOTS(-ox&?WBU@y9reP^cpAnowwgnx}yhRdGnA2 zfB*n6frK$Isz6B*B!5e02TR=mcNr1n62JjJ3UVNpL$i^{x4rh|Dk5%-d1iUhB{O-pee`17y&p6EqjKN`6|2d*C> zStN`8vL7h+pD@bJm5bQ($7rQ$?RS_GZ zI;3IQD^ip6=z)}C5TM4f8(C3S;eKU`8nNEjr3FJIf>cx;Jbol1xv46{P>vNqvmv8p zv18F>NTLdeQ4m<9%E&rR6HrKNuT~02;Aq96e|o?M@PG|K$t)jMFo!=9A%(VZZ&VRg zK~#)d3l~_$ABsLm@uAb^N5m=_04Su>Ukf|n4}TK{ks@suXFq~K0021~L~H@V4OA5W zG7Kn)0IYx65I}YW;z0%Qs~^wZy7iLF-)?=0Z0|)~8`{mM~ zXX)ksy&Xf~H{2)#X#OP*pb!DE{9jQD&<(uun7#cy{?`=$?zLAvuO|`jvD6R-c>Rqb zZ`eH>e0Hg8;$_$1rAyCxxvbB+CW^ZzZrUVT-(!2I(LUK@3vAk7F;nm{o)@`Vo||Di za$)rHs8wN zs<&r$!M)Pc?OKGx2QSFu^%M2$ae5IMu7qcMxCr_Xxg}IFdTXA`?b#Y;pX25NuRM^^Le=Ay_ z+H#dFY|L$2thuertuvLO+O{_2fNja8RnI1_BpM4!lM^PM4UdjySCv*J=H^mMBgcUW zh$F}sRPOQ#FWp6*k6W(<>&Z~RqzRIx|ME^CJ>-QC*@sKo7wG*3`$v)liW48EAy%Iw zS4zPhS^L*%`6XBKr_UF6H z@cB(|Y0-7isLz;MHd;Gc4C!fS)Abp7`i{H~Aq-{*px9Rxt}-PMCIPU42@z%-r{l~$ zsEmx%?u$4a$zhwSJCcqrSi|^@_yE>FtimL<0|ir53abxGyr zHHQ|H3>k~%Gp#0rp~cXl(P(3$bWjSA8|q`@;P@Wmw~s15`X?%Y8xOur%~v8b%7~0n zXw*6;k4oL%qvRQZ@hg=o_zh7CHlBPO1GOrxRwM#OYnAcQN^;D9Qxl8hmlr)Z1i-xYJ6>TBcF*s5;Rt#+C;hKq zZ-)IW9+1GVJRGZz!N9x_1P20_djE3Z5}xDi1(X2@f^5MqchEBBzuEYP48X^a{U19& z#=p16Zo?wr#iSQ1pT%r;BI$d9wf8)9{K1>T1F+T>a@xA^@*A|)UNW2K2a|gP5P(Po zu(&7fo=LM$lns>eQ~|;?)OK&%yg;5u$mU$Q=VsE7US3*p%VRN*-Y_nrpvc53qbc68WQx+t7Wn1V2t?3&$qx;Y^ zhPPI>s04y@+Z&A=+bg-6t(67eVs7~iE<)Cl-m3A+-l}qjft?8jwAh`U3<0W>Ylo6c zJB`^KtS{ew_w&*nykI!%#nR{6Ip?YF*#N}aml1#N?*@fC4T1Fj+Rg+}+y!OuAHNpi z;tchBMd${wiC05jicG^3>s{e+O>xQhWQgQ7j3cj0yce zz`sBN;s3UewG&93Fg?O2(#eekjg+C5jJx*U+g;kddm}XYAL?6JX6aE;Ef@L}2gg7M zk}_je`MTyB9ZG?W^wxrO6Y0q?{$|)I3`sf=K%GjRmt-(>6af&<@yP1qW$rX&VQwge zFxvut$8Sp%{AsaDhV8LxJ-OmKaYhsFr@63=AV6=>9smCO_v0fo>&l0!$Omu8)70#0 zsw!wIAh4{XL)T#dq3uXFwYDa8sP?H3My02y^a@?{35|w5Dy}dqQp^BE1#swayds05 zh1>wGP6;5Wi`uQ%s`Bv*RO!@FS$0IMGefYV>J05S?lI2a{>)B(s`U|N97mv4Rh>s#MEdh+Yf&YU@aYI~#n zgO3Wzx3QKzU&5U3HaAv21fZZpl>QV01cS2pWi1dG0Fqxx1}sJZVMqXc)sHDCPj5CM z1chug|C64_czSwR|H06vx!>RORV~;*Cb*0MJ|}(@10r~bJar)o3L*|L!}aSY_=}`p z@AEDEe=Spvn@%h`9N2newP)uF!x0Qx=G~MU_Q3N-*L-E*`b%qNm${qyzj+`S5@EtN zj`OLIvzu%%8?j%WE$_Ovt;{WJx3<@u zf)ZeHZgrgAnl$GOU07S0@@-gxZF36?6FIph3)U`t#fNedbDD}1t|zp&4_7xNS5#FP z*=yQ|m;PIv;~dp@cKUnnJj7es=LbEHywJJiL4n-fwKzW(;>(WS2+zU4PD3pjs%_)2wIed5x?Cu|QA z^$+Nec^{fV#6M*$dl?KU6IXS1X<1cSSyH@6|2Pz}cURh39csXGlUAQmm>!2w(EAZd z22Es|vc6td)4)n>Dkv~@%m8T_nwicg^|gTaCuOh$ur8S%K>lO?KX*YS3)2B`LYu%6 zRSLTCh@%{`he+T*E69pcp!;^FFtV(lGdEmhNU;c3U8-+G0y*#_&2pk|o za_YXs0z-qL0FMGwYf@`RdJ^y008DWaF|m<_Xo#uX(=#;cG;01sSO4jUyYPU%`RHVP zsuB+}4N;{KkfMtUvv%bjjZy&!L};?Oj%ML%lAjWlr9F5;sfdZk=MbASy;`MK{+?PB zlcLxq^8h*i? z05J=|24s`(Q1>J87x*8S5CVx2Ake>{{Y&P*XaMN)g$n@J4hG0gKKfG{PyMBFP13P|F`z%E-mx70v6-~(3iV@nIu4;FH48Hcqi(284BPo zGQ^}=g7EzrODrwTCXAR^V*X%RdvYmH*_0nxjvA{ImZljMQULH<NHs}AL~$D9N8fwzqxh=kn4^2slT7I?8CruW zJ(g|ZVN9k{lYc~=(PS)Wu4$^_pf6xdKuLS#R8_OdwAx{SCXCWfPpRM1a$#nD^+KWK z5yS)6KCLFBNGIxajY#j=Wu}NM+=KLl1tqeD`FO zCh15?6Dh$5n(OQLepHnS2w2YcQZ9JP9j)A2k~$7*b#W|zT?^dalt{9M6q=~K_y6g^ zqaSX3^WdM3>(g1?dX0kl1sP^3XVtq*T5W!$3O9f#sV7h3D`H~U zrJ_|(9+ZlFjZ#>GN%4SkHV_Y%KZid+fHVT&A-y3SMEHXL`TwPZ{_2Mt^_L(0aO~0D zTlH5zIC=Fz-LX&4mv2A^zIu+*yjPY25(y;ffFJ?Q6A;J(cLYTdAX|Vm$^;;(448Ka zP0&1ifbg2f{n~+g|206K9bSFE#RE&gM$;Qy{HkVZB|HU#2uZNp>GwHYHueVLAi{e0+CB6tJ;KTa zi(x_j1&ZXeSCajEs06%?9*O`l2>^|d1OG|>L<#J@cat|ew!bp)()NhS$wEV1+Lcf)svrSZRf$w+|Q5b&1jo;#mql90UocZ7lO zcmO;k#t?o$1poaP$G$PUB7whEdVPe`e&l?au^kd}aU#KE!{~soFO2~hKIE4ko|`66 zeDn_8{_xW=j(ncZ0UHjFaQJfrKrAaWW`kIkCK?k<`NYOT6Hv|7teD3BFYZ{ukv(Y) z)HFI(pKd>?084u#)cT?{LtRsKc3D9Ic^x91vafu(yr8Clm1rvO(5FX2?oTP4GbwZ} zrn&8H*gR_OHLc?X8}#1J=@ zdhyl|4~`%W_^>{?Zu~$&LjYAks=w6%LtaEvQcbI&ytyO2++Zr`nCWO~To_qeD*RS0EMmGe((0wa< zU}y%ppI>^`g)ImuV6Tt|KDW<@SmY3KUQqPo{6o-&@5LlB#O}v%e+Qgv4h-<Mqzp@!js)TI*&{ zXv%_W8L5*lAWQJ@5Fw9Q(8ZUJt5(SrCkyZm;b=5P;2VcE~iK zS-JSXEmM8&b2po15J2<-mD1|x&ia2oD6ByOM1VZ6XdCD%t1%`N0Lyh%5c;2#6ErUb z-0o#xKL%zl>JjEh#*FesgJE#g|%y}G(GwdL7xb=H`-Z3$-=HtfX>Sx)DL<>k=w z;syqR+uMPK@{Sc>$UM_lPLnZIY%CqG=uE8cgb|f(tSV2S1W4{QjsOV)%#9HWA}@Zq z3gFs#FYN;$0Rjf#-3}hewZr~j+F=cf?^7y(`>|2{8-4)7A3V7|v0X$7w|8DI8x^bh ziZQ!1kx(*Bz)2+i8pQDHYa{0{`y9g+qR9A@XVcHd?i+azAz;MrgCC*$SYbehn)e*;8 zeo9R$e?_Ta3s4=6z^7La^LT&!%=+#^&A#IoS-LGM`1z-d1(<=9)#)-6<;?{(tsU$* z2N(d#$A4=}dbuKM_fZ8-U*&mZ5HY7zAmmXW|Mcny5177vnOT^x(iG_q>5?MxDvbu` z|BXDAlBokDh%9V_6_@}Mavm)tL$NrRX<}k@3e|7zkJSCPQpwJwk*L6o0);WqA21Gt z1@MQ||5N~?{FfQQ5CL!(W(e?S{~+YRJ7mCLAqW2Qhi@+5dhm4S25H?7jy!l!$N!nO zIzBibn1u=0FQV1S}$_B`f1b$A|o zraXT4;~xJjflf5{Qq*=4iO_qs9J1h?b#wz)Q; z0Q}7q%>ET>{pUA5n=kyl+8VvCU^wJ-g8_tX9#>E3rYGdIv`yq#zp^;3UH0ILg;?LV z6}Y`&Xna$-RdXJ`w&1rrt;>#uuF5du0SG`-ENg#1fg}FS*Ypgad|}5uI0w?~pS{C_ zLm3l6GC>d>1NL#v+Pn~cIPzrjBJ_f!^o2xw(}8&npLb z3Urk~|H-L%K2%+QC82sKaVTftxvRU}P~u#5A#(PKq`}H=mxX{L7fIz#4{@ej-ND+$ zEnn-(Hd@6BnE#Dt*G5X?K*`*S&z&^hde*$SG3Pg@*P6Rqhvt^s60S6KJ!6Y8PFyj* zOsGmWCXzUAY)ULcag|*rEP}3=LJN{PJv$&EFI@6R@yrSr>;HenLhm*J=@P#w1A|`^ z^ubR6Dgys8uq!PUtYbn+le4Sa83H2xsVb{TK!)&Scx(x+;dgIg3Nn3sg604xz^63( zqtorfqVs1^2uG1$s;Xvx8f`}fI9gg-Er>#l1Bj8L3}_lJE6a=f=FE;k* zzPNLQgpM4HZ{DNUU1+7S5<5jzQZ?q>H5!9~Y>tA0j@DL#POmel^71lFdVLCRVy2|I zlncV%^YkuVT?R5iWkwz+dj>NAEW69!@G~H5Js`%}fDJiyF89rrp26QJzw+Q^y@3 z+8u@He|CJ@VCwgW;9z=H|^5J1ua$jLxr1K<)f04|9N_~?hL z-+cMa2S*+~sd@CljRzl`y857gQRo3a_$=r4AkJOhdD#WP8+7n2DgtQ(kOe_f0lx_ zXaafz7JsPHZ1aEuIMLm%z4+L(vKF??*V>@!JA!rud!G9?rUdwX`}*xYYqOq}{;=JP zNHF9I=JZbm5_4={boy+Q*nT=HS5|s#TWc%k#fgq>r*&f-vrlWzs%LrLIor76#Z^4~ z+R0{O=g8mO^dn#j`8VDD5CJ&psQ`Et2EeZ+Gaweb#|aDQzPC~8`w0S~6cEL~StNeA z$o3LEUzrOm_5m*53QYh7K(3?^kZeJE2P<-uOFDZ4)mM&H8G9?58j^D=+iHV|`Zl%# zUW*gwKUV*O1&>4Df@`aBq1yxDC%I-OFh9P$4kEC+wz0JWVA#^S;9OsI2Ad|T7FSlC z7hJ9_-^_*O+@`i!7!`H^} z|D+`_CXAv2So#{d5H-LUWx%zk*nf^f3>u?cAo-t3A)W%XCL^QQhp!kx1xm|s5J@z0 z=mQ2;u@5w4S0SBlsxE72s*5-yf`MO-T|9Gfbm={=&-N!BQ5?)<3_p}WxnL}Vp^H}c zKxFfgX0a4a21Wwud3rrKKoUXKnEmK624Mp@JJ+`_%T&meFG8cj?E@yRI#R2S;3!r^ zL~2y5^Fp02;`d19(dp9Eks0|dAOqNlgV{w3p?>PI=91PXve9GelQYVj*$bLm3k;^# z@>V@OfPy5$jGoee*ZwFKpX0#r$oBVq55Z%#(yH>8iuJko&uXO!}A{PE+z zsZzi=#5y=uNeC$0Kz>vND&_phSS=+)9;1J)BCU|wfJ#{ylco?D05>wix#Wm&kN`;w z3?T>-5PN`J0|+M2yJ#S;Z+~_7(cK>|U%qkc(T!7A&pbNw;RjcRC&0$3M;8;4FAemE z=7k>^qrm&(Ww8IAKm>!l-3bT8_ruc@nh)R(AgKX`H#lPe$^gHIPQdemJz(|)2*BnG zs_AgZ8}bCm2C}T?Hrs7 z<<^|7a46SlwrqvxYhATw7h8rMet-z`1MqjkTmtqdJj@FYC>FbAmYyH65iWoCy-WZ_ z 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

    -rH_Ux@$r$~e>fdePR$Z7o)tHW-@&XHt;76Oryl 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 diff --git a/fle/cluster/scenarios/default_lab_scenario/script.dat b/fle/cluster/scenarios/default_lab_scenario/script.dat deleted file mode 100644 index 6ed02ae17325594e0b2647a9296391e92b33cc71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1441 zcmb_c!EVz)5Vh0YHcCiSL6CYtqDWkQpf@hmH$G#vu_x}T>s@PiorLlO`~v&|7fxLG z9R7emfalmr8=6E#!cro8X6MbDH!~YYabzMqF@9Xrob>Ncy!q_xe8@(bBN-?cDp2Y9 zF&ijnO;TwXkDAyhx=0yYSetjBzpmJErF^jZ&TuwUfN^C~%0k{MvkrZovS-uU8FERf zj50-%JEiXhj(}q*ncL29VPx(p5_0<9OO3GDTvcX)sRyg0)3iLgie%-CX=$F}>=?5N%gwNZRRbm}jNEFK+SPzKFINdc zSp!E)9|T77Aq0S5>^k`uU{k*`!C3*4$h0Co-nLw#j$vV?MjD4JkH>d3M~yvM-LTeC F{0E) Date: Wed, 30 Jul 2025 11:31:25 +0530 Subject: [PATCH 30/53] moved mods and tools to fle/env/factorio --- fle/env/{ => factorio}/mods/README.md | 0 fle/env/{ => factorio}/mods/alerts.lua | 0 .../mods/build_checkerboard.lua | 0 fle/env/{ => factorio}/mods/checksum.lua | 0 .../{ => factorio}/mods/clear_entities.lua | 0 .../{ => factorio}/mods/clear_inventory.lua | 0 .../{ => factorio}/mods/connection_points.lua | 0 fle/env/{ => factorio}/mods/enemies.lua | 0 fle/env/{ => factorio}/mods/initialise.lua | 0 .../mods/initialise_inventory.lua | 0 .../{ => factorio}/mods/priority_queue.lua | 0 .../{ => factorio}/mods/production_score.lua | 0 .../mods/recipe_fluid_connection_mappings.lua | 0 fle/env/{ => factorio}/mods/reset.lua | 0 .../{ => factorio}/mods/reset_position.lua | 0 fle/env/{ => factorio}/mods/serialize.lua | 0 fle/env/{ => factorio}/mods/util.lua | 0 .../tools/admin/clear_collision_boxes.lua} | 0 .../script/tools/admin/clear_entities.lua} | 0 .../tools/admin/extend_collision_boxes.lua} | 0 .../tools/admin/get_factory_centroid.lua} | 0 .../script/tools/admin/get_messages.lua} | 0 .../script/tools/admin/get_path.lua} | 0 .../tools/admin/get_production_stats.lua} | 0 .../script/tools/admin/inspect_entities.lua} | 0 .../script/tools/admin/load_blueprint.lua} | 0 .../script/tools/admin/load_entity_state.lua} | 0 .../tools/admin/load_research_state.lua} | 0 .../script/tools/admin/production_stats.lua} | 0 .../tools/admin/regenerate_resources.lua} | 0 .../script/tools/admin/render.lua} | 0 .../script/tools/admin/render_message.lua} | 0 .../script/tools/admin/request_path.lua} | 0 .../script/tools/admin/save_blueprint.lua} | 0 .../script/tools/admin/save_entity_state.lua} | 0 .../tools/admin/save_research_state.lua} | 0 .../script/tools/agent/can_place_entity.lua | 84 + .../script/tools/agent/connect_entities.lua} | 0 .../script/tools/agent/craft_item.lua} | 0 .../script/tools/agent/extract_item.lua} | 0 .../tools/agent/get_connection_amount.lua} | 0 .../script/tools/agent/get_entities.lua} | 0 .../script/tools/agent/get_entity.lua} | 0 .../tools/agent/get_prototype_recipe.lua} | 0 .../tools/agent/get_research_progress.lua} | 0 .../tools/agent/get_resource_patch.lua} | 0 .../script/tools/agent/harvest_resource.lua} | 0 .../script/tools/agent/insert_item.lua} | 0 .../script/tools/agent/inspect_inventory.lua | 219 ++ .../script/tools/agent/launch_rocket.lua} | 0 .../script/tools/agent/move_to.lua} | 0 .../script/tools/agent/nearest.lua} | 0 .../script/tools/agent/nearest_buildable.lua} | 0 .../script/tools/agent/pickup_entity.lua} | 0 .../script/tools/agent/place_entity.lua} | 0 .../tools/agent/place_entity_next_to.lua} | 0 .../script/tools/agent/print.lua} | 0 .../script/tools/agent/rotate_entity.lua} | 0 .../script/tools/agent/score.lua | 352 ++++ .../script/tools/agent/send_message.lua} | 0 .../script/tools/agent/set_entity_recipe.lua} | 0 .../script/tools/agent/set_research.lua} | 0 .../script/tools/agent/sleep.lua} | 0 .../script/utils/alerts.lua | 441 ++++ .../script/utils/build_checkerboard.lua | 4 + .../script/utils/checksum.lua | 15 + .../script/utils/clear_entities.lua | 65 + .../script/utils/clear_inventory.lua | 2 + .../script/utils/connection_points.lua | 295 +++ .../script/utils/enemies.lua | 7 + .../script/utils/initialise.lua | 1168 ++++++++++ .../script/utils/initialise_inventory.lua | 9 + .../script/utils/priority_queue.lua | 61 + .../script/utils/production_score.lua | 339 +++ .../recipe_fluid_connection_mappings.lua | 174 ++ .../script/utils/reset.lua | 1 + .../script/utils/reset_position.lua | 2 + .../script/utils/serialize.lua | 1873 +++++++++++++++++ .../script/utils/util.lua | 690 ++++++ fle/env/{ => factorio}/tools/__init__.py | 6 +- .../admin/clear_collision_boxes/client.py | 2 +- .../admin/clear_collision_boxes/server.lua | 42 + .../tools/admin/clear_entities/client.py | 2 +- .../tools/admin/clear_entities/server.lua | 65 + .../admin/extend_collision_boxes/client.py | 2 +- .../admin/extend_collision_boxes/server.lua | 201 ++ .../admin/get_factory_centroid/client.py | 2 +- .../admin/get_factory_centroid/server.lua | 198 ++ .../admin/get_factory_centroid/server2.lua | 0 .../tools/admin/get_messages/server.lua | 1 + .../tools/admin/get_path/client.py | 2 +- .../factorio/tools/admin/get_path/server.lua | 38 + .../admin/get_production_stats/client.py | 2 +- .../admin/get_production_stats/server.lua | 48 + .../tools/admin/inspect_entities/deprecated} | 0 .../tools/admin/inspect_entities/server.lua | 123 ++ .../tools/admin/load_blueprint/client.py | 2 +- .../tools/admin/load_blueprint/server.lua | 64 + .../tools/admin/load_entity_state/client.py | 2 +- .../tools/admin/load_entity_state/server.lua | 289 +++ .../tools/admin/load_research_state/client.py | 2 +- .../admin/load_research_state/server.lua | 48 + .../tools/admin/message_utils.py | 0 .../tools/admin/production_stats/client.py | 2 +- .../tools/admin/production_stats/server.lua | 48 + .../admin/regenerate_resources/client.py | 2 +- .../admin/regenerate_resources/server.lua | 37 + .../tools/admin/render/agent.md | 0 .../tools/admin/render/client.py | 8 +- .../layers/connection_layers_renderer.py | 2 +- .../render/layers/entities_layer_renderer.py | 2 +- .../render/layers/grid_layer_renderer.py | 2 +- .../admin/render/layers/layer_renderer.py | 2 +- .../render/layers/marker_layers_renderer.py | 2 +- .../render/layers/natural_layer_renderer.py | 2 +- .../render/layers/resource_layer_renderer.py | 2 +- .../render/layers/water_layer_renderer.py | 2 +- .../tools/admin/render/rendered_image.py | 0 .../tools/admin/render/renderer.py | 30 +- .../factorio/tools/admin/render/server.lua | 157 ++ .../admin/render/utils/colour_manager.py | 4 +- .../admin/render/utils/connection_renderer.py | 2 +- .../render/utils/electricity_renderer.py | 2 +- .../admin/render/utils/entity_categoriser.py | 0 .../admin/render/utils/image_calculator.py | 2 +- .../admin/render/utils/legend_renderer.py | 8 +- .../admin/render/utils/natural_renderer.py | 2 +- .../tools/admin/render/utils/render_config.py | 0 .../admin/render/utils/shape_renderer.py | 2 +- .../tools/admin/render/utils/tile_renderer.py | 2 +- .../tools/admin/render_message/client.py | 2 +- .../tools/admin/render_message/server.lua | 18 + .../tools/admin/request_path/client.py | 2 +- .../tools/admin/request_path/server.lua | 118 ++ .../tools/admin/save_blueprint/client.py | 2 +- .../tools/admin/save_blueprint/server.lua | 61 + .../tools/admin/save_entity_state/client.py | 2 +- .../tools/admin/save_entity_state/server.lua | 383 ++++ .../tools/admin/save_research_state/client.py | 2 +- .../admin/save_research_state/server.lua | 62 + fle/env/{ => factorio}/tools/agent.md | 0 .../tools/agent/can_place_entity/agent.md | 0 .../tools/agent/can_place_entity/client.py | 2 +- .../tools/agent/can_place_entity/server.lua | 0 .../tools/agent/connect_entities/agent.md | 0 .../tools/agent/connect_entities/client.py | 32 +- .../connect_entities/groupable_entities.py | 0 .../agent/connect_entities/path_result.py | 0 .../tools/agent/connect_entities/resolver.py | 0 .../resolvers/fluid_connection_resolver.py | 2 +- .../resolvers/power_connection_resolver.py | 2 +- .../transport_connection_resolver.py | 2 +- .../tools/agent/connect_entities/server.lua | 1307 ++++++++++++ .../tools/agent/craft_item/agent.md | 0 .../tools/agent/craft_item/client.py | 4 +- .../tools/agent/craft_item/server.lua | 213 ++ .../tools/agent/extract_item/agent.md | 0 .../tools/agent/extract_item/client.py | 2 +- .../tools/agent/extract_item/server.lua | 195 ++ .../agent/get_connection_amount/agent.md | 0 .../agent/get_connection_amount/client.py | 4 +- .../agent/get_connection_amount/server.lua | 0 .../tools/agent/get_entities/agent.md | 0 .../tools/agent/get_entities/client.py | 4 +- .../tools/agent/get_entities/server.lua | 37 + .../tools/agent/get_entity/agent.md | 0 .../tools/agent/get_entity/client.py | 4 +- .../tools/agent/get_entity/server.lua | 48 + .../tools/agent/get_prototype_recipe/agent.md | 0 .../agent/get_prototype_recipe/client.py | 2 +- .../agent/get_prototype_recipe/server.lua | 10 + .../agent/get_research_progress/agent.md | 0 .../agent/get_research_progress/client.py | 2 +- .../agent/get_research_progress/server.lua | 47 + .../tools/agent/get_resource_patch/agent.md | 0 .../tools/agent/get_resource_patch/client.py | 2 +- .../tools/agent/get_resource_patch/server.lua | 105 + .../tools/agent/harvest_resource/agent.md | 0 .../tools/agent/harvest_resource/client.py | 8 +- .../tools/agent/harvest_resource/server.lua | 622 ++++++ .../tools/agent/insert_item/agent.md | 0 .../tools/agent/insert_item/client.py | 4 +- .../tools/agent/insert_item/server.lua | 244 +++ .../tools/agent/inspect_inventory/agent.md | 0 .../tools/agent/inspect_inventory/client.py | 2 +- .../tools/agent/inspect_inventory/server.lua | 0 .../tools/agent/launch_rocket/agent.md | 0 .../tools/agent/launch_rocket/client.py | 4 +- .../tools/agent/launch_rocket/server.lua | 44 + .../tools/agent/move_to/agent.md | 0 .../tools/agent/move_to/client.py | 6 +- .../factorio/tools/agent/move_to/server.lua | 265 +++ .../tools/agent/nearest/agent.md | 0 .../tools/agent/nearest/client.py | 2 +- .../factorio/tools/agent/nearest/server.lua | 71 + .../tools/agent/nearest_buildable/agent.md | 0 .../tools/agent/nearest_buildable/client.py | 2 +- .../tools/agent/nearest_buildable/server.lua | 189 ++ .../tools/agent/pickup_entity/agent.md | 0 .../tools/agent/pickup_entity/client.py | 2 +- .../tools/agent/pickup_entity/server.lua | 154 ++ .../tools/agent/place_entity/agent.md | 0 .../tools/agent/place_entity/client.py | 6 +- .../tools/agent/place_entity/server.lua | 348 +++ .../tools/agent/place_entity_next_to/agent.md | 0 .../agent/place_entity_next_to/client.py | 2 +- .../agent/place_entity_next_to/server.lua | 360 ++++ .../{ => factorio}/tools/agent/print/agent.md | 0 .../tools/agent/print/client.py | 2 +- fle/env/factorio/tools/agent/print/server.lua | 4 + .../tools/agent/rotate_entity/agent.md | 0 .../tools/agent/rotate_entity/client.py | 2 +- .../tools/agent/rotate_entity/server.lua | 64 + .../tools/agent/score/client.py | 2 +- .../tools/agent/score/server.lua | 0 .../tools/agent/send_message/agent.md | 0 .../tools/agent/send_message/client.py | 4 +- .../tools/agent/send_message/server.lua | 1 + .../tools/agent/set_entity_recipe/agent.md | 0 .../tools/agent/set_entity_recipe/client.py | 2 +- .../tools/agent/set_entity_recipe/server.lua | 48 + .../tools/agent/set_research/agent.md | 0 .../tools/agent/set_research/client.py | 2 +- .../tools/agent/set_research/server.lua | 74 + .../tools/agent/shift_entity/client.py | 10 +- .../{ => factorio}/tools/agent/sleep/agent.md | 0 .../tools/agent/sleep/client.py | 2 +- fle/env/factorio/tools/agent/sleep/server.lua | 6 + fle/env/{ => factorio}/tools/controller.py | 0 fle/env/{ => factorio}/tools/init.py | 2 +- fle/env/{ => factorio}/tools/tool.py | 2 +- fle/env/utils/rcon.py | 9 +- 232 files changed, 12387 insertions(+), 128 deletions(-) rename fle/env/{ => factorio}/mods/README.md (100%) rename fle/env/{ => factorio}/mods/alerts.lua (100%) rename fle/env/{ => factorio}/mods/build_checkerboard.lua (100%) rename fle/env/{ => factorio}/mods/checksum.lua (100%) rename fle/env/{ => factorio}/mods/clear_entities.lua (100%) rename fle/env/{ => factorio}/mods/clear_inventory.lua (100%) rename fle/env/{ => factorio}/mods/connection_points.lua (100%) rename fle/env/{ => factorio}/mods/enemies.lua (100%) rename fle/env/{ => factorio}/mods/initialise.lua (100%) rename fle/env/{ => factorio}/mods/initialise_inventory.lua (100%) rename fle/env/{ => factorio}/mods/priority_queue.lua (100%) rename fle/env/{ => factorio}/mods/production_score.lua (100%) rename fle/env/{ => factorio}/mods/recipe_fluid_connection_mappings.lua (100%) rename fle/env/{ => factorio}/mods/reset.lua (100%) rename fle/env/{ => factorio}/mods/reset_position.lua (100%) rename fle/env/{ => factorio}/mods/serialize.lua (100%) rename fle/env/{ => factorio}/mods/util.lua (100%) rename fle/env/{tools/admin/clear_collision_boxes/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/clear_collision_boxes.lua} (100%) rename fle/env/{tools/admin/clear_entities/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/clear_entities.lua} (100%) rename fle/env/{tools/admin/extend_collision_boxes/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/extend_collision_boxes.lua} (100%) rename fle/env/{tools/admin/get_factory_centroid/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/get_factory_centroid.lua} (100%) rename fle/env/{tools/admin/get_messages/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/get_messages.lua} (100%) rename fle/env/{tools/admin/get_path/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/get_path.lua} (100%) rename fle/env/{tools/admin/get_production_stats/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/get_production_stats.lua} (100%) rename fle/env/{tools/admin/inspect_entities/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/inspect_entities.lua} (100%) rename fle/env/{tools/admin/load_blueprint/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/load_blueprint.lua} (100%) rename fle/env/{tools/admin/load_entity_state/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/load_entity_state.lua} (100%) rename fle/env/{tools/admin/load_research_state/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/load_research_state.lua} (100%) rename fle/env/{tools/admin/production_stats/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/production_stats.lua} (100%) rename fle/env/{tools/admin/regenerate_resources/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/regenerate_resources.lua} (100%) rename fle/env/{tools/admin/render/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/render.lua} (100%) rename fle/env/{tools/admin/render_message/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/render_message.lua} (100%) rename fle/env/{tools/admin/request_path/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/request_path.lua} (100%) rename fle/env/{tools/admin/save_blueprint/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/save_blueprint.lua} (100%) rename fle/env/{tools/admin/save_entity_state/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/save_entity_state.lua} (100%) rename fle/env/{tools/admin/save_research_state/server.lua => factorio/scenarios/default_lab_scenario/script/tools/admin/save_research_state.lua} (100%) create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/can_place_entity.lua rename fle/env/{tools/agent/connect_entities/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/connect_entities.lua} (100%) rename fle/env/{tools/agent/craft_item/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/craft_item.lua} (100%) rename fle/env/{tools/agent/extract_item/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/extract_item.lua} (100%) rename fle/env/{tools/admin/inspect_entities/deprecated => factorio/scenarios/default_lab_scenario/script/tools/agent/get_connection_amount.lua} (100%) rename fle/env/{tools/agent/get_entities/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/get_entities.lua} (100%) rename fle/env/{tools/agent/get_entity/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/get_entity.lua} (100%) rename fle/env/{tools/agent/get_prototype_recipe/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/get_prototype_recipe.lua} (100%) rename fle/env/{tools/agent/get_research_progress/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/get_research_progress.lua} (100%) rename fle/env/{tools/agent/get_resource_patch/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/get_resource_patch.lua} (100%) rename fle/env/{tools/agent/harvest_resource/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/harvest_resource.lua} (100%) rename fle/env/{tools/agent/insert_item/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/insert_item.lua} (100%) create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/inspect_inventory.lua rename fle/env/{tools/agent/launch_rocket/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/launch_rocket.lua} (100%) rename fle/env/{tools/agent/move_to/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/move_to.lua} (100%) rename fle/env/{tools/agent/nearest/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/nearest.lua} (100%) rename fle/env/{tools/agent/nearest_buildable/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/nearest_buildable.lua} (100%) rename fle/env/{tools/agent/pickup_entity/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/pickup_entity.lua} (100%) rename fle/env/{tools/agent/place_entity/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity.lua} (100%) rename fle/env/{tools/agent/place_entity_next_to/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity_next_to.lua} (100%) rename fle/env/{tools/agent/print/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/print.lua} (100%) rename fle/env/{tools/agent/rotate_entity/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/rotate_entity.lua} (100%) create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/score.lua rename fle/env/{tools/agent/send_message/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/send_message.lua} (100%) rename fle/env/{tools/agent/set_entity_recipe/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/set_entity_recipe.lua} (100%) rename fle/env/{tools/agent/set_research/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/set_research.lua} (100%) rename fle/env/{tools/agent/sleep/server.lua => factorio/scenarios/default_lab_scenario/script/tools/agent/sleep.lua} (100%) create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/alerts.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/build_checkerboard.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/checksum.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_entities.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_inventory.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/connection_points.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/enemies.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise_inventory.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/priority_queue.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/production_score.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/recipe_fluid_connection_mappings.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset_position.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/serialize.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/util.lua rename fle/env/{ => factorio}/tools/__init__.py (91%) rename fle/env/{ => factorio}/tools/admin/clear_collision_boxes/client.py (88%) create mode 100644 fle/env/factorio/tools/admin/clear_collision_boxes/server.lua rename fle/env/{ => factorio}/tools/admin/clear_entities/client.py (85%) create mode 100644 fle/env/factorio/tools/admin/clear_entities/server.lua rename fle/env/{ => factorio}/tools/admin/extend_collision_boxes/client.py (94%) create mode 100644 fle/env/factorio/tools/admin/extend_collision_boxes/server.lua rename fle/env/{ => factorio}/tools/admin/get_factory_centroid/client.py (98%) create mode 100644 fle/env/factorio/tools/admin/get_factory_centroid/server.lua rename fle/env/{ => factorio}/tools/admin/get_factory_centroid/server2.lua (100%) create mode 100644 fle/env/factorio/tools/admin/get_messages/server.lua rename fle/env/{ => factorio}/tools/admin/get_path/client.py (97%) create mode 100644 fle/env/factorio/tools/admin/get_path/server.lua rename fle/env/{ => factorio}/tools/admin/get_production_stats/client.py (89%) create mode 100644 fle/env/factorio/tools/admin/get_production_stats/server.lua rename fle/env/{tools/agent/get_connection_amount/server.lua => factorio/tools/admin/inspect_entities/deprecated} (100%) create mode 100644 fle/env/factorio/tools/admin/inspect_entities/server.lua rename fle/env/{ => factorio}/tools/admin/load_blueprint/client.py (93%) create mode 100644 fle/env/factorio/tools/admin/load_blueprint/server.lua rename fle/env/{ => factorio}/tools/admin/load_entity_state/client.py (95%) create mode 100644 fle/env/factorio/tools/admin/load_entity_state/server.lua rename fle/env/{ => factorio}/tools/admin/load_research_state/client.py (97%) create mode 100644 fle/env/factorio/tools/admin/load_research_state/server.lua rename fle/env/{ => factorio}/tools/admin/message_utils.py (100%) rename fle/env/{ => factorio}/tools/admin/production_stats/client.py (97%) create mode 100644 fle/env/factorio/tools/admin/production_stats/server.lua rename fle/env/{ => factorio}/tools/admin/regenerate_resources/client.py (87%) create mode 100644 fle/env/factorio/tools/admin/regenerate_resources/server.lua rename fle/env/{ => factorio}/tools/admin/render/agent.md (100%) rename fle/env/{ => factorio}/tools/admin/render/client.py (96%) rename fle/env/{ => factorio}/tools/admin/render/layers/connection_layers_renderer.py (98%) rename fle/env/{ => factorio}/tools/admin/render/layers/entities_layer_renderer.py (97%) rename fle/env/{ => factorio}/tools/admin/render/layers/grid_layer_renderer.py (94%) rename fle/env/{ => factorio}/tools/admin/render/layers/layer_renderer.py (93%) rename fle/env/{ => factorio}/tools/admin/render/layers/marker_layers_renderer.py (96%) rename fle/env/{ => factorio}/tools/admin/render/layers/natural_layer_renderer.py (99%) rename fle/env/{ => factorio}/tools/admin/render/layers/resource_layer_renderer.py (98%) rename fle/env/{ => factorio}/tools/admin/render/layers/water_layer_renderer.py (98%) rename fle/env/{ => factorio}/tools/admin/render/rendered_image.py (100%) rename fle/env/{ => factorio}/tools/admin/render/renderer.py (89%) create mode 100644 fle/env/factorio/tools/admin/render/server.lua rename fle/env/{ => factorio}/tools/admin/render/utils/colour_manager.py (97%) rename fle/env/{ => factorio}/tools/admin/render/utils/connection_renderer.py (98%) rename fle/env/{ => factorio}/tools/admin/render/utils/electricity_renderer.py (98%) rename fle/env/{ => factorio}/tools/admin/render/utils/entity_categoriser.py (100%) rename fle/env/{ => factorio}/tools/admin/render/utils/image_calculator.py (98%) rename fle/env/{ => factorio}/tools/admin/render/utils/legend_renderer.py (99%) rename fle/env/{ => factorio}/tools/admin/render/utils/natural_renderer.py (99%) rename fle/env/{ => factorio}/tools/admin/render/utils/render_config.py (100%) rename fle/env/{ => factorio}/tools/admin/render/utils/shape_renderer.py (99%) rename fle/env/{ => factorio}/tools/admin/render/utils/tile_renderer.py (99%) rename fle/env/{ => factorio}/tools/admin/render_message/client.py (95%) create mode 100644 fle/env/factorio/tools/admin/render_message/server.lua rename fle/env/{ => factorio}/tools/admin/request_path/client.py (97%) create mode 100644 fle/env/factorio/tools/admin/request_path/server.lua rename fle/env/{ => factorio}/tools/admin/save_blueprint/client.py (93%) create mode 100644 fle/env/factorio/tools/admin/save_blueprint/server.lua rename fle/env/{ => factorio}/tools/admin/save_entity_state/client.py (97%) create mode 100644 fle/env/factorio/tools/admin/save_entity_state/server.lua rename fle/env/{ => factorio}/tools/admin/save_research_state/client.py (98%) create mode 100644 fle/env/factorio/tools/admin/save_research_state/server.lua rename fle/env/{ => factorio}/tools/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/can_place_entity/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/can_place_entity/client.py (97%) rename fle/env/{ => factorio}/tools/agent/can_place_entity/server.lua (100%) rename fle/env/{ => factorio}/tools/agent/connect_entities/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/connect_entities/client.py (97%) rename fle/env/{ => factorio}/tools/agent/connect_entities/groupable_entities.py (100%) rename fle/env/{ => factorio}/tools/agent/connect_entities/path_result.py (100%) rename fle/env/{ => factorio}/tools/agent/connect_entities/resolver.py (100%) rename fle/env/{ => factorio}/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py (99%) rename fle/env/{ => factorio}/tools/agent/connect_entities/resolvers/power_connection_resolver.py (98%) rename fle/env/{ => factorio}/tools/agent/connect_entities/resolvers/transport_connection_resolver.py (98%) create mode 100644 fle/env/factorio/tools/agent/connect_entities/server.lua rename fle/env/{ => factorio}/tools/agent/craft_item/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/craft_item/client.py (92%) create mode 100644 fle/env/factorio/tools/agent/craft_item/server.lua rename fle/env/{ => factorio}/tools/agent/extract_item/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/extract_item/client.py (97%) create mode 100644 fle/env/factorio/tools/agent/extract_item/server.lua rename fle/env/{ => factorio}/tools/agent/get_connection_amount/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/get_connection_amount/client.py (90%) create mode 100644 fle/env/factorio/tools/agent/get_connection_amount/server.lua rename fle/env/{ => factorio}/tools/agent/get_entities/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/get_entities/client.py (98%) create mode 100644 fle/env/factorio/tools/agent/get_entities/server.lua rename fle/env/{ => factorio}/tools/agent/get_entity/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/get_entity/client.py (96%) create mode 100644 fle/env/factorio/tools/agent/get_entity/server.lua rename fle/env/{ => factorio}/tools/agent/get_prototype_recipe/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/get_prototype_recipe/client.py (97%) create mode 100644 fle/env/factorio/tools/agent/get_prototype_recipe/server.lua rename fle/env/{ => factorio}/tools/agent/get_research_progress/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/get_research_progress/client.py (97%) create mode 100644 fle/env/factorio/tools/agent/get_research_progress/server.lua rename fle/env/{ => factorio}/tools/agent/get_resource_patch/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/get_resource_patch/client.py (98%) create mode 100644 fle/env/factorio/tools/agent/get_resource_patch/server.lua rename fle/env/{ => factorio}/tools/agent/harvest_resource/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/harvest_resource/client.py (94%) create mode 100644 fle/env/factorio/tools/agent/harvest_resource/server.lua rename fle/env/{ => factorio}/tools/agent/insert_item/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/insert_item/client.py (97%) create mode 100644 fle/env/factorio/tools/agent/insert_item/server.lua rename fle/env/{ => factorio}/tools/agent/inspect_inventory/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/inspect_inventory/client.py (97%) rename fle/env/{ => factorio}/tools/agent/inspect_inventory/server.lua (100%) rename fle/env/{ => factorio}/tools/agent/launch_rocket/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/launch_rocket/client.py (89%) create mode 100644 fle/env/factorio/tools/agent/launch_rocket/server.lua rename fle/env/{ => factorio}/tools/agent/move_to/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/move_to/client.py (94%) create mode 100644 fle/env/factorio/tools/agent/move_to/server.lua rename fle/env/{ => factorio}/tools/agent/nearest/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/nearest/client.py (98%) create mode 100644 fle/env/factorio/tools/agent/nearest/server.lua rename fle/env/{ => factorio}/tools/agent/nearest_buildable/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/nearest_buildable/client.py (98%) create mode 100644 fle/env/factorio/tools/agent/nearest_buildable/server.lua rename fle/env/{ => factorio}/tools/agent/pickup_entity/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/pickup_entity/client.py (98%) create mode 100644 fle/env/factorio/tools/agent/pickup_entity/server.lua rename fle/env/{ => factorio}/tools/agent/place_entity/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/place_entity/client.py (95%) create mode 100644 fle/env/factorio/tools/agent/place_entity/server.lua rename fle/env/{ => factorio}/tools/agent/place_entity_next_to/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/place_entity_next_to/client.py (98%) create mode 100644 fle/env/factorio/tools/agent/place_entity_next_to/server.lua rename fle/env/{ => factorio}/tools/agent/print/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/print/client.py (97%) create mode 100644 fle/env/factorio/tools/agent/print/server.lua rename fle/env/{ => factorio}/tools/agent/rotate_entity/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/rotate_entity/client.py (98%) create mode 100644 fle/env/factorio/tools/agent/rotate_entity/server.lua rename fle/env/{ => factorio}/tools/agent/score/client.py (95%) rename fle/env/{ => factorio}/tools/agent/score/server.lua (100%) rename fle/env/{ => factorio}/tools/agent/send_message/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/send_message/client.py (95%) create mode 100644 fle/env/factorio/tools/agent/send_message/server.lua rename fle/env/{ => factorio}/tools/agent/set_entity_recipe/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/set_entity_recipe/client.py (98%) create mode 100644 fle/env/factorio/tools/agent/set_entity_recipe/server.lua rename fle/env/{ => factorio}/tools/agent/set_research/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/set_research/client.py (97%) create mode 100644 fle/env/factorio/tools/agent/set_research/server.lua rename fle/env/{ => factorio}/tools/agent/shift_entity/client.py (87%) rename fle/env/{ => factorio}/tools/agent/sleep/agent.md (100%) rename fle/env/{ => factorio}/tools/agent/sleep/client.py (96%) create mode 100644 fle/env/factorio/tools/agent/sleep/server.lua rename fle/env/{ => factorio}/tools/controller.py (100%) rename fle/env/{ => factorio}/tools/init.py (87%) rename fle/env/{ => factorio}/tools/tool.py (95%) diff --git a/fle/env/mods/README.md b/fle/env/factorio/mods/README.md similarity index 100% rename from fle/env/mods/README.md rename to fle/env/factorio/mods/README.md diff --git a/fle/env/mods/alerts.lua b/fle/env/factorio/mods/alerts.lua similarity index 100% rename from fle/env/mods/alerts.lua rename to fle/env/factorio/mods/alerts.lua diff --git a/fle/env/mods/build_checkerboard.lua b/fle/env/factorio/mods/build_checkerboard.lua similarity index 100% rename from fle/env/mods/build_checkerboard.lua rename to fle/env/factorio/mods/build_checkerboard.lua diff --git a/fle/env/mods/checksum.lua b/fle/env/factorio/mods/checksum.lua similarity index 100% rename from fle/env/mods/checksum.lua rename to fle/env/factorio/mods/checksum.lua diff --git a/fle/env/mods/clear_entities.lua b/fle/env/factorio/mods/clear_entities.lua similarity index 100% rename from fle/env/mods/clear_entities.lua rename to fle/env/factorio/mods/clear_entities.lua diff --git a/fle/env/mods/clear_inventory.lua b/fle/env/factorio/mods/clear_inventory.lua similarity index 100% rename from fle/env/mods/clear_inventory.lua rename to fle/env/factorio/mods/clear_inventory.lua diff --git a/fle/env/mods/connection_points.lua b/fle/env/factorio/mods/connection_points.lua similarity index 100% rename from fle/env/mods/connection_points.lua rename to fle/env/factorio/mods/connection_points.lua diff --git a/fle/env/mods/enemies.lua b/fle/env/factorio/mods/enemies.lua similarity index 100% rename from fle/env/mods/enemies.lua rename to fle/env/factorio/mods/enemies.lua diff --git a/fle/env/mods/initialise.lua b/fle/env/factorio/mods/initialise.lua similarity index 100% rename from fle/env/mods/initialise.lua rename to fle/env/factorio/mods/initialise.lua diff --git a/fle/env/mods/initialise_inventory.lua b/fle/env/factorio/mods/initialise_inventory.lua similarity index 100% rename from fle/env/mods/initialise_inventory.lua rename to fle/env/factorio/mods/initialise_inventory.lua diff --git a/fle/env/mods/priority_queue.lua b/fle/env/factorio/mods/priority_queue.lua similarity index 100% rename from fle/env/mods/priority_queue.lua rename to fle/env/factorio/mods/priority_queue.lua diff --git a/fle/env/mods/production_score.lua b/fle/env/factorio/mods/production_score.lua similarity index 100% rename from fle/env/mods/production_score.lua rename to fle/env/factorio/mods/production_score.lua diff --git a/fle/env/mods/recipe_fluid_connection_mappings.lua b/fle/env/factorio/mods/recipe_fluid_connection_mappings.lua similarity index 100% rename from fle/env/mods/recipe_fluid_connection_mappings.lua rename to fle/env/factorio/mods/recipe_fluid_connection_mappings.lua diff --git a/fle/env/mods/reset.lua b/fle/env/factorio/mods/reset.lua similarity index 100% rename from fle/env/mods/reset.lua rename to fle/env/factorio/mods/reset.lua diff --git a/fle/env/mods/reset_position.lua b/fle/env/factorio/mods/reset_position.lua similarity index 100% rename from fle/env/mods/reset_position.lua rename to fle/env/factorio/mods/reset_position.lua diff --git a/fle/env/mods/serialize.lua b/fle/env/factorio/mods/serialize.lua similarity index 100% rename from fle/env/mods/serialize.lua rename to fle/env/factorio/mods/serialize.lua diff --git a/fle/env/mods/util.lua b/fle/env/factorio/mods/util.lua similarity index 100% rename from fle/env/mods/util.lua rename to fle/env/factorio/mods/util.lua diff --git a/fle/env/tools/admin/clear_collision_boxes/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_collision_boxes.lua similarity index 100% rename from fle/env/tools/admin/clear_collision_boxes/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_collision_boxes.lua diff --git a/fle/env/tools/admin/clear_entities/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_entities.lua similarity index 100% rename from fle/env/tools/admin/clear_entities/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_entities.lua diff --git a/fle/env/tools/admin/extend_collision_boxes/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/extend_collision_boxes.lua similarity index 100% rename from fle/env/tools/admin/extend_collision_boxes/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/extend_collision_boxes.lua diff --git a/fle/env/tools/admin/get_factory_centroid/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_factory_centroid.lua similarity index 100% rename from fle/env/tools/admin/get_factory_centroid/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_factory_centroid.lua diff --git a/fle/env/tools/admin/get_messages/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_messages.lua similarity index 100% rename from fle/env/tools/admin/get_messages/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_messages.lua diff --git a/fle/env/tools/admin/get_path/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_path.lua similarity index 100% rename from fle/env/tools/admin/get_path/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_path.lua diff --git a/fle/env/tools/admin/get_production_stats/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_production_stats.lua similarity index 100% rename from fle/env/tools/admin/get_production_stats/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_production_stats.lua diff --git a/fle/env/tools/admin/inspect_entities/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/inspect_entities.lua similarity index 100% rename from fle/env/tools/admin/inspect_entities/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/inspect_entities.lua diff --git a/fle/env/tools/admin/load_blueprint/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_blueprint.lua similarity index 100% rename from fle/env/tools/admin/load_blueprint/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_blueprint.lua diff --git a/fle/env/tools/admin/load_entity_state/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_entity_state.lua similarity index 100% rename from fle/env/tools/admin/load_entity_state/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_entity_state.lua diff --git a/fle/env/tools/admin/load_research_state/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_research_state.lua similarity index 100% rename from fle/env/tools/admin/load_research_state/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_research_state.lua diff --git a/fle/env/tools/admin/production_stats/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/production_stats.lua similarity index 100% rename from fle/env/tools/admin/production_stats/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/production_stats.lua diff --git a/fle/env/tools/admin/regenerate_resources/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/regenerate_resources.lua similarity index 100% rename from fle/env/tools/admin/regenerate_resources/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/regenerate_resources.lua diff --git a/fle/env/tools/admin/render/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render.lua similarity index 100% rename from fle/env/tools/admin/render/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render.lua diff --git a/fle/env/tools/admin/render_message/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render_message.lua similarity index 100% rename from fle/env/tools/admin/render_message/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render_message.lua diff --git a/fle/env/tools/admin/request_path/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/request_path.lua similarity index 100% rename from fle/env/tools/admin/request_path/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/request_path.lua diff --git a/fle/env/tools/admin/save_blueprint/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_blueprint.lua similarity index 100% rename from fle/env/tools/admin/save_blueprint/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_blueprint.lua diff --git a/fle/env/tools/admin/save_entity_state/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_entity_state.lua similarity index 100% rename from fle/env/tools/admin/save_entity_state/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_entity_state.lua diff --git a/fle/env/tools/admin/save_research_state/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_research_state.lua similarity index 100% rename from fle/env/tools/admin/save_research_state/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_research_state.lua diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/can_place_entity.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/can_place_entity.lua new file mode 100644 index 000000000..439351f47 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/can_place_entity.lua @@ -0,0 +1,84 @@ +global.actions.can_place_entity = function(player_index, entity, direction, x, y) + local player = global.agent_characters[player_index] + local position = {x = x, y = y} + + ---- Check player's reach distance + local dx = player.position.x - x + local dy = player.position.y - y + local distance = math.sqrt(dx * dx + dy * dy) + + if distance > player.reach_distance then + error("The distance to the target position is too far away to place the entity (" ..distance.."). Move closer.") + end + + -- if not global.actions.can_reach_entity(player, x, y) then + -- error("The target position is too far away to place the entity. The player position is " .. player.position.x .. ", " .. player.position.y .. " and the target position is " .. x .. ", " .. y .. ". Move closer.") + -- end + + -- Check entity prototype exists + if game.entity_prototypes[entity] == nil then + local name = entity:gsub(" ", "_"):gsub("-", "_") + error(name .. " isn't a valid entity prototype. Did you make a typo?") + end + + -- Check inventory for the entity + if player.get_item_count(entity) == 0 then + local name = entity:gsub(" ", "_"):gsub("-", "_") + error("No " .. name .. " in inventory.") + end + + -- Get entity direction and collision box + local direction_enum = get_entity_direction(entity, direction) + local prototype = game.entity_prototypes[entity] + local collision_box = prototype.collision_box + local width = math.abs(collision_box.right_bottom.x - collision_box.left_top.x) + local height = math.abs(collision_box.right_bottom.y - collision_box.left_top.y) + + -- Define the area where the entity will be placed + local target_area = { + {x = position.x - width / 2, y = position.y - height / 2}, + {x = position.x + width / 2, y = position.y + height / 2} + } + + -- Check for collision with other entities + local entities = player.surface.find_entities_filtered{area = target_area, force = player.force} + + -- iterate over entities and remove any with the player_character name + for i = #entities, 1, -1 do + if entities[i].name == "character" or entities[i].name == 'item-on-ground' then + table.remove(entities, i) + end + end + if #entities > 1 then + error("Cannot place the entity at the specified location due to collision with other entities.") + end + + -- Additional checks for specific entities like offshore-pump + if entity == "offshore-pump" then + local tile = player.surface.get_tile(x, y) + if not tile.prototype.name:match("water") then + error("Cannot place the entity at the specified position due to lack of water.") + end + end + + ---- Check if the entity can be placed + --- if placement fails, it tries 11 different positions (i=0 to 10) by moving the player diagonally + local can_build = global.utils.avoid_entity(player_index, entity, position, direction) + if not can_build then + error("Cannot place the entity at the specified position: x="..position.x..", y="..position.y) + end + return true +end + +function get_entity_direction(entity, direction) + if direction == nil then + return defines.direction.north + end + local prototype = game.entity_prototypes[entity] + local cardinals = {defines.direction.north, defines.direction.east, defines.direction.south, defines.direction.west} + if prototype and prototype.type == "inserter" then + return cardinals[(direction % 4) + 1] + else + return cardinals[direction % 4] + end +end \ No newline at end of file diff --git a/fle/env/tools/agent/connect_entities/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/connect_entities.lua similarity index 100% rename from fle/env/tools/agent/connect_entities/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/connect_entities.lua diff --git a/fle/env/tools/agent/craft_item/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/craft_item.lua similarity index 100% rename from fle/env/tools/agent/craft_item/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/craft_item.lua diff --git a/fle/env/tools/agent/extract_item/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/extract_item.lua similarity index 100% rename from fle/env/tools/agent/extract_item/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/extract_item.lua diff --git a/fle/env/tools/admin/inspect_entities/deprecated b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_connection_amount.lua similarity index 100% rename from fle/env/tools/admin/inspect_entities/deprecated rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_connection_amount.lua diff --git a/fle/env/tools/agent/get_entities/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entities.lua similarity index 100% rename from fle/env/tools/agent/get_entities/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entities.lua diff --git a/fle/env/tools/agent/get_entity/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entity.lua similarity index 100% rename from fle/env/tools/agent/get_entity/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entity.lua diff --git a/fle/env/tools/agent/get_prototype_recipe/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_prototype_recipe.lua similarity index 100% rename from fle/env/tools/agent/get_prototype_recipe/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_prototype_recipe.lua diff --git a/fle/env/tools/agent/get_research_progress/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_research_progress.lua similarity index 100% rename from fle/env/tools/agent/get_research_progress/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_research_progress.lua diff --git a/fle/env/tools/agent/get_resource_patch/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_resource_patch.lua similarity index 100% rename from fle/env/tools/agent/get_resource_patch/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_resource_patch.lua diff --git a/fle/env/tools/agent/harvest_resource/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/harvest_resource.lua similarity index 100% rename from fle/env/tools/agent/harvest_resource/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/harvest_resource.lua diff --git a/fle/env/tools/agent/insert_item/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/insert_item.lua similarity index 100% rename from fle/env/tools/agent/insert_item/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/insert_item.lua diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/inspect_inventory.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/inspect_inventory.lua new file mode 100644 index 000000000..1b5c3c96f --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/inspect_inventory.lua @@ -0,0 +1,219 @@ +global.actions.inspect_inventory = function(player_index, is_character_inventory, x, y, entity, all_players) + local position = {x=x, y=y} + local player = global.agent_characters[player_index] + local surface = player.surface + local is_fast = global.fast + local automatic_close = True + + local function get_player_inventory_items(player) + -- if not is_fast then + -- player.opened = player + -- script.on_nth_tick(60, function() + -- if automatic_close == True then + -- player.opened = nil + -- automatic_close = False + -- end + -- end) + -- end + + local inventory = player.get_main_inventory() + if not inventory or not inventory.valid then + return nil + end + + local item_counts = inventory.get_contents() + return item_counts + end + + local function get_inventory() + local closest_distance = math.huge + local closest_entity = nil + + local area = {{position.x - 2, position.y - 2}, {position.x + 2, position.y + 2}} + local buildings = surface.find_entities_filtered({ area = area, force = "player", name = entity }) + -- game.print("Found "..#buildings.. " "..entity) + for _, building in ipairs(buildings) do + if building.name ~= 'character' then + local distance = ((position.x - building.position.x) ^ 2 + (position.y - building.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest_entity = building + end + end + end + + if closest_entity == nil then + error("No entity at given coordinates.") + end + + if not is_fast then + player.opened = closest_entity + script.on_nth_tick(60, function() + if automatic_close == True then + player.opened = nil + automatic_close = False + end + end) + end + + if closest_entity.type == "furnace" then + local source = closest_entity.get_inventory(defines.inventory.furnace_source).get_contents() + local output = closest_entity.get_inventory(defines.inventory.furnace_result).get_contents() + for k, v in pairs(output) do + source[k] = (source[k] or 0) + v + end + return source + end + + if closest_entity.type == "assembling-machine" then + local source = closest_entity.get_inventory(defines.inventory.assembling_machine_input).get_contents() + local output = closest_entity.get_inventory(defines.inventory.assembling_machine_output).get_contents() + for k, v in pairs(output) do + source[k] = (source[k] or 0) + v + end + return source + end + + if closest_entity.type == "lab" then + return closest_entity.get_inventory(defines.inventory.lab_input).get_contents() + end + + -- Handle centrifuge inventories + if closest_entity.type == "assembling-machine" and closest_entity.name == "centrifuge" then + local source = closest_entity.get_inventory(defines.inventory.assembling_machine_input).get_contents() + local output = closest_entity.get_inventory(defines.inventory.assembling_machine_output).get_contents() + -- Merge input and output contents + for k, v in pairs(output) do + source[k] = (source[k] or 0) + v + end + return source + end + + return closest_entity.get_inventory(defines.inventory.chest).get_contents() + end + + local player = global.agent_characters[player_index] + if not player then + error("Player not found") + end + + if all_players then + local all_inventories = {} + for _, p in pairs(global.agent_characters) do + local inventory_items = get_player_inventory_items(p) + if inventory_items then + table.insert(all_inventories, inventory_items) + else + table.insert(all_inventories, {}) + end + end + return dump(all_inventories) + end + + if is_character_inventory then + local inventory_items = get_player_inventory_items(player) + if inventory_items then + return dump(inventory_items) + else + error("Could not get player inventory") + end + else + local inventory_items = get_inventory() + if inventory_items then + return dump(inventory_items) + else + error("Could not get inventory of entity at "..x..", "..y) + end + end +end + +global.actions.inspect_inventory2 = function(player_index, is_character_inventory, x, y) + local position = {x=x, y=y} + local player = global.agent_characters[player_index] + local surface = player.surface + + local function get_player_inventory_items(player) + local inventory = player.get_main_inventory() + if not inventory or not inventory.valid then + return nil + end + + local item_counts = inventory.get_contents() + return item_counts + end + + local function get_inventory() + local closest_distance = math.huge + local closest_entity = nil + + local area = {{position.x - 0.5, position.y - 0.5}, {position.x + 0.5, position.y + 0.5}} + local buildings = surface.find_entities_filtered{area = area, force = "player"} + -- Find the closest building + for _, building in ipairs(buildings) do + if building.rotatable and building.name ~= 'character' then + local distance = ((position.x - building.position.x) ^ 2 + (position.y - building.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest_entity = building + end + end + end + + if closest_entity == nil then + error("No entity at given coordinates.") + end + + -- If the closest entity is a furnace, return the inventory of the furnace + if closest_entity.type == "furnace" then + local source = closest_entity.get_inventory(defines.inventory.furnace_source).get_contents() + local output = closest_entity.get_inventory(defines.inventory.furnace_result).get_contents() + -- Merge the two tables + for k, v in pairs(output) do + source[k] = (source[k] or 0) + v + end + return source + end + + -- If the closest entity is an assembling machine, return the inventory of the assembling machine + if closest_entity.type == "assembling-machine" then + local source = closest_entity.get_inventory(defines.inventory.assembling_machine_input).get_contents() + local output = closest_entity.get_inventory(defines.inventory.assembling_machine_output).get_contents() + -- Merge the two tables + for k, v in pairs(output) do + source[k] = (source[k] or 0) + v + end + return source + end + + -- If the closest entity is a lab, return the inventory of the lab + if closest_entity.type == "lab" then + return closest_entity.get_inventory(defines.inventory.lab_input).get_contents() + end + + -- For other entities (like chests), return the chest inventory + return closest_entity.get_inventory(defines.inventory.chest).get_contents() + end + + local player = global.agent_characters[player_index] + if not player then + error("Player not found") + end + + if is_character_inventory then + local inventory_items = get_player_inventory_items(player) + + if inventory_items then + return dump(inventory_items) + else + error("Could not get player inventory") + end + else + local inventory_items = get_inventory() + + if inventory_items then + return dump(inventory_items) + else + error("Could not get inventory of entity at "..x..", "..y) + end + end +end \ No newline at end of file diff --git a/fle/env/tools/agent/launch_rocket/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/launch_rocket.lua similarity index 100% rename from fle/env/tools/agent/launch_rocket/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/launch_rocket.lua diff --git a/fle/env/tools/agent/move_to/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/move_to.lua similarity index 100% rename from fle/env/tools/agent/move_to/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/move_to.lua diff --git a/fle/env/tools/agent/nearest/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest.lua similarity index 100% rename from fle/env/tools/agent/nearest/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest.lua diff --git a/fle/env/tools/agent/nearest_buildable/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest_buildable.lua similarity index 100% rename from fle/env/tools/agent/nearest_buildable/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest_buildable.lua diff --git a/fle/env/tools/agent/pickup_entity/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/pickup_entity.lua similarity index 100% rename from fle/env/tools/agent/pickup_entity/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/pickup_entity.lua diff --git a/fle/env/tools/agent/place_entity/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity.lua similarity index 100% rename from fle/env/tools/agent/place_entity/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity.lua diff --git a/fle/env/tools/agent/place_entity_next_to/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity_next_to.lua similarity index 100% rename from fle/env/tools/agent/place_entity_next_to/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity_next_to.lua diff --git a/fle/env/tools/agent/print/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/print.lua similarity index 100% rename from fle/env/tools/agent/print/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/print.lua diff --git a/fle/env/tools/agent/rotate_entity/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/rotate_entity.lua similarity index 100% rename from fle/env/tools/agent/rotate_entity/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/rotate_entity.lua diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/score.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/score.lua new file mode 100644 index 000000000..517b6e26f --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/score.lua @@ -0,0 +1,352 @@ +local function get_total_production_counts(production_statistics) + local produced = production_statistics.input_counts + local consumed = production_statistics.output_counts + for name, value in pairs (consumed) do + if produced[name] then + produced[name] = produced[name] - value + else + produced[name] = -value + end + end + return produced +end + +local function get_raw_resources() + local raw_resources = {} + local entities = game.entity_prototypes + for name, entity_prototype in pairs (entities) do + if entity_prototype.resource_category then + if entity_prototype.mineable_properties and entity_prototype.mineable_properties.products then + for k, product in pairs (entity_prototype.mineable_properties.products) do + raw_resources[product.name] = true + end + end + end + if entity_prototype.fluid then + raw_resources[entity_prototype.fluid.name] = true + end + end + return raw_resources +end + +local function get_product_list() + local product_list = {} + local recipes = game.recipe_prototypes + + for recipe_name, recipe_prototype in pairs (recipes) do + if recipe_prototype.allow_decomposition then + local ingredients = recipe_prototype.ingredients + local products = recipe_prototype.products + for k, product in pairs (products) do + if not product_list[product.name] then + product_list[product.name] = {} + end + local recipe_ingredients = {} + local product_amount = util.product_amount(product) + if product_amount > 0 then + for j, ingredient in pairs (ingredients) do + recipe_ingredients[ingredient.name] = ((ingredient.amount)/#products) / product_amount + end + recipe_ingredients.energy = (recipe_prototype.energy / #products) / product_amount + table.insert(product_list[product.name], recipe_ingredients) + end + end + end + end + + local items = game.item_prototypes + local entities = game.entity_prototypes + --[[Now we do some tricky stuff for space science type items]] + local rocket_silos = {} + for k, entity in pairs (entities) do + if entity.type == "rocket-silo" and entity.fixed_recipe then + local recipe = recipes[entity.fixed_recipe] + if not recipe then return end + local required_parts = entity.rocket_parts_required + local list = {} + for k, product in pairs (recipe.products) do + local product_amount = util.product_amount(product) + if product_amount > 0 then + product_amount = product_amount * required_parts + list[product.name] = product_amount + end + end + list["energy"] = recipe.energy + table.insert(rocket_silos, list) + end + end + for k, item in pairs (items) do + local launch_products = item.rocket_launch_products + if launch_products then + for k, launch_product in pairs (launch_products) do + product_list[launch_product.name] = product_list[launch_product.name] or {} + local launch_product_amount = util.product_amount(launch_product) + if launch_product_amount > 0 then + for k, silo_products in pairs (rocket_silos) do + local this_silo = {} + for product_name, product_count in pairs (silo_products) do + this_silo[product_name] = product_count / launch_product_amount + end + this_silo[item.name] = 1 / launch_product_amount + table.insert(product_list[launch_product.name], this_silo) + end + end + end + end + end + return product_list +end + +local default_seed_prices = function() + return + { + ["iron-ore"] = 3.1, + ["copper-ore"] = 3.6, + ["coal"] = 3, + ["stone"] = 2.4, + ["crude-oil"] = 0.2, + ["water"] = 1/1000, + ["steam"] = 1/1000, + ["wood"] = 3.2, + ["raw-fish"] = 100, + ["energy"] = 1, + ["uranium-ore"] = 8.2 + } +end + +local default_ingredient_exponent = function() return 2 end--1.025 end -- We use 2 as the exponent to heavily bias in favour of complex recipes. +local default_raw_resource_price = function() return 2.5 end +local default_resource_ignore = function() return {} end + +local default_param = function() + return + { + ingredient_exponent = 1.025, --[[The exponent for increase in value for each additional ingredient formula exponent^#ingredients-2]] + raw_resource_price = 2.5, --[[If a raw resource isn't given a price, it uses this price]] + resource_ignore = {} --[[This is used to account for mods removing resource generation, in which case we want the item price to be calculated from recipes.]] + } +end + +local ingredient_multiplier = function(recipe, param) + return (param.ingredient_exponent or 1) ^ (table_size(recipe) - 2) +end + +local deduce_nil_prices = function(price_list, param) + local nil_prices = {} + for name, item in pairs (game.item_prototypes) do + if not price_list[name] then + nil_prices[name] = {} + end + end + for name, item in pairs (game.fluid_prototypes) do + if not price_list[name] then + nil_prices[name] = {} + end + end + local recipes = game.recipe_prototypes + for name, recipe in pairs (recipes) do + for k, ingredient in pairs (recipe.ingredients) do + if nil_prices[ingredient.name] then + table.insert(nil_prices[ingredient.name], recipe) + end + end + end + for name, recipes in pairs (nil_prices) do + if #recipes > 0 then + local recipe_cost + local ingredient_amount + for k, recipe in pairs (recipes) do + local ingredient_value = 0 + for k, ingredient in pairs (recipe.ingredients) do + if ingredient.name == name then + ingredient_amount = ingredient.amount + else + local ingredient_price = price_list[ingredient.name] + if ingredient_price then + ingredient_value = ingredient_value + (ingredient_price * ingredient.amount) + else + ingredient_value = nil + break + end + end + end + if not ingredient_value then break end + local product_value = 0 + for k, product in pairs (recipe.products) do + local amount = util.product_amount(product) + local product_price = price_list[product.name] + if product_price then + product_value = product_value + product_price * amount + else + product_value = nil + break + end + end + if not product_value then + break + end + local reverse_price = (product_value - param.energy_addition(recipe, product_value)) / ingredient_multiplier(recipe.ingredients, param) -- Not perfect, but close enough + local this_cost = (reverse_price - ingredient_value) / ingredient_amount + if recipe_cost then + recipe_cost = math.min(recipe_cost, this_cost) + else + recipe_cost = this_cost + end + end + if recipe_cost then + price_list[name] = param.round(recipe_cost) + end + end + end +end + +local ln = math.log +local default_energy_addition = function(recipe, cost) + return ((ln(recipe.energy + 1) * (cost ^ 0.5))) +end + +local default_rounding = function(number) + return number +end + +production_score = {} + +production_score.generate_price_list = function(param) + + local param = param or {} + local price_list = param.seed_prices or default_seed_prices() + + param.ingredient_exponent = param.ingredient_exponent or default_ingredient_exponent() + param.raw_resource_price = param.raw_resource_price or default_raw_resource_price() + param.resource_ignore = param.resource_ignore or default_resource_ignore() + param.round = param.round or default_rounding + param.energy_addition = param.energy_addition or default_energy_addition + param.normalise = param.normalise or function(number) return number end + + local resource_list = get_raw_resources() + + for name, k in pairs (resource_list) do + if not price_list[name] then + price_list[name] = param.round(param.raw_resource_price) + end + end + + for k, name in pairs (param.resource_ignore or {}) do + price_list[name] = nil + end + + local product_list = get_product_list() + local get_price_recursive + get_price_recursive = function(name, current_loop, loop_force) + local price = price_list[name] + if price then return price end + price = 0 + if current_loop[name] then + if loop_force then + return param.raw_resource_price + end + return + end + current_loop[name] = true + local entry = product_list[name] + if not entry then return end + local recipe_cost + for k, recipe in pairs (entry) do + local this_recipe_cost = 0 + for ingredient_name, cost in pairs (recipe) do + if ingredient_name ~= "energy" then + local addition = get_price_recursive(ingredient_name, current_loop, loop_force) + if addition and addition > 0 then + this_recipe_cost = this_recipe_cost + (addition * cost) + else + this_recipe_cost = 0 + break + end + end + end + if this_recipe_cost > 0 then + this_recipe_cost = (this_recipe_cost * ingredient_multiplier(recipe, param)) + param.energy_addition(recipe, this_recipe_cost) + if recipe_cost then + recipe_cost = math.min(recipe_cost, this_recipe_cost) + else + recipe_cost = this_recipe_cost + end + end + end + if recipe_cost then + price = param.round(recipe_cost) + price_list[name] = price + return price + end + end + local items = game.item_prototypes + for name, item in pairs (items) do + local current_loop = {} + get_price_recursive(name, current_loop) + end + local fluids = game.fluid_prototypes + for name, fluid in pairs (fluids) do + local current_loop = {} + get_price_recursive(name, current_loop) + end + deduce_nil_prices(price_list, param) + for name, item in pairs (items) do + local current_loop = {} + get_price_recursive(name, current_loop, true) + end + for name, fluid in pairs (fluids) do + local current_loop = {} + get_price_recursive(name, current_loop, true) + end + deduce_nil_prices(price_list, param) + + for k, price in pairs (price_list) do + price_list[k] = param.normalise(price) + end + + return price_list +end + +production_score.get_production_scores = function(price_list) + local price_list = price_list or production_score.generate_price_list() + local scores = {} + for k, force in pairs (game.forces) do + local score = 0 + for name, value in pairs (get_total_production_counts(force.item_production_statistics)) do + local price = price_list[name] + if price then + score = score + (price * value) + end + end + for name, value in pairs (get_total_production_counts(force.fluid_production_statistics)) do + local price = price_list[name] + if price then + score = score + (price * value) + end + end + scores[force.name] = math.floor(score) + end + return scores +end + +global.goal = nil +global.actions.score = function() + local production_score = production_score.get_production_scores() + production_score["player"] = production_score["player"] - global.initial_score["player"] + + -- Try to get goal description from first player if available, otherwise skip + local goal_description = nil + if #game.players > 0 and game.players[1] and game.players[1].valid then + goal_description = game.players[1].get_goal_description() + end + + if global.goal ~= goal_description then + global.goal = goal_description + --game.print(goal_description) + --if goal_description ~= nil and #goal_description > 1 then + --production_score["goal"] = goal_description[1]:gsub("-", "_") + --end + return dump(production_score) + end + return dump(production_score) +end \ No newline at end of file diff --git a/fle/env/tools/agent/send_message/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/send_message.lua similarity index 100% rename from fle/env/tools/agent/send_message/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/send_message.lua diff --git a/fle/env/tools/agent/set_entity_recipe/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_entity_recipe.lua similarity index 100% rename from fle/env/tools/agent/set_entity_recipe/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_entity_recipe.lua diff --git a/fle/env/tools/agent/set_research/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_research.lua similarity index 100% rename from fle/env/tools/agent/set_research/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_research.lua diff --git a/fle/env/tools/agent/sleep/server.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/sleep.lua similarity index 100% rename from fle/env/tools/agent/sleep/server.lua rename to fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/sleep.lua diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/alerts.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/alerts.lua new file mode 100644 index 000000000..eb5e6228b --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/alerts.lua @@ -0,0 +1,441 @@ +global.alerts = {} + +-- Define a function to check if the transport belt is blocked +function is_transport_belt_blocked(entity) + if entity.type == "transport-belt" then + local line_1 = entity.get_transport_line(1) + local line_2 = entity.get_transport_line(2) + + local direction_vector = { + [0] = {x = 0, y = -1}, -- North + [2] = {x = 1, y = 0}, -- East + [4] = {x = 0, y = 1}, -- South + [6] = {x = -1, y = 0}, -- West + } + + local dx = direction_vector[entity.direction].x + local dy = direction_vector[entity.direction].y + local next_position = {x = entity.position.x + dx, y = entity.position.y + dy} + local next_entities = entity.surface.find_entities_filtered { + area = {{next_position.x - 0.5, next_position.y - 0.5}, {next_position.x + 0.5, next_position.y + 0.5}}, + } + + local has_sink = false + for _, next_entity in ipairs(next_entities) do + if next_entity.type == "transport-belt" or next_entity.type == "splitter" or next_entity.type == "loader" or (next_entity.type == "assembling-machine" and next_entity.get_recipe()) then + has_sink = true + break + end + end + + if not has_sink then + local line_1_moving = line_1 and line_1.get_contents() and not line_1.can_insert_at_back() + local line_2_moving = line_2 and line_2.get_contents() and not line_2.can_insert_at_back() + + if line_1_moving or line_2_moving then + return true + end + end + end + return false +end + +-- Define a function to check if an entity is full +function is_full(entity) + -- Map inventory types to string names + local inventory_names = { + [defines.inventory.rocket_silo_rocket] = "rocket", + [defines.inventory.rocket_silo_input] = "rocket input", + [defines.inventory.rocket_silo_output] = "rocket output", + [defines.inventory.rocket_silo_modules] = "rocket modules", + [defines.inventory.fuel] = "fuel source", + [defines.inventory.burnt_result] = "burnt result", + [defines.inventory.assembling_machine_input] = "assembling machine input", + [defines.inventory.assembling_machine_output] = "assembling machine output", + [defines.inventory.chest] = "chest", + [defines.inventory.furnace_source] = "furnace source", + [defines.inventory.furnace_result] = "furnace result", + } + + local full_inventories = {} + + -- Iterate over all possible inventory types + for inventory_id, inventory_name in pairs(inventory_names) do + local inventory = entity.get_inventory(inventory_id) + -- Check if the entity has this type of inventory and if it's full + if inventory and #inventory > 0 then + for i = 1, #inventory do + -- Check if the slot is completely filled + local stack = inventory[i] + if not stack.valid_for_read or stack.count < stack.prototype.stack_size then -- Slot is not completely filled + goto continue + end + end + + -- Determine a more specific message depending on the entity type + if entity.type == "furnace" then + if inventory_id == defines.inventory.fuel then + inventory_name = "furnace fuel source" + elseif inventory_id == defines.inventory.furnace_source then + inventory_name = "furnace source" + elseif inventory_id == defines.inventory.furnace_result then + inventory_name = "furnace result" + end + elseif entity.type == "assembling-machine" then + if inventory_id == defines.inventory.assembling_machine_input then + inventory_name = "assembler input" + elseif inventory_id == defines.inventory.assembling_machine_output then + inventory_name = "assembler output" + end + elseif entity.type == "rocket-silo" then + if inventory_id == defines.inventory.rocket_silo_input then + inventory_name = "rocket silo input" + elseif inventory_id == defines.inventory.rocket_silo_output then + inventory_name = "rocket silo output" + elseif inventory_id == defines.inventory.rocket_silo_modules then + inventory_name = "rocket silo modules" + elseif inventory_id == defines.inventory.rocket then + inventory_name = "rocket" + end + elseif entity.type == "mining-drill" then + if inventory_id == defines.inventory.chest then + inventory_name = "fuel source" + end + elseif entity.type == "chest" then + if inventory_id == defines.inventory.chest then + inventory_name = "chest storage" + end + end + + table.insert(full_inventories, inventory_name .. " is full") -- Add the name of the full inventory to the list + + ::continue:: + end + end + + return full_inventories +end + +function can_mine(entity) + if entity.type == "mining-drill" then + -- Check if there's no resource under the drill + if entity.mining_target == nil then + return false + end + end + return true +end +-- Define a function to check if the entity is a burner and has fuel +function has_fuel(entity) + if entity.burner then + if not entity.burner.currently_burning then + local fuel_inventory = entity.get_inventory(defines.inventory.fuel) + for item_name, item_count in pairs(fuel_inventory.get_contents()) do + local fuel_value = game.item_prototypes[item_name].fuel_value + if fuel_value and fuel_value > 0 then + return true + end + end + return false + else + return true + end + end + return true +end + +-- Define a function to check if the entity (furnace) has ingredients +function has_ingredients(entity) + if entity.type == "furnace" and entity.get_recipe() == nil then + return false + end + return true +end +-- Define a function to check if the entity is a drill and has space to output +--function has_output_space(entity) +-- if entity.type == "mining-drill" and entity.mining_target then +-- local output_inventory = entity.get_output_inventory() +-- if output_inventory and output_inventory.is_full() then +-- return false +-- end +-- end +-- if entity.type == "mining-drill" then +-- local resource = entity.surface.find_entities_filtered{position = entity.position, type = "resource"}[1] +-- local output_inventory = entity.get_output_inventory() +-- local drop_position = entity.drop_position +-- local items_on_ground = entity.surface.find_entities_filtered{area = {{drop_position.x - 0.5, drop_position.y - 0.5}, {drop_position.x + 0.5, drop_position.y + 0.5}}, type = "item-entity"} +-- +-- if #items_on_ground >= 1 or (resource and output_inventory and not output_inventory.is_empty() and output_inventory and output_inventory.can_insert(resource.prototype.mined_item) == false) then +-- return false +-- end +-- end +-- return true +--end + +function has_output_space(entity) + if entity.status == defines.entity_status.waiting_for_space_in_destination then + return false + end + if entity.type == "mining-drill" and entity.mining_target then + local output_inventory = entity.get_output_inventory() + if output_inventory and output_inventory.is_full() then + return false + end + + local drop_position = entity.drop_position + local items_on_ground = entity.surface.find_entities_filtered{ + area = {{drop_position.x - 0.5, drop_position.y - 0.5}, {drop_position.x + 0.5, drop_position.y + 0.5}}, + type = "item-entity" + } + + local destination_entity = entity.surface.find_entities_filtered{ + position = drop_position, + type = {"container", "transport-belt", "underground-belt", "splitter"} + }[1] + + if #items_on_ground >= 1 then + return false + elseif destination_entity then + if destination_entity.type == "container" then + local chest_inventory = destination_entity.get_inventory(defines.inventory.chest) + return not chest_inventory.is_full() + else + -- For belts, undergrounds, and splitters, assume there's space + return true + end + else + -- No destination entity, check if output inventory is not empty and can't insert more + local resource = entity.mining_target + return not (output_inventory and not output_inventory.is_empty() and + output_inventory.can_insert({name = resource.name, count = 1}) == false) + end + end + return true +end + +--function has_electricity(entity) +-- if entity.prototype.electric_energy_source_prototype then +-- if entity.electric_drain <= 0 then +-- return false +-- end +-- end +-- return true +--end + +function has_electricity(entity) + if entity.prototype.electric_energy_source_prototype then + -- Check if the entity is connected to an electric network + if entity.electric_network_id then + -- Check if the entity is receiving power + local energy_usage = entity.energy / entity.electric_buffer_size + return energy_usage > 0 + else + -- Entity is not connected to an electric network + return false + end + end + -- Entity doesn't require electricity + return true +end + +-- Define a function to check if the entity (boiler) has necessary input liquid +function has_input_liquid(entity) + if entity.type == "boiler" then + local fluid_box = entity.fluidbox[1] + if fluid_box == nil or fluid_box.amount <= 0 then + return false + end + end + -- If the entity is a steam engine, check if it has water + if entity.type == "generator" then + local fluid_box = entity.fluidbox[1] + if fluid_box == nil or fluid_box.amount <= 0 then + return false + end + end + return true +end + +function is_inserter_waiting_for_source(entity) + if entity.type == "inserter" then + local pickup_target = entity.pickup_target + if pickup_target and pickup_target.valid then + local pickup_inventory = pickup_target.get_inventory(defines.inventory.chest) + if pickup_inventory and pickup_inventory.is_empty() then + return true + end + end + end + return false +end + + +function get_issues(entity) + local issues = {} + + if not can_mine(entity) then + table.insert(issues, "\'nothing to mine\'") + end + if not has_fuel(entity) then + table.insert(issues, "\'out of fuel\'") + end + if not has_ingredients(entity) then + table.insert(issues, "\'no ingredients to smelt\'") + end + if is_inserter_waiting_for_source(entity) then + table.insert(issues, "\'inserter waiting for source items\'") + end + + local full_inventories = is_full(entity) + for _, full_inventory in ipairs(full_inventories) do + table.insert(issues, "\'"..full_inventory.."\'") + end + + --if not has_output_space(entity) then + -- if entity.drop_position ~= nil then + -- local rounded_x = round_to_half(entity.drop_position.x) + -- local rounded_y = round_to_half(entity.drop_position.y) + -- + -- table.insert(issues, "\'waiting for space in destination as the output is full. Place a sink object at (" .. entity.drop_position.x .. ", ".. entity.drop_position.y .. ") to unblock.\'") + -- else + -- table.insert(issues, "\'waiting for space in destination\'") + -- end + --end + if not has_output_space(entity) then + if entity.drop_position then + local rounded_x = round_to_half(entity.drop_position.x) + local rounded_y = round_to_half(entity.drop_position.y) + + local destination_entity = entity.surface.find_entities_filtered{ + position = entity.drop_position, + type = {"container", "transport-belt", "underground-belt", "splitter", "furnace"} + }[1] + + if destination_entity then + if destination_entity.type == "container" then + table.insert(issues, "\'chest at drop position is full. Empty the chest at (" .. rounded_x .. ", " .. rounded_y .. ") to continue mining.\'") + elseif destination_entity.type == "furnace" then + table.insert(issues, "\'furnace belt at drop position is blocked.\'") + else + table.insert(issues, "\'transport belt at drop position is blocked. Clear the belt at (" .. rounded_x .. ", " .. rounded_y .. ") to continue mining.\'") + end + else + table.insert(issues, "\'output blocked by item on the ground. There is no sink entity in place to accept the output.\'") + end + else + table.insert(issues, "\'waiting for space in destination\'") + end + end + + if not has_electricity(entity) then + if entity.electric_network_id and (entity.type ~= 'solar-panel' and entity.type ~= 'generator') then + table.insert(issues, "\'not receiving electricity\'") + else + table.insert(issues, "\'not connected to power network\'") + end + end + + local assembler_issue = lacks_assembler_resources(entity) + if assembler_issue then + table.insert(issues, assembler_issue) + end + + if is_transport_belt_blocked(entity) then + table.insert(issues, "\'transport belt blocked. Place an inserter to drain entities from it.\'") + end + + if not has_input_liquid(entity) then + table.insert(issues, "\'no input liquid\'") + end + return issues +end +function is_not_fluid(name) + local items = { "crude-oil", "petroleum-gas", "sulfuric-acid", "water", "steam", "light-oil", "heavy-oil", "lubricant" } + for _,v in pairs(items) do + if v == name then + return false + end + end + return true +end +-- Define a function to check if the entity is an assembler machine and lacks resources +function lacks_assembler_resources(entity) + if entity.type == "assembling-machine" then + local recipe = entity.get_recipe() + if recipe then + local ingredients = recipe.ingredients + local input_inventory = entity.get_inventory(defines.inventory.assembling_machine_input) + local missing_resources = {} + + for _, ingredient in pairs(ingredients) do + -- don't perform a check in the case of liquids like crude-oil, water etc. + if is_not_fluid(ingredient.name) then + if game.item_prototypes[ingredient.name].type ~= "fluid" then + local available_amount = input_inventory.get_item_count(ingredient.name) + local missing_amount = ingredient.amount - available_amount + if missing_amount > 0 then + table.insert(missing_resources, ingredient.name .. " (" .. tostring(missing_amount) .. ")") + end + end + end + end + + if #missing_resources > 0 then + return "\'cannot create " .. recipe.name .. " due to missing resources: " .. table.concat(missing_resources, ",_").."\'" + + end + end + end + return false +end + +-- Define a helper function to round a number to the nearest 0.5 +function round_to_half(number) + return math.floor(number * 2 + 0.5) / 2 +end + + +-- Define a function to be called every tick +local function on_tick(event) + -- Run the check every 60 ticks (1 second) + if event.tick % 60 == 0 then + for _, surface in pairs(game.surfaces) do + local entities = surface.find_entities_filtered({force = "player"}) + for _, entity in pairs(entities) do + local issues = get_issues(entity) + + if #issues > 0 then + local position = entity.position + local entity_key = entity.name .. "_" .. position.x .. "_" .. position.y + local name = '"'..entity.name:gsub(" ", "_")..'"' + if not global.alerts[entity_key] then + global.alerts[entity_key] = { + position = position, + issues = issues, + entity_name = name, + tick = event.tick + } + end + end + end + end + end +end + +-- Define a function to get alerts older than the number of seconds +global.get_alerts = function(seconds) + local current_tick = game.tick + local old_alerts = {} + + for key, alert in pairs(global.alerts) do + if current_tick - alert.tick > 60*seconds then + table.insert(old_alerts, alert) + global.alerts[key] = nil + end + end + + return old_alerts +end + +-- Register the on_tick function to the on_tick event +script.on_event(defines.events.on_tick, on_tick) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/build_checkerboard.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/build_checkerboard.lua new file mode 100644 index 000000000..eb9f267dc --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/build_checkerboard.lua @@ -0,0 +1,4 @@ +local player = global.agent_characters[arg1] +local surface = player.surface + +surface.build_checkerboard({{-100, -100}, {100, 100}}) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/checksum.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/checksum.lua new file mode 100644 index 000000000..cd2c73dbe --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/checksum.lua @@ -0,0 +1,15 @@ +if not global.__lua_script_checksums then + global.__lua_script_checksums = {} +end + +global.get_lua_script_checksums = function() + return game.table_to_json(global.__lua_script_checksums) +end + +global.set_lua_script_checksum = function(name, checksum) + global.__lua_script_checksums[name] = checksum +end + +global.clear_lua_script_checksums = function() + global.__lua_script_checksums = {} +end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_entities.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_entities.lua new file mode 100644 index 000000000..ea8224337 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_entities.lua @@ -0,0 +1,65 @@ +global.actions.clear_entities = function(player_index) + local function clear_area_of_entities(player, area, force_filter) + local surface = player.surface + local entities = surface.find_entities_filtered{ + area = area, + force = force_filter, + type = { + "accumulator", "ammo-turret", "arithmetic-combinator", "artillery-turret", + "assembling-machine", "beacon", "boiler", "constant-combinator", + "container", "curved-rail", "decider-combinator", "electric-pole", + "electric-turret", "fluid-turret", "furnace", "gate", "generator", + "heat-interface", "heat-pipe", "inserter", "lab", "lamp", + "land-mine", "linked-belt", "linked-container", "loader", + "loader-1x1", "market", "mining-drill", "offshore-pump", + "pipe", "pipe-to-ground", "power-switch", "programmable-speaker", + "pump", "radar", "rail-chain-signal", "rail-signal", + "reactor", "roboport", "rocket-silo", "solar-panel", + "splitter", "storage-tank", "straight-rail", "train-stop", + "transport-belt", "underground-belt", "wall" + } + } + + for _, entity in ipairs(entities) do + if entity and entity.valid and entity ~= player then + entity.destroy() + end + end + + -- Clear dropped items separately + local dropped_items = surface.find_entities_filtered{ + area = area, + name = "item-on-ground" + } + for _, item in ipairs(dropped_items) do + if item and item.valid then + item.destroy() + end + end + end + + local function reset_character_inventory(player) + for inventory_id, inventory in pairs(defines.inventory) do + local character_inventory = player.get_inventory(inventory) + if character_inventory then + character_inventory.clear() + end + end + end + + -- Main execution + local player = global.agent_characters[player_index] + local area = { + {player.position.x - 1000, player.position.y - 1000}, + {player.position.x + 1000, player.position.y + 1000} + } + + -- Clear player force entities + clear_area_of_entities(player, area, player.force) + -- Clear neutral force entities + clear_area_of_entities(player, area, "neutral") + + reset_character_inventory(player) + player.force.reset() + return 1 +end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_inventory.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_inventory.lua new file mode 100644 index 000000000..ed66682d1 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_inventory.lua @@ -0,0 +1,2 @@ +global.agent_characters[arg1].clear_items_inside() +rcon.print(1) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/connection_points.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/connection_points.lua new file mode 100644 index 000000000..31ece9561 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/connection_points.lua @@ -0,0 +1,295 @@ +-- This is primarily used to get the connection points of entities for the purpose of blocking them during pathing. +-- This is to prevent bad cases where connections are blocked by belts / pipes etc. + +-- Function to get connection points for storage tanks based on their direction +global.utils.get_storage_tank_connection_points = function(entity) + local x, y = entity.position.x, entity.position.y + local connection_points = {} + + -- Storage tanks have two possible orientations: + -- 1. TopRight/BottomLeft connections (direction = 0 or direction = 2) + -- 2. TopLeft/BottomRight connections (direction = 1 or direction = 3) + + -- Note: entity.direction is in Factorio's 8-way direction system (0-7) + -- We need to handle both orientations (0/2 and 1/3) + if entity.direction == 1 or entity.direction == 2 then + -- TopRight/BottomLeft connections + table.insert(connection_points, {x = x + 1, y = y - 2}) -- Top right - Top + table.insert(connection_points, {x = x + 2, y = y - 1}) -- Top right - Right + + table.insert(connection_points, {x = x - 1, y = y + 2}) -- Bottom left - Bottom + table.insert(connection_points, {x = x - 2, y = y + 1}) -- Bottom left - Left + else + -- TopLeft/BottomRight connections + table.insert(connection_points, {x = x - 1, y = y - 2}) -- Top left - Top + table.insert(connection_points, {x = x - 2, y = y - 1}) -- Top left - Left + + table.insert(connection_points, {x = x + 1, y = y + 2}) -- Bottom right - Bottom + table.insert(connection_points, {x = x + 2, y = y + 1}) -- Bottom right - Right + + end + + return connection_points +end + +global.utils.get_chemical_plant_connection_points = function(plant) + local positions = {} + local x, y = plant.position.x, plant.position.y + + if plant.direction == defines.direction.north then + -- Input pipes + table.insert(positions, {x = x - 1, y = y + 1.5}) + table.insert(positions, {x = x + 1, y = y + 1.5}) + -- Output pipes + table.insert(positions, {x = x - 1, y = y - 1.5}) + table.insert(positions, {x = x + 1, y = y - 1.5}) + elseif plant.direction == defines.direction.south then + -- Input pipes + table.insert(positions, {x = x - 1, y = y - 1.5}) + table.insert(positions, {x = x + 1, y = y - 1.5}) + -- Output pipes + table.insert(positions, {x = x - 1, y = y + 1.5}) + table.insert(positions, {x = x + 1, y = y + 1.5}) + elseif plant.direction == defines.direction.east then + -- Input pipes + table.insert(positions, {x = x - 1.5, y = y - 1}) + table.insert(positions, {x = x - 1.5, y = y + 1}) + -- Output pipes + table.insert(positions, {x = x + 1.5, y = y - 1}) + table.insert(positions, {x = x + 1.5, y = y + 1}) + elseif plant.direction == defines.direction.west then + -- Input pipes + table.insert(positions, {x = x + 1.5, y = y - 1}) + table.insert(positions, {x = x + 1.5, y = y + 1}) + -- Output pipes + table.insert(positions, {x = x - 1.5, y = y - 1}) + table.insert(positions, {x = x - 1.5, y = y + 1}) + end + + return positions +end + + +global.utils.get_generator_connection_positions = function(entity) + local x, y = entity.position.x, entity.position.y + local orientation = entity.orientation + local entity_prototype = game.entity_prototypes[entity.name] + local dx, dy = 0, 0 + local offsetx, offsety = 0, 0 + if orientation == 0 or orientation == defines.direction.north then + dx, dy = 0, -1 + --offsetx, offsety = 0, 0 + elseif orientation == 0.25 or orientation == defines.direction.east then + dx, dy = 1, 0 + --offsetx, offsety = -0.5, 0 + elseif orientation == 0.5 or orientation == defines.direction.south then + dx, dy = 0, -1 + --offsetx, offsety = 0, -0.5 + elseif orientation == 0.75 or orientation == defines.direction.west then + dx, dy = 1, 0 + --offsetx, offsety = 0, 0 + else + -- Log detailed information about the unexpected orientation + local orientation_info = string.format( + "Numeric value: %s, Type: %s, Defines values: N=%s, E=%s, S=%s, W=%s", + tostring(orientation), + type(orientation), + tostring(defines.direction.north), + tostring(defines.direction.east), + tostring(defines.direction.south), + tostring(defines.direction.west) + ) + error(string.format( + "Unexpected orientation for entity: %s at position: %s. Orientation info: %s", + entity.name, + serpent.line(entity.position), + orientation_info + )) + end + local height = entity_prototype.tile_height/2 + local pipe_positions = { + {x = x + (height*dx) + offsetx, y = y + (height*dy) + offsety}, + {x = x - (height*dx) + offsetx, y = y - (height*dy) + offsety} + } + + return pipe_positions +end + +global.utils.get_pumpjack_connection_points = function(entity) + local x, y = entity.position.x, entity.position.y + local orientation = entity.orientation + + local dx, dy + if orientation == 0 or orientation == defines.direction.north then + dx, dy = 1, -2 + elseif orientation == 0.25 or orientation == defines.direction.east then + dx, dy = 2, -1 + elseif orientation == 0.5 or orientation == defines.direction.south then + dx, dy = -1, 2 + elseif orientation == 0.75 or orientation == defines.direction.west then + dx, dy = -2, 1 + end + + local pipe_position = {{x = x + dx, y = y + dy}} + + return pipe_position +end + +global.utils.get_boiler_connection_points = function(entity) + local x, y = entity.position.x, entity.position.y + local orientation = entity.orientation * 8 + + local dx, dy = 0, 0 + if orientation == defines.direction.north then + dx, dy = 1.5, 0.5 + local pipe_positions = { + --water_inputs = water_inputs, + --steam_output = {x = x, y = y - 1*dy} + {x = x + 1*dx, y = y + 1*dy}, + {x = x - 1*dx, y = y + 1*dy}, + {x = x, y = y - 1*dy} + } + + return pipe_positions + elseif orientation == defines.direction.south then + dx, dy = -1.5, -0.5 + local pipe_positions = { + --water_inputs = water_inputs, + --steam_output = {x = x, y = y - 1*dy} + {x = x + 1*dx, y = y + 1*dy}, + {x = x - 1*dx, y = y + 1*dy}, + {x = x, y = y - 1*dy} + } + return pipe_positions + elseif orientation == defines.direction.east then + dx, dy = 0.5, 1.5 + local pipe_positions = { + --water_inputs = water_inputs, + --steam_output = {x = x, y = y - 1*dy} + {x = x + 1*dx, y = y + 1*dy}, + {x = x + 1*dx, y = y - 1*dy}, + {x = x - 1*dx, y = y} + } + return pipe_positions + elseif orientation == defines.direction.west then + dx, dy = -0.5, -1.5 + local pipe_positions = { + --water_inputs = water_inputs, + --steam_output = {x = x, y = y - 1*dy} + {x = x + 1*dx, y = y + 1*dy}, + {x = x + 1*dx, y = y - 1*dy}, + {x = x - 1*dx, y = y} + } + + return pipe_positions + end + --local water_inputs = {} + --water_inputs[1] = {x = x + 1*dx, y = y + 1*dy} + --water_inputs[2] = {x = x - 1*dx, y = y - 1*dy} + + --local pipe_positions = { + -- --water_inputs = water_inputs, + -- --steam_output = {x = x, y = y - 1*dy} + -- {x = x + 1*dx, y = y + 1*dy}, + -- {x = x - 1*dx, y = y + 1*dy}, + -- {x = x, y = y - 1*dy} + --} + --return pipe_positions +end + +global.utils.get_offshore_pump_connection_points = function(entity) + local x, y = entity.position.x, entity.position.y + local orientation = entity.orientation * 8 + + local dx, dy + if orientation == defines.direction.north then + dx, dy = 0, 1 + elseif orientation == defines.direction.south then + dx, dy = 0, -1 + elseif orientation == defines.direction.east then + dx, dy = -1, 0 + elseif orientation == defines.direction.west then + dx, dy = 1, 0 + end + + if dy == nil then + return { {x = x, y = y - 1} } + end + + return { {x = x + dx, y = y + dy} } +end + +global.utils.get_refinery_connection_points = function(refinery) + -- Block the middle input point also + local positions = {} + local x, y = refinery.position.x, refinery.position.y + + if refinery.direction == defines.direction.north then + -- Crude oil input + table.insert(positions, {x = x+1, y = y + 3}) + --table.insert(positions, {x = x+1, y = y + 4}) -- additional clearance + table.insert(positions, {x = x-1, y = y + 3}) + --table.insert(positions, {x = x-1, y = y + 4}) -- additional clearance + + table.insert(positions, {x = x, y = y + 3}) + + -- Outputs (petroleum, light oil, heavy oil) + table.insert(positions, {x = x - 2, y = y - 3}) + --table.insert(positions, {x = x - 2, y = y - 4}) -- additional clearance + + table.insert(positions, {x = x - 1, y = y - 3}) + + table.insert(positions, {x = x, y = y - 3}) + --table.insert(positions, {x = x, y = y - 4}) -- additional clearance + + table.insert(positions, {x = x + 1, y = y - 3}) + + table.insert(positions, {x = x + 2, y = y - 3}) + --table.insert(positions, {x = x + 2, y = y - 4}) -- additional clearance + elseif refinery.direction == defines.direction.south then + -- Crude oil input + table.insert(positions, {x = x+1, y = y - 3}) + --table.insert(positions, {x = x+1, y = y - 4}) -- additional clearance + + table.insert(positions, {x = x, y = y - 3}) + table.insert(positions, {x = x-1, y = y - 3}) + --table.insert(positions, {x = x-1, y = y - 4}) -- additional clearance + + -- Outputs + table.insert(positions, {x = x - 2, y = y + 3}) + --table.insert(positions, {x = x - 2, y = y + 4}) -- additional clearance + + table.insert(positions, {x = x - 1, y = y + 3}) + table.insert(positions, {x = x, y = y + 3}) + --table.insert(positions, {x = x, y = y + 4}) -- additional clearance + table.insert(positions, {x = x + 2, y = y + 3}) + --table.insert(positions, {x = x + 2, y = y + 4}) -- additional clearance + table.insert(positions, {x = x + 1, y = y + 3}) + elseif refinery.direction == defines.direction.east then + -- Crude oil input + table.insert(positions, {x = x - 3, y = y+1}) + table.insert(positions, {x = x - 4, y = y+1}) + table.insert(positions, {x = x - 3, y = y}) + table.insert(positions, {x = x - 3, y = y-1}) + table.insert(positions, {x = x - 4, y = y-1}) + -- Outputs + table.insert(positions, {x = x + 3, y = y - 2}) + table.insert(positions, {x = x + 3, y = y - 1}) + table.insert(positions, {x = x + 3, y = y}) + table.insert(positions, {x = x + 3, y = y + 1}) + table.insert(positions, {x = x + 3, y = y + 2}) + elseif refinery.direction == defines.direction.west then + -- Crude oil input + table.insert(positions, {x = x + 3, y = y+1}) + table.insert(positions, {x = x + 3, y = y}) + table.insert(positions, {x = x + 3, y = y-1}) + -- Outputs + table.insert(positions, {x = x - 3, y = y - 2}) + table.insert(positions, {x = x - 3, y = y - 1}) + table.insert(positions, {x = x - 3, y = y}) + table.insert(positions, {x = x - 3, y = y + 1}) + table.insert(positions, {x = x - 3, y = y + 2}) + end + + return positions +end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/enemies.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/enemies.lua new file mode 100644 index 000000000..95c64a104 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/enemies.lua @@ -0,0 +1,7 @@ +game.forces["enemy"].kill_all_units() -- Removes all biters +game.map_settings.enemy_expansion.enabled = false -- Stops biters from expanding +game.map_settings.enemy_evolution.enabled = false -- Stops biters from evolving +local surface = game.surfaces[1] +for _, entity in pairs(surface.find_entities_filtered({type="unit-spawner"})) do + entity.destroy() +end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise.lua new file mode 100644 index 000000000..9d401830d --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise.lua @@ -0,0 +1,1168 @@ +-- Initialise.lua + +-- If the global actions table doesn't exist, create it +if not global.actions then + global.actions = {} +end +if not global.utils then + global.utils = {} +end + +-- Initialize debug flags +if global.debug == nil then + global.debug = { + rendering = false -- Flag to toggle debug rendering of polygons and shapes + } +end + + +-- Note: The debug_rendering.lua library will be loaded separately by the LuaScriptManager +--local player = game.players[arg1] +player.surface.always_day=true +--game.players[1].character_collision_mask = "not-colliding-with-itself" +player.force.character_build_distance_bonus = 100 +player.force.research_all_technologies() + + +local beam_duration = 9 +local surface=player.surface +local pp = player.position +local cnt = 0 +local directions = {'north', 'northeast', 'east', 'southeast', 'south', 'southwest', 'west', 'northwest'} + +--Initialise.lua +--local position = player.position + +--player.game_view_settings.show_controller_gui = false +--player.game_view_settings.show_minimap = false +--player.game_view_settings.show_side_menu = false +--player.game_view_settings.show_quickbar = false +--player.game_view_settings.show_shortcut_bar = false +--player.game_view_settings.show_map_view_options = false +--player.game_view_settings.show_shortcut_bar = false + +global.points_of_interest = {} +global.distances_to_nearest = {} +global.relative_points_of_interest = {} +global.interesting_entities = { + ['enemy']=true, + ['trees']=true, + ['iron-ore']=true, + ['stone']=true, + ['copper-ore']=true, + ['uranium-ore']=true, + ['crude-oil']=true, + ['water']=true, + ['coal']=true +} +global.initial_score = {} + +-- Check if a player's inventory is empty +local function check_player_inventory_empty(player) + local inventory = player.get_main_inventory() + return inventory.is_empty() +end + +global.utils.get_direction = function(from_position, to_position) + local dx = to_position.x - from_position.x + local dy = to_position.y - from_position.y + local adx = math.abs(dx) + local ady = math.abs(dy) + local diagonal_threshold = 0.5 + + if adx > ady then + if dx > 0 then + return (ady / adx > diagonal_threshold) and (dy > 0 and 3 or 1) or 2 + else + return (ady / adx > diagonal_threshold) and (dy > 0 and 5 or 7) or 6 + end + else + if dy > 0 then + return (adx / ady > diagonal_threshold) and (dx > 0 and 3 or 5) or 4 + else + return (adx / ady > diagonal_threshold) and (dx > 0 and 1 or 7) or 0 + end + end +end + +global.utils.get_direction_with_diagonals = function(from_pos, to_pos) + local dx = to_pos.x - from_pos.x + local dy = to_pos.y - from_pos.y + + if dx == 0 and dy == 0 then + return nil + end + + -- Check for cardinal directions first + local cardinal_margin = 0.20 --0.25 + if math.abs(dx) < cardinal_margin then + return dy > 0 and defines.direction.south or defines.direction.north + elseif math.abs(dy) < cardinal_margin then + return dx > 0 and defines.direction.east or defines.direction.west + end + + -- Handle diagonal directions + if dx > 0 then + return dy > 0 and defines.direction.southeast or defines.direction.northeast + else + return dy > 0 and defines.direction.southwest or defines.direction.northwest + end +end + + +global.utils.get_closest_entity = function(player, position) + local closest_distance = math.huge + local closest_entity = nil + local entities = player.surface.find_entities_filtered{ + position = position, + force = "player", + radius = 3 + } + + for _, entity in ipairs(entities) do + if entity.name ~= 'character' and entity.name ~= 'laser-beam' then + local distance = ((position.x - entity.position.x) ^ 2 + (position.y - entity.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest_entity = entity + end + end + end + + return closest_entity +end + +global.utils.is_position_blocked = function(player, position) + rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 1, g = 0, b = 1}, surface = player.surface, radius = 0.5, filled = false, target = position, time_to_live = 60000} + + local can_place = player.surface.can_place_entity{ + name = "simple-entity-with-owner", + position = position, + } + return not can_place +end + + +global.utils.calculate_movement_ticks = function(player, from_pos, to_pos) + -- Calculate distance between points + local dx = to_pos.x - from_pos.x + local dy = to_pos.y - from_pos.y + local distance = math.sqrt(dx * dx + dy * dy) + + -- Get player's walking speed (tiles per tick) + -- Character base speed is 0.15 tiles/tick + local walking_speed = player.character_running_speed + if not walking_speed or walking_speed == 0 then + walking_speed = 0.15 -- Default walking speed + end + + -- Calculate ticks needed for movement + return math.ceil(distance / walking_speed) +end + +-- Wrapper around LuaSurface.can_place_entity that replicates all checks LuaPlayer.can_place_entity performs. +-- This allows our code to validate placement without relying on an actual LuaPlayer instance. +-- extra_params can be provided by callers to pass additional flags (e.g. fast_replace) if needed. +global.utils.can_place_entity = function(player, entity_name, position, direction, extra_params) + local params = extra_params or {} + params.name = entity_name + params.position = position + params.direction = direction + params.force = player.force + -- Use the manual build-check path so the engine applies the same rules as when a human player builds. + params.build_check_type = defines.build_check_type.manual + return player.surface.can_place_entity(params) +end + +global.actions.can_reach = function(player, x, y) + local dx = player.position.x - x + local dy = player.position.y - y + local distance = math.sqrt(dx * dx + dy * dy) + + if distance > player.reach_distance then + return false + end + return true +end + +global.utils.get_placement_issues = function(player, entity_name, position, direction) + local reasons = {} + + -- Check if entity prototype exists + local prototype = game.entity_prototypes[entity_name] + if not prototype then + table.insert(reasons, "invalid entity name " .. entity_name) + return reasons + end + + -- Check if player has required technology + if not player.force.recipes[entity_name] then + table.insert(reasons, "required technology not researched") + end + + -- Get rotated collision box + local collision_box = prototype.collision_box + local box_width = collision_box.right_bottom.x - collision_box.left_top.x + local box_height = collision_box.right_bottom.y - collision_box.left_top.y + + local rotated_box + if direction and (direction == defines.direction.east or direction == defines.direction.west) then + -- Swap width and height for east/west orientations + rotated_box = { + left_top = {x = position.x - box_height/2, y = position.y - box_width/2}, + right_bottom = {x = position.x + box_height/2, y = position.y + box_width/2} + } + else + rotated_box = { + left_top = {x = position.x - box_width/2, y = position.y - box_height/2}, + right_bottom = {x = position.x + box_width/2, y = position.y + box_height/2} + } + end + + -- Check for collisions with existing entities using rotated box + local entities = player.surface.find_entities_filtered{area = rotated_box} + if #entities > 0 then + local colliding = {} + for _, entity in pairs(entities) do + if entity.valid and entity.name ~= 'character' then + local entity_prototype = game.entity_prototypes[entity.name] + if entity_prototype and entity_prototype.collision_mask and prototype.collision_mask then + -- Check if collision masks overlap + for mask_type in pairs(prototype.collision_mask) do + if entity_prototype.collision_mask[mask_type] then + table.insert(colliding, entity.name) + break + end + end + end + end + end + if #colliding > 0 then + table.insert(reasons, "colliding with " .. table.concat(colliding, ", ")) + end + end + + -- Check all tiles in the rotated area for collision and water + local left = math.floor(rotated_box.left_top.x) + local right = math.ceil(rotated_box.right_bottom.x) + local top = math.floor(rotated_box.left_top.y) + local bottom = math.ceil(rotated_box.right_bottom.y) + + local water_tiles = 0 + local total_tiles = 0 + local problematic_tiles = {} + + for x = left, right do + for y = top, bottom do + local tile = player.surface.get_tile(x, y) + if tile then + total_tiles = total_tiles + 1 + + -- Water checks + if tile.prototype.collision_mask["water-tile"] then + water_tiles = water_tiles + 1 + end + + -- General tile collision check + if prototype.collision_mask then + for mask_type in pairs(prototype.collision_mask) do + if mask_type ~= "water-tile" and tile.prototype.collision_mask[mask_type] then + if not problematic_tiles[tile.name] then + problematic_tiles[tile.name] = true + end + break + end + end + end + end + end + end + + -- Water placement logic + if not prototype.collision_mask["water-tile"] and water_tiles == 0 then + table.insert(reasons, "must be placed on water") + elseif prototype.collision_mask["water-tile"] and water_tiles > 0 then + table.insert(reasons, "cannot build on water") + end + + -- Add problematic tiles to reasons + for tile_name, _ in pairs(problematic_tiles) do + table.insert(reasons, "cannot build on " .. tile_name) + end + + -- Check if outside map bounds + local map_bounds = {{-1000000, -1000000}, {1000000, 1000000}} -- Adjust as needed + if rotated_box.left_top.x < map_bounds[1][1] or rotated_box.right_bottom.x > map_bounds[2][1] or + rotated_box.left_top.y < map_bounds[1][2] or rotated_box.right_bottom.y > map_bounds[2][2] then + table.insert(reasons, "position outside map bounds") + end + + -- Check if player has required items in inventory + if prototype.items_to_place_this then + for _, stack in ipairs(prototype.items_to_place_this) do + local count = stack.count or 1 + local inventory_count = player.get_item_count(stack.name) + if inventory_count < count then + table.insert(reasons, string.format("missing %d %s", count - inventory_count, stack.name)) + end + end + end + + return reasons +end + +global.utils.describe_placement_issues = function(player, entity_name, position, direction) + local issues = global.utils.get_placement_issues(player, entity_name, position, direction) + if #issues > 0 then + return "Cannot build because: " .. table.concat(issues, "; ") + end + return "" +end + +global.utils.avoid_entity = function(player_index, entity, position, direction) + local player = global.agent_characters[player_index] + local player_position = player.position + for i=0, 10 do + local can_place = player.surface.can_place_entity{ + name = entity, + force = "player", + position = position, + direction = global.utils.get_entity_direction(entity, direction) + } + if can_place then + return true + end + player.teleport({player_position.x + i, player_position.y + i}) + end + player.teleport(player_position) + return false +end + + + +---- Define a function to be called every tick +--local function on_tick(event) +-- -- Run the check every 60 ticks (1 second) +-- if event.tick % 60 == 0 then +-- for _, player in pairs(game.connected_players) do +-- if check_player_inventory_empty(player) then +-- -- Perform an action or notify the player when their inventory is empty +-- player.print("Your inventory is empty!") +-- end +-- end +-- end +--end +-- +---- Register the on_tick function to the on_tick event +--script.on_event(defines.events.on_tick, on_tick) + +--script.on_nth_tick(3600, function(event) +-- game.take_screenshot{ +-- surface=game.surfaces[1], +-- position={0,0}, +-- resolution={2560, 1600}, +-- zoom=0.2, +-- path="timelapse/" .. string.format("%06d", event.tick/event.nth_tick) .. ".jpg", +-- show_entity_info=true, +-- allow_in_replay=true, +-- daytime=1 +-- } +--end) + + +for _, ent in pairs(surface.find_entities_filtered({force="player", position=pp, radius=50})) do + if ent.name ~= "character" then + ent.destroy() + end +end + +for key, entity in pairs(surface.find_entities_filtered({force="enemy", radius=250, position=pp })) do + cnt = cnt+1 + entity.destroy() + end + +global.crafting_queue = {} + +script.on_event(defines.events.on_tick, function(event) + -- Iterate over the crafting queue and update the remaining ticks + for i, task in ipairs(global.crafting_queue) do + task.remaining_ticks = task.remaining_ticks - 1 + + -- If the crafting is finished, consume the ingredients, insert the crafted entity, and remove the task from the queue + if task.remaining_ticks <= 0 then + for _, ingredient in pairs(task.recipe.ingredients) do + task.player.remove_item({name = ingredient.name, count = ingredient.amount * task.count}) + end + task.player.insert({name = task.entity_name, count = task.count}) + table.remove(global.crafting_queue, i) + end + end +end) + +function abort(message) + local msg = tostring(message):gsub(" ", "_") + rcon.print(msg) +end + +function create_beam_bounding_box (player, surface, direction, top_left, bottom_right) + local bottom_left = {x=top_left.x, y=bottom_right.y} + local top_right = {x=bottom_right.x, y=top_left.y} + local direction = 0 + surface.create_entity{name='laser-beam', position=player.position, source_position=top_left, target_position=top_right, duration=beam_duration, direction=direction, force='neutral', player=player} + surface.create_entity{name='laser-beam', position=player.position, source_position=top_right, target_position=bottom_right, duration=beam_duration, direction=direction, force='neutral', player=player} + surface.create_entity{name='laser-beam', position=player.position, source_position=bottom_right, target_position=bottom_left, duration=beam_duration, direction=direction, force='neutral', player=player} + surface.create_entity{name='laser-beam', position=player.position, source_position=bottom_left, target_position=top_left, duration=beam_duration, direction=direction, force='neutral', player=player} + surface.create_entity{name='laser-beam', position=player.position, source_position=player.position, duration=beam_duration, target_position={x=player.position.x, y=player.position.y+0.1}, direction=direction, force='neutral', player=player} +end + +function create_arrow_with_direction(player, direction, position) + local beam_length = 0.75 + local arrow_width = 0.5 + + -- Calculate the end position of the main beam + local end_position = {x = position.x, y = position.y} + local dx, dy = 0, 0 + + if direction == 0 then -- North + dy = -beam_length + elseif direction == 2 then -- East + dx = beam_length + elseif direction == 4 then -- South + dy = beam_length + elseif direction == 6 then -- West + dx = -beam_length + elseif direction == 1 then -- Northeast + dx, dy = beam_length * 0.7071, -beam_length * 0.7071 + elseif direction == 3 then -- Southeast + dx, dy = beam_length * 0.7071, beam_length * 0.7071 + elseif direction == 5 then -- Southwest + dx, dy = -beam_length * 0.7071, beam_length * 0.7071 + elseif direction == 7 then -- Northwest + dx, dy = -beam_length * 0.7071, -beam_length * 0.7071 + end + + end_position.x = position.x + dx + end_position.y = position.y + dy + + -- Create the main beam + player.surface.create_entity{ + name = 'laser-beam', + position = position, + source_position = position, + target_position = end_position, + duration = 6000, + force = 'neutral', + player = player + } + + -- Calculate and create the two side beams for the arrowhead + local perpendicular_dx = -dy * arrow_width + local perpendicular_dy = dx * arrow_width + + local arrow_left = { + x = end_position.x + perpendicular_dx - dx * 0.3, + y = end_position.y + perpendicular_dy - dy * 0.3 + } + local arrow_right = { + x = end_position.x - perpendicular_dx - dx * 0.3, + y = end_position.y - perpendicular_dy - dy * 0.3 + } + + player.surface.create_entity{ + name = 'laser-beam', + position = end_position, + source_position = end_position, + target_position = arrow_left, + duration = 100000, + force = 'neutral', + player = player + } + + player.surface.create_entity{ + name = 'laser-beam', + position = end_position, + source_position = end_position, + target_position = arrow_right, + duration = 100000, + force = 'neutral', + player = player + } +end + +function create_beam_point_with_direction (player, direction, position) + -- Calculate the end position of the beam based on the direction. + local end_position = {x=position.x, y=position.y} + local length = 0.5 + if direction == defines.direction.north then + end_position.y = end_position.y - length + elseif direction == defines.direction.south then + end_position.y = end_position.y + length + elseif direction == defines.direction.west then + end_position.x = end_position.x - length + elseif direction == defines.direction.east then + end_position.x = end_position.x + length + elseif direction == defines.direction.northeast then + end_position.x = end_position.x + length + end_position.y = end_position.y - length + elseif direction == defines.direction.southeast then + end_position.x = end_position.x + length + end_position.y = end_position.y + length + elseif direction == defines.direction.southwest then + end_position.x = end_position.x - length + end_position.y = end_position.y + length + elseif direction == defines.direction.northwest then + end_position.x = end_position.x - length + end_position.y = end_position.y - length + end + + -- Create the beam entity. + player.surface.create_entity{ + name='laser-beam', + position=position, + source_position=position, + target_position=end_position, + duration=60000, + direction=direction, + force='neutral', + player=player + } +end +function create_beam_point (player, position) + -- Create beams in all four cardinal directions. + create_beam_point_with_direction(player, defines.direction.north, position) + create_beam_point_with_direction(player, defines.direction.south, position) + create_beam_point_with_direction(player, defines.direction.west, position) + create_beam_point_with_direction(player, defines.direction.east, position) +end + + +function observe_points_of_interest (surface, player, search_radius) + local enemy = surface.find_nearest_enemy{position=player.position, max_distance=search_radius} + if enemy ~= nil then + global.points_of_interest['enemy'] = enemy.position + end + + for k, v in pairs(global.points_of_interest) do + global.relative_points_of_interest[k] = { + x=v.x-player.position.x, + y=v.y-player.position.y + } + end + + rcon.print(1) + return dump(global.relative_points_of_interest) +end + +function find_passable_tiles(player, localBoundingBox) + -- Generate a 100x100 bounding box centered on the player + local origin = player.position + local offset = localBoundingBox / 2 + + local left = origin.x - offset + local right = origin.x + offset + local top = origin.y - offset + local bottom = origin.y + offset + + local bounding_box = { + left_top = {x = left, y = top}, + right_bottom = {x = right, y = bottom} + } + local impassable_tiles = {} + for x = left, right do + for y = top, bottom do + local tile = player.surface.get_tile(x, y) + local is_impassable = tile.prototype.collision_mask["player-layer"] + + if is_impassable then + --(x - xmin) * localBoundingBox + (y - ymin) + local relative_x, relative_y = x + offset, y + offset + --local index = coords_to_index(relative_x, relative_y) + local index = relative_y * localBoundingBox + relative_x + if not impassable_tiles[index] then + impassable_tiles[index] = 100 + end + end + end + end + + -- Find all entities within the bounding box + local entities = player.surface.find_entities_filtered{area = {{left, top}, {right, bottom}}, force = player.force} + + for _, entity in ipairs(entities) do + local collision_box = entity.prototype.collision_box + local is_passable = (collision_box.left_top.x == 0 and collision_box.left_top.y == 0 and + collision_box.right_bottom.x == 0 and collision_box.right_bottom.y == 0) + if not is_passable then -- Change this condition to check for impassable entities + local x, y = math.floor(entity.position.x), math.floor(entity.position.y) + local relative_x, relative_y = x - left, y - top + local index = relative_y * localBoundingBox + relative_x + + if not impassable_tiles[index] then + impassable_tiles[index] = 99--{x = relative_x, y = relative_y, entity = entity} + end + end + end + + -- Convert the X, Y coordinates of each entity into coordinates relative to the origin + local relative_entities = {} + for _, entity in ipairs(entities) do + table.insert(relative_entities, {x = left, y = top, entity = entity}) + end + + -- Create a 1D sparse index of the x, y coordinate and entity, starting from the top left of the bounding box + local sparse_index = {} + for _, relative_entity in ipairs(relative_entities) do + -- Ensure the coordinates are integers by rounding them + local index_x = math.floor(relative_entity.x + offset) + local index_y = math.floor(relative_entity.y + offset) + + local index = index_y * localBoundingBox + index_x + --impassable_tiles[index] = 99 + end + + rcon.print(1) + return impassable_tiles +end + +function get_locality(position, surface, localBoundingBox) + local origin = position + local offset = localBoundingBox / 2 + + local left = origin.x - offset + local right = origin.x + offset + local top = origin.y - offset + local bottom = origin.y + offset + + local bounding_box = { + left_top = {x = left, y = top}, + right_bottom = {x = right, y = bottom} + } + + local entities = surface.find_entities(bounding_box) + + local relative_entities = {} + for _, entity in ipairs(entities) do + local relative_x = entity.position.x - origin.x + local relative_y = entity.position.y - origin.y + table.insert(relative_entities, {x = relative_x, y = relative_y, entity = entity}) + end + + local sparse_index = {} + for _, relative_entity in ipairs(relative_entities) do + local index_x = math.floor(relative_entity.x + offset) + local index_y = math.floor(relative_entity.y + offset) + + local index = index_y * localBoundingBox + index_x + local name = relative_entity.entity.name + sparse_index[index] = name:gsub("-", "_") + end + + -- Find water tiles + local water_tiles = surface.find_tiles_filtered{area=bounding_box, name={"water", "deepwater"}} + for _, tile in ipairs(water_tiles) do + local relative_x = tile.position.x - origin.x + local relative_y = tile.position.y - origin.y + + local index_x = math.floor(relative_x + offset) + local index_y = math.floor(relative_y + offset) + + local index = index_y * localBoundingBox + index_x + local name = "water" + sparse_index[index] = name:gsub("-", "_") + end + + rcon.print(1) + return sparse_index +end + + +function has_value(tbl, val) + for _, value in ipairs(tbl) do + if value == val then + return true + end + end + return false +end + +function get_local_environment (position, surface, localBoundingBox, field_x, field_y, debug) + + local top = position.y-localBoundingBox/2 + local left = position.x-localBoundingBox/2 + local bottom = position.y+localBoundingBox/2 + local right = position.x+localBoundingBox/2 + + if field_x > 0 then + right = right + field_x + left = right - field_x + elseif field_x < 0 then + left = left + field_x + right = left - field_x + end + + if field_y < 0 then + top = top + field_y + bottom = top-field_y + elseif field_y > 0 then + bottom = bottom + field_y + top = bottom-field_y + end + + bounding_box_top_left = {x=left, y=top} + bounding_box_bottom_right = {x=right, y=bottom} + + if debug then + create_beam_bounding_box(player, surface, direction, bounding_box_top_left, bounding_box_bottom_right) + entities = surface.find_entities_filtered{name="laser-beam", invert=true, area={bounding_box_top_left, bounding_box_bottom_right}} + else + entities = surface.find_entities({bounding_box_top_left, bounding_box_bottom_right}) + end + + mt = {} -- create the matrix + for i,entity in pairs(entities) do + + local x_pos = left-entity.position.x + local y_pos = top-entity.position.y + local name = entity.name + + mt[math.floor(x_pos*(top-bottom) + y_pos)] = name:gsub("-", "_") + set_points_of_interest(player, name, 1, x_pos, y_pos) + + end + + rcon.print(1) + return mt +end + +function observe_statistics (player) + performance = { + item = { + input_counts=player.force.item_production_statistics.input_counts, + output_counts=player.force.item_production_statistics.output_counts + }, + fluid = { + input_counts=player.force.fluid_production_statistics.input_counts, + output_counts=player.force.fluid_production_statistics.output_counts + }, + kills = { + input_counts=player.force.kill_count_statistics.input_counts, + output_counts=player.force.kill_count_statistics.output_counts + }, + built = { + input_counts=player.force.entity_build_count_statistics.input_counts, + output_counts=player.force.entity_build_count_statistics.output_counts + } + } + return performance +end + +function clean_nils(t) + local ans = {} + for _,v in pairs(t) do + ans[ #ans+1 ] = v + end + return ans +end + +function observe_chunk(player, surface, chunk_x, chunk_y) + counts = {} + key = chunk_x .. ", " ..chunk_y + + chunk_area = {{chunk_x*32, chunk_y*32}, {(chunk_x+1)*32, (chunk_y+1)*32}} + + counts['all'] = surface.count_entities_filtered{area=chunk_area} + + -- Environment + counts['trees'] = surface.count_entities_filtered{area=chunk_area, type = "tree"} + counts['water'] = surface.count_tiles_filtered{area=chunk_area, name = 'water'} + counts['pollution'] = surface.get_pollution({chunk_x*32,chunk_y*32}) + + -- Forces + counts['factory'] = surface.count_entities_filtered{area=chunk_area, force = "player"} + counts['enemy'] = surface.count_entities_filtered{area=chunk_area, force = "enemy"} + + -- Resources + counts['iron-ore'] = surface.count_entities_filtered{area=chunk_area, name = "iron-ore"} + counts['coal'] = surface.count_entities_filtered{area=chunk_area, name = "coal"} + counts['copper-ore'] = surface.count_entities_filtered{area=chunk_area, name = "copper-ore"} + counts['uranium-ore'] = surface.count_entities_filtered{area=chunk_area, name = "uranium-ore"} + counts['stone'] = surface.count_entities_filtered{area=chunk_area, name = "stone"} + counts['crude-oil'] = surface.count_entities_filtered{area=chunk_area, name = "crude-oil"} + + for field_name, count in pairs(counts) do + --if count ~= 0 then + set_points_of_interest(player, field_name, count, chunk_x, chunk_y) + --else + --table.remove(counts, field_name) + --end + end + + rcon.print(1) + return counts +end + +function observe_buildable (player, inventory) + local recipes = {} + for field_name, recipe in pairs(player.force.recipes) do + local name = field_name:gsub("-", "_") + local can_build = 0 + for i, ingredient in pairs(recipe.ingredients) do + if inventory[ingredient.name] ~= nil then + local max_build = inventory[ingredient.name] / ingredient.amount + can_build = math.floor(max_build) + else + can_build = 0 + break + end + end + if can_build ~= 0 then + recipes[name] = can_build + end + end + return serpent.line(recipes) +end + +function dump(o) + if type(o) == 'table' then + local s = '{ ' + for k,v in pairs(o) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. dump(v) .. ',' + end + return s .. '} ' + else + return tostring(o) + end +end + +function set_points_of_interest (player, field, count, chunk_x, chunk_y) + if count > 0 and global.interesting_entities[field] then + x_distance = (chunk_x*32 - player.position.x) * (chunk_x*32 - player.position.x) + y_distance = (chunk_y*32 - player.position.y) * (chunk_y*32 - player.position.y) + distance_square = x_distance + y_distance + + if global.distances_to_nearest[field] == nil then + global.distances_to_nearest[field] = 100000 + end + + if global.distances_to_nearest[field] > distance_square then + global.distances_to_nearest[field] = math.sqrt(distance_square) --only compute expensive sqrt when necessary + global.points_of_interest[field] = {x=chunk_x*32, y=chunk_y*32} + end + end +end + +-- Define a function to calculate the raw material value of an item +function raw_material_value(item_name) + local values = { + ["iron-ore"] = 1, + ["copper-ore"] = 1, + ["coal"] = 1, + ["stone"] = 1, + ["crude-oil"] = 1 + } + -- Add more items and their raw material values here if needed + + return values[item_name] or 0 +end + +function get_productivity(player) + local force = player.force + local production_statistics = force.item_production_statistics + + -- Calculate production rates + local production_rates = {} + for _, prototype in pairs(game.item_prototypes) do + local name = prototype.name + local count = production_statistics.get_flow_count{name = name, input = true, precision_index = 5, count = true} + if count > 0 then + production_rates[name] = count + end + end + + -- Calculate consumption rates + local consumption_rates = {} + for _, prototype in pairs(game.item_prototypes) do + local name = prototype.name + local count = production_statistics.get_flow_count{name = name, input = false, precision_index = 5, count = true} + if count > 0 then + consumption_rates[name] = count + end + end + + -- Use the raw_material_value function to calculate the total value created + local total_production_value = 0 + local total_consumption_value = 0 + + for name, rate in pairs(production_rates) do + local value = raw_material_value(name) * rate + total_production_value = total_production_value + value + end + + for name, rate in pairs(consumption_rates) do + local value = raw_material_value(name) * rate + total_consumption_value = total_consumption_value + value + end + + local net_value_created = total_production_value - total_consumption_value + + + return { + production=production_rates, + consumption=consumption_rates + } +end + +function craft_entity(player, entity_name, count) + -- Ensure the player and entity_name are valid + if not player or not player.valid or not entity_name then + return "Entity not valid" + end + + -- Get the recipe for the entity + local recipe = player.force.recipes[entity_name] + if not recipe then + return "Recipe doesnt exist" + end + + -- Check if the entity can be crafted by hand + if recipe.category ~= "crafting" then + return "It requires " .. entity_name:gsub("-", "_") .. " which cannot be crafted by hand" + end + + + -- Check if the player has enough items to craft the entity + for _, ingredient in pairs(recipe.ingredients) do + local has_count = player.get_item_count(ingredient.name) + local need_count = ingredient.amount * count + if has_count < need_count then + return "Insufficient "..ingredient.name:gsub('-',"_"):gsub(' ',"_").." "..(need_count-has_count).." needed" + end + end + + -- Craft the entity, consuming the ingredients + for _, ingredient in pairs(recipe.ingredients) do + player.remove_item({name = ingredient.name, count = ingredient.amount * count}) + end + + -- Insert the crafted entity into the player's inventory + player.insert({name = entity_name, count = count}) + + return 1 +end + +function get_missing_ingredients(player, recipe, count, checked_recipes) + local missing_ingredients = {} + checked_recipes = checked_recipes or {} + for _, ingredient in pairs(recipe.ingredients) do + if game.item_prototypes[ingredient.name] then + local count_that_player_has = player.get_item_count(ingredient.name) + local needed = ingredient.amount * count + if count_that_player_has < needed then + local difference = needed - count_that_player_has + + + -- Check if the ingredient can be crafted + local ingredient_recipe = player.force.recipes[ingredient.name] + if ingredient_recipe and not checked_recipes[ingredient.name] then + checked_recipes[ingredient.name] = true + local sub_missing_ingredients = get_missing_ingredients(player, ingredient_recipe, difference, checked_recipes) + for sub_ingredient_name, sub_count in pairs(sub_missing_ingredients) do + if missing_ingredients[sub_ingredient_name] then + missing_ingredients[sub_ingredient_name] = missing_ingredients[sub_ingredient_name] + sub_count + else + missing_ingredients[sub_ingredient_name] = sub_count + end + end + if sub_missing_ingredients ~= nil then + missing_ingredients[ingredient.name] = difference + end + end + end + else + if game.fluid_prototypes[ingredient.name] then + abort("Crafting requires fluid ingredient " .. ingredient.name) + else + abort("Unknown ingredient " .. ingredient.name) + end + end + end + return missing_ingredients +end + +function recursively_craft_missing_ingredients(player, missing_ingredients) + local uncraftable_ingredients = {} + for ingredient_name, count in pairs(missing_ingredients) do + local success = craft_entity(player, ingredient_name, count) + if not success then + local recipe = player.force.recipes[ingredient_name] + if recipe then + local sub_missing_ingredients = get_missing_ingredients(player, recipe, count) + recursively_craft_missing_ingredients(player, sub_missing_ingredients) + else + uncraftable_ingredients[ingredient_name] = count + end + end + end + return uncraftable_ingredients +end + +function add_to_crafting_queue(player, entity_name, count) + -- Ensure the player and entity_name are valid + if not player or not player.valid or not entity_name then + return "not_valid_entity" + end + + -- Get the recipe for the entity + local recipe = player.force.recipes[entity_name] + if not recipe then + return "no_valid_recipe" + end + + -- Check if the player has enough items to craft the entity + local missing_ingredients = get_missing_ingredients(player, recipe, count) + if next(missing_ingredients) == nil then + -- Craft the requested entity + return craft_entity(player, entity_name, count) + else + local uncraftable_ingredients = recursively_craft_missing_ingredients(player, missing_ingredients) + + if next(uncraftable_ingredients) ~= nil then + local uncraftable_message = "Cannot craft the following missing ingredients " + for ingredient_name, count in pairs(uncraftable_ingredients) do + uncraftable_message = uncraftable_message .. count .. "x " .. ingredient_name .. "___" + end + return uncraftable_message:sub(1, -3) + + else + -- Craft the requested entity + return craft_entity(player, entity_name, count) + end + + end +end + +function inspect(player, radius, position) + local surface = player.surface + local bounding_box = { + left_top = {x = position.x - radius, y = position.y - radius}, + right_bottom = {x = position.x + radius, y = position.y + radius} + } + + local entities = surface.find_entities_filtered({bounding_box, force = "player"}) + local entity_data = {} + + for _, entity in ipairs(entities) do + if entity.name ~= 'character' then + local data = { + name = entity.name:gsub("-", "_"), + position = entity.position, + direction = entity.direction,--directions[entity.direction+1], + health = entity.health, + force = entity.force.name, + energy = entity.energy, + status = entity.status, + --crafted_items = entity.crafted_items or nil + } + + -- Get entity contents if it has an inventory + if entity.get_inventory(defines.inventory.chest) then + local inventory = entity.get_inventory(defines.inventory.chest).get_contents() + data.contents = inventory + end + + data.warnings = get_issues(entity) + + -- Get entity orientation if it has an orientation attribute + if entity.type == "train-stop" or entity.type == "car" or entity.type == "locomotive" then + data.orientation = entity.orientation + end + + -- Get connected entities for pipes and transport belts + if entity.type == "pipe" or entity.type == "transport-belt" then + local path_ends = find_path_ends(entity) + data.path_ends = {} + for _, path_end in pairs(path_ends) do + local path_position = {x=path_end.position.x - player.position.x, y=path_end.position.y - player.position.y} + table.insert(data.path_ends, {name = path_end.name:gsub("-", "_"), position = path_position, unit_number = path_end.unit_number}) + end + end + + table.insert(entity_data, data) + else + local data = { + name = "player_character", + position = entity.position, + direction = directions[entity.direction+1], + } + table.insert(entity_data, data) + end + end + + -- Sort entities with path_ends by the length of path_ends in descending order + table.sort(entity_data, function(a, b) + if a.path_ends and b.path_ends then + return #a.path_ends > #b.path_ends + elseif a.path_ends then + return true + else + return false + end + end) + + -- Remove entities that exist in the path_ends of other entities + local visited_paths = {} + local filtered_entity_data = {} + for _, data in ipairs(entity_data) do + if data.path_ends then + local should_add = true + for _, path_end in ipairs(data.path_ends) do + if visited_paths[path_end.unit_number] then + should_add = false + break + end + end + if should_add then + for _, path_end in ipairs(data.path_ends) do + visited_paths[path_end.unit_number] = true + end + table.insert(filtered_entity_data, data) + else + data.path_ends = nil + --table.insert(filtered_entity_data, data) + end + else + table.insert(filtered_entity_data, data) + end + end + entity_data = filtered_entity_data + + return entity_data +end + +function required_resource_present(entity, position, surface) + local prototype = game.entity_prototypes[entity] + local entity_type = prototype.type + + if entity_type == "mining-drill" then + local resources = surface.find_entities_filtered{position=position, type="resource"} + for _, resource in ipairs(resources) do + if resource.prototype.mineable_properties.minable then + return true, nil + end + end + return false, "minable resource" + + elseif entity_type == "offshore-pump" then + local tile = surface.get_tile(position) + if tile.prototype.name == "water" or tile.prototype.name == "deepwater" then + return true, nil + end + return false, "water" + + else + return true, nil + end +end + +rcon.print(1) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise_inventory.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise_inventory.lua new file mode 100644 index 000000000..e1c280ad9 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise_inventory.lua @@ -0,0 +1,9 @@ +global.actions.initialise_inventory = function(player_index, item_names_and_counts_json) + local player = global.agent_characters[player_index] + local item_names_and_counts = game.json_to_table(item_names_and_counts_json) + + -- Loop through the entity names and insert them into the player's inventory + for item, count in pairs(item_names_and_counts) do + player.get_main_inventory().insert{name=item, count=count} + end +end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/priority_queue.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/priority_queue.lua new file mode 100644 index 000000000..c87ad1d60 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/priority_queue.lua @@ -0,0 +1,61 @@ +local PriorityQueue = {} +PriorityQueue.__index = PriorityQueue + +-- Create a new PriorityQueue. +function PriorityQueue.new(comparator) + return setmetatable({queue = {}, comparator = comparator or function(a, b) return a < b end}, PriorityQueue) +end + +-- Insert a new element into the queue. +function PriorityQueue:insert(element) + -- Insert the element at the end of the queue. + table.insert(self.queue, element) + + -- Bubble up the element to its correct place. + local i = #self.queue + while i > 1 do + local parent = math.floor(i / 2) + if not self.comparator(self.queue[i], self.queue[parent]) then break end + self.queue[i], self.queue[parent] = self.queue[parent], self.queue[i] + i = parent + end +end + +-- Remove and return the top element of the queue. +function PriorityQueue:pop() + if #self.queue == 0 then error("Queue is empty") end + + local top = self.queue[1] + + -- Replace the root with the last element in the queue. + self.queue[1] = self.queue[#self.queue] + table.remove(self.queue) + + -- Bubble down the root element to its correct place. + local i = 1 + while true do + local left = 2 * i + local right = left + 1 + local smallest = i + + if left <= #self.queue and self.comparator(self.queue[left], self.queue[smallest]) then + smallest = left + end + if right <= #self.queue and self.comparator(self.queue[right], self.queue[smallest]) then + smallest = right + end + if smallest == i then break end + + self.queue[i], self.queue[smallest] = self.queue[smallest], self.queue[i] + i = smallest + end + + return top +end + +-- Check if the queue is empty. +function PriorityQueue:is_empty() + return #self.queue == 0 +end + +return PriorityQueue diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/production_score.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/production_score.lua new file mode 100644 index 000000000..0c9decc70 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/production_score.lua @@ -0,0 +1,339 @@ +--local player = game.game.get_player(arg1) + +local function get_total_production_counts(production_statistics) + local produced = production_statistics.input_counts + local consumed = production_statistics.output_counts + for name, value in pairs (consumed) do + if produced[name] then + produced[name] = produced[name] - value + else + produced[name] = -value + end + end + return produced +end + +local function get_raw_resources() + local raw_resources = {} + local entities = game.entity_prototypes + for name, entity_prototype in pairs (entities) do + if entity_prototype.resource_category then + if entity_prototype.mineable_properties and entity_prototype.mineable_properties.products then + for k, product in pairs (entity_prototype.mineable_properties.products) do + raw_resources[product.name] = true + end + end + end + if entity_prototype.fluid then + raw_resources[entity_prototype.fluid.name] = true + end + end + return raw_resources +end + +local function get_product_list() + local product_list = {} + local recipes = game.recipe_prototypes + + for recipe_name, recipe_prototype in pairs (recipes) do + if recipe_prototype.allow_decomposition then + local ingredients = recipe_prototype.ingredients + local products = recipe_prototype.products + for k, product in pairs (products) do + if not product_list[product.name] then + product_list[product.name] = {} + end + local recipe_ingredients = {} + local product_amount = util.product_amount(product) + if product_amount > 0 then + for j, ingredient in pairs (ingredients) do + recipe_ingredients[ingredient.name] = ((ingredient.amount)/#products) / product_amount + end + recipe_ingredients.energy = (recipe_prototype.energy / #products) / product_amount + table.insert(product_list[product.name], recipe_ingredients) + end + end + end + end + + local items = game.item_prototypes + local entities = game.entity_prototypes + --[[Now we do some tricky stuff for space science type items]] + local rocket_silos = {} + for k, entity in pairs (entities) do + if entity.type == "rocket-silo" and entity.fixed_recipe then + local recipe = recipes[entity.fixed_recipe] + if not recipe then return end + local required_parts = entity.rocket_parts_required + local list = {} + for k, product in pairs (recipe.products) do + local product_amount = util.product_amount(product) + if product_amount > 0 then + product_amount = product_amount * required_parts + list[product.name] = product_amount + end + end + list["energy"] = recipe.energy + table.insert(rocket_silos, list) + end + end + for k, item in pairs (items) do + local launch_products = item.rocket_launch_products + if launch_products then + for k, launch_product in pairs (launch_products) do + product_list[launch_product.name] = product_list[launch_product.name] or {} + local launch_product_amount = util.product_amount(launch_product) + if launch_product_amount > 0 then + for k, silo_products in pairs (rocket_silos) do + local this_silo = {} + for product_name, product_count in pairs (silo_products) do + this_silo[product_name] = product_count / launch_product_amount + end + this_silo[item.name] = 1 / launch_product_amount + table.insert(product_list[launch_product.name], this_silo) + end + end + end + end + end + return product_list +end + +local default_seed_prices = function() + return + { + ["iron-ore"] = 3.1, + ["copper-ore"] = 3.6, + ["coal"] = 3, + ["stone"] = 2.4, + ["crude-oil"] = 0.2, + ["water"] = 1/1000, + ["steam"] = 1/1000, + ["wood"] = 3.2, + ["raw-fish"] = 100, + ["energy"] = 1, + ["uranium-ore"] = 8.2 + } +end + +local default_ingredient_exponent = function() return 2 end --1.025 end +local default_raw_resource_price = function() return 2.5 end +local default_resource_ignore = function() return {} end + +local default_param = function() + return + { + ingredient_exponent = 1.025, --[[The exponent for increase in value for each additional ingredient formula exponent^#ingredients-2]] + raw_resource_price = 2.5, --[[If a raw resource isn't given a price, it uses this price]] + resource_ignore = {} --[[This is used to account for mods removing resource generation, in which case we want the item price to be calculated from recipes.]] + } +end + +local ingredient_multiplier = function(recipe, param) + return (param.ingredient_exponent or 1) ^ (table_size(recipe) - 2) +end + +local deduce_nil_prices = function(price_list, param) + local nil_prices = {} + for name, item in pairs (game.item_prototypes) do + if not price_list[name] then + nil_prices[name] = {} + end + end + for name, item in pairs (game.fluid_prototypes) do + if not price_list[name] then + nil_prices[name] = {} + end + end + local recipes = game.recipe_prototypes + for name, recipe in pairs (recipes) do + for k, ingredient in pairs (recipe.ingredients) do + if nil_prices[ingredient.name] then + table.insert(nil_prices[ingredient.name], recipe) + end + end + end + for name, recipes in pairs (nil_prices) do + if #recipes > 0 then + local recipe_cost + local ingredient_amount + for k, recipe in pairs (recipes) do + local ingredient_value = 0 + for k, ingredient in pairs (recipe.ingredients) do + if ingredient.name == name then + ingredient_amount = ingredient.amount + else + local ingredient_price = price_list[ingredient.name] + if ingredient_price then + ingredient_value = ingredient_value + (ingredient_price * ingredient.amount) + else + ingredient_value = nil + break + end + end + end + if not ingredient_value then break end + local product_value = 0 + for k, product in pairs (recipe.products) do + local amount = util.product_amount(product) + local product_price = price_list[product.name] + if product_price then + product_value = product_value + product_price * amount + else + product_value = nil + break + end + end + if not product_value then + break + end + local reverse_price = (product_value - param.energy_addition(recipe, product_value)) / ingredient_multiplier(recipe.ingredients, param) -- Not perfect, but close enough + local this_cost = (reverse_price - ingredient_value) / ingredient_amount + if recipe_cost then + recipe_cost = math.min(recipe_cost, this_cost) + else + recipe_cost = this_cost + end + end + if recipe_cost then + price_list[name] = param.round(recipe_cost) + end + end + end +end + +local ln = math.log +local default_energy_addition = function(recipe, cost) + return ((ln(recipe.energy + 1) * (cost ^ 0.5))) +end + +local default_rounding = function(number) + return number +end + +production_score = {} + +global.actions.generate_price_list = function(param) + + local param = param or {} + local price_list = param.seed_prices or default_seed_prices() + + param.ingredient_exponent = param.ingredient_exponent or default_ingredient_exponent() + param.raw_resource_price = param.raw_resource_price or default_raw_resource_price() + param.resource_ignore = param.resource_ignore or default_resource_ignore() + param.round = param.round or default_rounding + param.energy_addition = param.energy_addition or default_energy_addition + param.normalise = param.normalise or function(number) return number end + + local resource_list = get_raw_resources() + + for name, k in pairs (resource_list) do + if not price_list[name] then + price_list[name] = param.round(param.raw_resource_price) + end + end + + for k, name in pairs (param.resource_ignore or {}) do + price_list[name] = nil + end + + local product_list = get_product_list() + local get_price_recursive + get_price_recursive = function(name, current_loop, loop_force) + local price = price_list[name] + if price then return price end + price = 0 + if current_loop[name] then + if loop_force then + return param.raw_resource_price + end + return + end + current_loop[name] = true + local entry = product_list[name] + if not entry then return end + local recipe_cost + for k, recipe in pairs (entry) do + local this_recipe_cost = 0 + for ingredient_name, cost in pairs (recipe) do + if ingredient_name ~= "energy" then + local addition = get_price_recursive(ingredient_name, current_loop, loop_force) + if addition and addition > 0 then + this_recipe_cost = this_recipe_cost + (addition * cost) + else + this_recipe_cost = 0 + break + end + end + end + if this_recipe_cost > 0 then + this_recipe_cost = (this_recipe_cost * ingredient_multiplier(recipe, param)) + param.energy_addition(recipe, this_recipe_cost) + if recipe_cost then + recipe_cost = math.min(recipe_cost, this_recipe_cost) + else + recipe_cost = this_recipe_cost + end + end + end + if recipe_cost then + price = param.round(recipe_cost) + price_list[name] = price + return price + end + end + local items = game.item_prototypes + for name, item in pairs (items) do + local current_loop = {} + get_price_recursive(name, current_loop) + end + local fluids = game.fluid_prototypes + for name, fluid in pairs (fluids) do + local current_loop = {} + get_price_recursive(name, current_loop) + end + deduce_nil_prices(price_list, param) + for name, item in pairs (items) do + local current_loop = {} + get_price_recursive(name, current_loop, true) + end + for name, fluid in pairs (fluids) do + local current_loop = {} + get_price_recursive(name, current_loop, true) + end + deduce_nil_prices(price_list, param) + + for k, price in pairs (price_list) do + price_list[k] = param.normalise(price) + end + + return price_list +end + +production_score.get_production_scores = function(price_list) + local price_list = price_list or global.actions.generate_price_list() + local scores = {} + for k, force in pairs (game.forces) do + local score = 0 + for name, value in pairs (get_total_production_counts(force.item_production_statistics)) do + local price = price_list[name] + if price then + score = score + (price * value) + end + end + for name, value in pairs (get_total_production_counts(force.fluid_production_statistics)) do + local price = price_list[name] + if price then + score = score + (price * value) + end + end + scores[force.name] = math.floor(score) + end + return scores +end + +local scores = production_score.get_production_scores() +if scores then + global.initial_score = scores +else + global.initial_score = {["player"] = 0} +end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/recipe_fluid_connection_mappings.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/recipe_fluid_connection_mappings.lua new file mode 100644 index 000000000..c94bfd388 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/recipe_fluid_connection_mappings.lua @@ -0,0 +1,174 @@ +-- Helper function to get fluid type from ingredient/product +local function get_fluid_type(item) + if item.type == "fluid" then + return item.name + end + return nil +end + +-- Helper function to get position offsets based on fluid type and recipe +local function get_refinery_position_offsets(fluid_type, recipe, is_input) + -- Basic oil processing + if #recipe.ingredients == 1 then + if not is_input then + return 2, -3 -- Top right + else + return 1, 3 -- Bottom right + end + end + + -- game.print(serpent.block(recipe.ingredients)) + -- Advanced oil processing + if #recipe.ingredients == 2 and recipe.ingredients[2].name == "crude-oil" then + if is_input then + if fluid_type == "crude-oil" then + return 1, 3 -- Top left + else + return -1, 3 -- Top right (water) + end + else + if fluid_type == "heavy-oil" then + return -2, -3 -- Bottom left + elseif fluid_type == "light-oil" then + return 0, -3 -- Bottom middle + else + return 2, -3 -- Bottom right (heavy oil) + end + end + end + + -- Coal liquefaction + if #recipe.ingredients == 3 and recipe.ingredients[3].name == "steam" then + if is_input then + if fluid_type == "steam" then + return 1, 3 -- Top left + else + return -1, 3 -- Top right (heavy oil) + end + else + if fluid_type == "petroleum-gas" then + return -2, -3 -- Bottom left + elseif fluid_type == "light-oil" then + return 0, -3 -- Bottom middle + else + return 2, -3 -- Bottom right (heavy oil) + end + end + end + return 0,0 +end + +-- Helper function to get chemical plant position offsets +local function get_chemical_plant_position_offsets(index, total, is_input) + if is_input then + if total == 1 then + return -1, -1.5 -- Bottom left for single input + else + -- For two inputs, space them out + return -1 + (index - 1) * 2, -1.5 + end + else + -- Outputs always go on the left side + return -1, 1.5 + end +end + +-- Helper function to rotate coordinates based on entity direction +local function rotate_coordinates(x_offset, y_offset, direction) + if direction == defines.direction.north then + return x_offset, y_offset + elseif direction == defines.direction.south then + return -x_offset, -y_offset + elseif direction == defines.direction.east then + return -y_offset, x_offset + elseif direction == defines.direction.west then + return y_offset, -x_offset + end +end + +global.utils.get_refinery_fluid_mappings = function(entity, recipe) + if not entity or not recipe then return nil end + if not entity.position then return nil end + + local x, y = entity.position.x, entity.position.y + local input_points = {} + local output_points = {} + + -- Map inputs + for _, ingredient in pairs(recipe.ingredients) do + if ingredient.type == "fluid" then + local x_offset, y_offset = get_refinery_position_offsets(ingredient.name, recipe, true) + local rotated_x, rotated_y = rotate_coordinates(x_offset, y_offset, entity.direction) + table.insert(input_points, { + x = x + rotated_x, + y = y + rotated_y, + type = "\""..ingredient.name.."\"" + }) + end + end + + -- Map outputs + for _, product in pairs(recipe.products) do + if product.type == "fluid" then + local x_offset, y_offset = get_refinery_position_offsets(product.name, recipe, false) + local rotated_x, rotated_y = rotate_coordinates(x_offset, y_offset, entity.direction) + table.insert(output_points, { + x = x + rotated_x, + y = y + rotated_y, + type = "\""..product.name.."\"" + }) + end + end + + return { + inputs = input_points, + outputs = output_points + } +end + +-- Helper function to map connection points to fluid types for chemical plant +global.utils.get_chemical_plant_fluid_mappings = function(entity, recipe) + if not entity or not recipe then return nil end + if not entity.position then return nil end + + local x, y = entity.position.x, entity.position.y + local input_points = {} + local output_points = {} + + -- Count fluid inputs + local fluid_inputs = {} + for _, ingredient in pairs(recipe.ingredients) do + if ingredient.type == "fluid" then + table.insert(fluid_inputs, ingredient) + end + end + + -- Map inputs + for i, ingredient in ipairs(fluid_inputs) do + local x_offset, y_offset = get_chemical_plant_position_offsets(i, #fluid_inputs, true) + local rotated_x, rotated_y = rotate_coordinates(x_offset, y_offset, entity.direction) + table.insert(input_points, { + x = x + rotated_x, + y = y + rotated_y, + type = "\""..ingredient.name.."\"" + }) + end + + -- Map outputs + for _, product in pairs(recipe.products) do + if product.type == "fluid" then + local x_offset, y_offset = get_chemical_plant_position_offsets(1, 1, false) + local rotated_x, rotated_y = rotate_coordinates(x_offset, y_offset, entity.direction) + table.insert(output_points, { + x = x + rotated_x, + y = y + rotated_y, + type = "\""..product.name.."\"" + }) + end + end + + return { + inputs = input_points, + outputs = output_points + } +end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset.lua new file mode 100644 index 000000000..9667a0d21 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset.lua @@ -0,0 +1 @@ +game.reset_game_state() \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset_position.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset_position.lua new file mode 100644 index 000000000..9b91c04a2 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset_position.lua @@ -0,0 +1,2 @@ +global.agent_characters[arg1].teleport({arg2, arg3}) +rcon.print(1) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/serialize.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/serialize.lua new file mode 100644 index 000000000..3934682da --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/serialize.lua @@ -0,0 +1,1873 @@ +-- Library for serializing items in Factorio +-- Based on code from playerManager and trainTeleports + +if not global.utils then + global.utils = {} +end + +local function version_to_table(version) + local t = {} + for p in string.gmatch(version, "%d+") do + t[#t + 1] = tonumber(p) + end + return t +end + +-- 0.17 compatibility +local supports_bar, get_bar, set_bar, version +if (pcall(function() local mods = script.active_mods end)) then + supports_bar = "supports_bar" + get_bar = "get_bar" + set_bar = "set_bar" + version = version_to_table(script.active_mods.base) +else + supports_bar = "hasbar" + get_bar = "getbar" + set_bar = "setbar" + version = version_to_table("0.17.69") +end + +-- returns true if the game version is greater than or equal to the given version +local function version_ge(comp) + comp = version_to_table(comp) + for i=1, 3 do + if comp[i] > version[i] then + return false + elseif comp[i] < version[i] then + return true + end + end + return true +end + +local has_create_grid = version_ge("1.1.7") + +-- Add this helper function at the top level +local function is_fluid_handler(entity_type) + return entity_type == "boiler" or + entity_type == "offshore-pump" or + entity_type == "pump" or + entity_type == "generator" or + entity_type == "oil-refinery" or + entity_type == "chemical-plant" + --entity_type == "pipe" or + --entity_type == "pipe-to-ground" +end + + +-- Equipment Grids are serialized into an array of equipment entries +-- where ench entry is a table with the following fields: +-- n: name +-- p: position (array of 2 numbers corresponding to x and y) +-- s: shield (optional) +-- e: energy (optional) +-- If the equipment is a burner the following is also present: +-- i: burner inventory +-- r: result inventory +-- b: curently burning (optional) +-- f: remaining_burning_fuel (optional) +global.utils.serialize_equipment_grid = function(grid) + local serialized = {} + local processed = {} + for y = 0, grid.height - 1 do + for x = 0, grid.width - 1 do + local equipment = grid.get({x, y}) + if equipment ~= nil then + local pos = equipment.position + local combined_pos = pos.x + pos.y * grid.width + 1 + if not processed[combined_pos] then + processed[combined_pos] = true + local entry = { + n = equipment.name, + p = {pos.x, pos.y}, + } + if equipment.shield > 0 then entry.s = equipment.shield end + if equipment.energy > 0 then entry.e = equipment.energy end + -- TODO: Test with Industrial Revolution + if equipment.burner then + local burner = equipment.burner + entry.i = global.utils.serialize_inventory(burner.inventory) + entry.r = global.utils.serialize_inventory(burner.burnt_result_inventory) + if burner.curently_burning then + entry.b = {} + global.utils.serialize_item_stack(burner.curently_burning, entry.b) + entry.f = burner.remaining_burning_fuel + end + end + table.insert(serialized, entry) + end + end + end + end + return serialized +end + +global.utils.deserialize_equipment_grid = function(grid, serialized) + grid.clear() + for _, entry in ipairs(serialized) do + local equipment = grid.put({ + name = entry.n, + position = entry.p, + }) + if equipment then + if entry.s then equipment.shield = entry.s end + if entry.e then equipment.energy = entry.e end + if entry.i then + if entry.b then global.utils.deserialize_item_stack(burner.currently_burning, entry.b) end + if entry.f then burner.remaining_burning_fuel = entry.f end + global.utils.deserialize_inventory(burner.burnt_result_inventory, entry.r) + global.utils.deserialize_inventory(burner.inventory, entry.i) + end + end + end +end + +-- Item stacks are serialized into a table with the following fields: +-- n: name +-- c: count +-- h: health (optional) +-- d: durability (optional) +-- a: ammo count (optional) +-- l: label (optional) +-- g: equipment grid (optional) +-- i: item inventory (optional) +-- If the item stack is exportable it has the following property instead +-- e: export string +-- Label is a table with the following fields: +-- t: label text (optional) +-- c: color (optional) +-- a: allow manual label change +global.utils.serialize_item_stack = function(slot, entry) + if + slot.is_blueprint + or slot.is_blueprint_book + or slot.is_upgrade_item + or slot.is_deconstruction_item + or slot.is_item_with_tags + then + local call_success, call_return = pcall(slot.export_stack) + if not call_success then + print("Error: '" .. call_return .. "' thrown exporting '" .. slot.name .. "'") + else + entry.e = call_return + end + + return + end + + entry.n = slot.name + entry.c = slot.count + if slot.health < 1 then entry.h = slot.health end + if slot.durability then entry.d = slot.durability end + if slot.type == "ammo" then entry.a = slot.ammo end + if slot.is_item_with_label then + local label = {} + if slot.label then label.t = slot.label end + if slot.label_color then label.c = slot.label_color end + label.a = slot.allow_manual_label_change + entry.l = label + end + + if slot.grid then + entry.g = global.utils.serialize_equipment_grid(slot.grid) + end + + if slot.is_item_with_inventory then + local sub_inventory = slot.get_inventory(defines.inventory.item_main) + entry.i = global.utils.serialize_inventory(sub_inventory) + end +end + +global.utils.deserialize_item_stack = function(slot, entry) + if entry.e then + local success = slot.import_stack(entry.e) + if success == 1 then + print("Error: import of '" .. entry.e .. "' succeeded with errors") + elseif success == -1 then + print("Error: import of '" .. entry.e .. "' failed") + end + + return + end + + local item_stack = { + name = entry.n, + count = entry.c, + } + if entry.h then item_stack.health = entry.h end + if entry.d then item_stack.durability = entry.d end + if entry.a then item_stack.ammo = entry.a end + + local call_success, call_return = pcall(slot.set_stack, item_stack) + if not call_success then + print("Error: '" .. call_return .. "' thrown setting stack ".. serpent.line(entry)) + + elseif not call_return then + print("Error: Failed to set stack " .. serpent.line(entry)) + + else + if entry.l then + -- TODO test this with AAI's unit-remote-control + local label = entry.l + if label.t then slot.label = label.t end + if label.c then slot.label_color = label.c end + slot.allow_manual_label_change = label.a + end + if entry.g then + if slot.grid then + global.utils.deserialize_equipment_grid(slot.grid, entry.g) + elseif slot.type == "item-with-entity-data" and has_create_grid then + slot.create_grid() + global.utils.deserialize_equipment_grid(slot.grid, entry.g) + else + print("Error: Attempt to deserialize equipment grid on an unsupported entity") + end + end + if entry.i then + local sub_inventory = slot.get_inventory(defines.inventory.item_main) + global.utils.deserialize_inventory(sub_inventory, entry.i) + end + end +end + +----- DEPRECATED +--global.utils.serialize_inventory = function(inventory) +-- local serialized = {} +-- for i = 1, #inventory do +-- local slot = inventory[i] +-- if slot.valid_for_read then +-- local item_name = "\"" .. slot.name .. "\"" +-- if serialized[item_name] then +-- serialized[item_name] = serialized[item_name] + slot.count +-- else +-- serialized[item_name] = slot.count +-- end +-- end +-- end +-- return serialized +--end + +----- DEPRECATED +--global.utils.serialize_inventory_old = function(inventory) +-- local serialized = {} +-- if inventory[supports_bar]() and inventory[get_bar]() <= #inventory then +-- serialized.b = inventory[get_bar]() +-- end +-- +-- serialized.i = {} +-- local previous_index = 0 +-- local previous_serialized = nil +-- for i = 1, #inventory do +-- local item = {} +-- local slot = inventory[i] +-- if inventory.supports_filters() then +-- item.f = inventory.get_filter(i) +-- end +-- +-- if slot.valid_for_read then +-- global.utils.serialize_item_stack(slot, item) +-- end +-- +-- if item.n or item.f or item.e then +-- local item_serialized = game.table_to_json(item) +-- if item_serialized == previous_serialized then +-- local previous_item = serialized.i[#serialized.i] +-- previous_item.r = (previous_item.r or 0) + 1 +-- previous_index = i +-- +-- else +-- if i ~= previous_index + 1 then +-- item.s = i +-- end +-- +-- previous_index = i +-- previous_serialized = item_serialized +-- table.insert(serialized.i, item) +-- end +-- +-- else +-- -- Either an empty slot or serilization failed +-- previous_index = 0 +-- previous_serialized = nil +-- end +-- end +-- +-- return serialized +--end + +global.utils.deserialize_inventory = function(inventory, serialized) + if serialized.b and inventory[supports_bar]() then + inventory[set_bar](serialized.b) + end + + local last_slot_index = 0 + for _, entry in ipairs(serialized.i) do + local base_index = entry.s or last_slot_index + 1 + + local repeat_count = entry.r or 0 + for offset = 0, repeat_count do + -- XXX what if the inventory is smaller on this instance? + local index = base_index + offset + local slot = inventory[index] + if entry.f then + local call_success, call_return = pcall(inventory.set_filter, index, entry.f) + if not call_success then + print("Error: '" .. call_return .. "' thrown setting filter " .. entry.f) + + elseif not call_return then + print("Error: Failed to set filter " .. entry.f) + end + end + + if entry.n or entry.e then + global.utils.deserialize_item_stack(slot, entry) + end + end + last_slot_index = base_index + repeat_count + end +end + +global.utils.serialize_recipe = function(recipe) + local serialized = { + name = "\"" .. recipe.name .. "\"", + category = "\"" .. recipe.category .. "\"", + enabled = recipe.enabled, + --hidden = recipe.hidden, + energy = recipe.energy, + --order = recipe.order, + --group = recipe.group and "\"" .recipe.group.name.. "\"" or nil, + --subgroup = recipe.subgroup and "\"" .recipe.subgroup.name.. "\"" or nil, + --force = recipe.force and recipe.force.name or nil, + } + + -- Serialize ingredients + serialized.ingredients = {} + for _, ingredient in pairs(recipe.ingredients) do + table.insert(serialized.ingredients, {name = "\""..ingredient.name.."\"", type = "\""..ingredient.type.."\"", amount = ingredient.amount}) + end + + -- Serialize products + serialized.products = {} + + -- First try normal products + local output_list = recipe.products + if output_list then + for _, product in pairs(output_list) do + local product_table = {name = "\""..product.name.."\"", type = "\""..product.type.."\"", amount = product.amount} + if product.probability then product_table.probability = product.probability end + table.insert(serialized.products, product_table) + end + end + + return serialized +end + +global.utils.serialize_fluidbox = function(fluidbox) + local serialized = { + length = #fluidbox, + } + if fluidbox.owner and fluidbox.owner.name then + serialized[fluidbox.owner] = fluidbox.owner.name + end + + -- Serialize each fluid box + serialized.fluidboxes = {} + for i = 1, #fluidbox do + local box = fluidbox[i] + local prototype = fluidbox.get_prototype(i) + local connections = fluidbox.get_connections(i) + local filter = fluidbox.get_filter(i) + local flow = fluidbox.get_flow(i) + local locked_fluid = fluidbox.get_locked_fluid(i) + local fluid_system_id = fluidbox.get_fluid_system_id(i) + + local serialized_box = { + prototype = prototype and prototype.object_name or nil, + capacity = fluidbox.get_capacity(i), + connections = {}, + filter = filter, + flow = flow, + locked_fluid = locked_fluid, + fluid_system_id = fluid_system_id, + } + + -- Serialize fluid + if box then + serialized_box.fluid = { + name = box.name, + amount = box.amount, + temperature = box.temperature, + } + end + + -- Serialize connections + for _, connection in pairs(connections) do + if connection.owner and connection.owner.name then + table.insert(serialized_box.connections, "\""..connection.owner.name .. "\"") + + end + end + + serialized.fluidboxes[i] = serialized_box + end + + return serialized +end + + +-- Helper function to get relative direction of neighbor +local function get_neighbor_direction(entity, neighbor) + local dx = neighbor.position.x - entity.position.x + local dy = neighbor.position.y - entity.position.y + + -- Determine primary direction based on which delta is larger + if math.abs(dx) > math.abs(dy) then + return dx > 0 and defines.direction.east or defines.direction.west + else + return dy > 0 and defines.direction.south or defines.direction.north + end +end + +-- Helper function to expand a bounding box by a given amount +local function expand_box(box, amount) + return { + left_top = { + x = box.left_top.x - amount, + y = box.left_top.y - amount + }, + right_bottom = { + x = box.right_bottom.x + amount, + y = box.right_bottom.y + amount + } + } +end + +local function serialize_neighbours(entity) + local neighbours = {} + + -- Get entity's prototype collision box + local prototype = game.entity_prototypes[entity.name] + local collision_box = prototype.collision_box + + -- Create a slightly larger search box + local search_box = expand_box(collision_box, 0.707) -- Expand by 0.5 tiles + + -- Convert to world coordinates + local world_box = { + left_top = { + x = entity.position.x + search_box.left_top.x, + y = entity.position.y + search_box.left_top.y + }, + right_bottom = { + x = entity.position.x + search_box.right_bottom.x, + y = entity.position.y + search_box.right_bottom.y + } + } + + -- Find entities within the expanded collision box + local nearby = entity.surface.find_entities_filtered{ + area = world_box + } + + -- Process each nearby entity + for _, neighbor in pairs(nearby) do + -- Skip if it's the same entity or if it has no unit number + if neighbor.unit_number and neighbor.unit_number ~= entity.unit_number then + table.insert(neighbours, { + unit_number = neighbor.unit_number, + direction = get_neighbor_direction(entity, neighbor), + name = "\""..neighbor.name.."\"", + position = neighbor.position + --type = neighbor.type + }) + end + end + + return neighbours +end + +function add_burner_inventory(serialized, burner) + local fuel_inventory = burner.inventory + if fuel_inventory and #fuel_inventory > 0 then + serialized.fuel_inventory = {} + serialized.remaining_fuel = 0 + for i = 1, #fuel_inventory do + local item = fuel_inventory[i] + if item and item.valid_for_read then + + table.insert(serialized.fuel_inventory, {name = "\""..item.name.."\"", count = item.count}) + serialized.remaining_fuel = serialized.remaining_fuel + item.count + end + end + end +end + +function get_entity_direction(entity, direction) + + -- If direction is nil then return north + if direction == nil then + return defines.direction.north + end + --game.print("Getting direction: " .. entity .. " with direction: " .. direction) + + + local prototype = game.entity_prototypes[entity] + -- if prototype is nil (e.g because the entity is a ghost or player character) then return the direction as is + if prototype == nil then + return direction + end + --game.print(game.entity_prototypes[entity].name, {skip=defines.print_skip.never}) + + local cardinals = { + defines.direction.north, + defines.direction.east, + defines.direction.south, + defines.direction.west + } + if prototype and (prototype.name == "boiler" or prototype.type == "generator" or prototype.name == "heat-exchanger") then + if direction == 0 then + return defines.direction.north + elseif direction == 1 then + return defines.direction.east + elseif direction == 2 then + return defines.direction.south + else + return defines.direction.west + end + elseif prototype and prototype.name == "offshore-pump" then + if direction == 2 then + return defines.direction.north + elseif direction == 3 then + return defines.direction.east + elseif direction == 0 then + return defines.direction.south + else + return defines.direction.west + end + elseif prototype and prototype.name == "oil-refinery" then + if direction == 0 then + return defines.direction.north + elseif direction == 1 then + return defines.direction.east + elseif direction == 2 then + return defines.direction.south + else + return defines.direction.west + end + elseif prototype and prototype.name == "chemical-plant" then + if direction == 2 then + return defines.direction.north + elseif direction == 3 then + return defines.direction.east + elseif direction == 0 then + return defines.direction.south + else + return defines.direction.west + end + elseif prototype and prototype.type == "transport-belt" or prototype.type == "splitter" then + --game.print("Transport belt direction: " .. direction) + if direction == 0 then + return defines.direction.north + elseif direction == 3 then + return defines.direction.west + elseif direction == 2 then + return defines.direction.south + else + return defines.direction.east + end + elseif prototype and prototype.type == "inserter" then + --return cardinals[(direction % 4)] + if direction == 0 then + return defines.direction.south + elseif direction == 1 then + return defines.direction.west + elseif direction == 2 then + return defines.direction.north + else + return defines.direction.east + end + elseif prototype.type == "mining-drill" then + if direction == 1 then + return cardinals[2] + elseif direction == 2 then + return cardinals[3] + elseif direction == 3 then + return cardinals[4] + else + return cardinals[1] + end + elseif prototype.type == "underground-belt" then + if direction == 1 then + return defines.direction.east + elseif direction == 2 then + return defines.direction.south + elseif direction == 3 then + return defines.direction.west + else + return defines.direction.north + end + elseif prototype.type == "pipe-to-ground" then + if direction == 1 then + return defines.direction.west + elseif direction == 2 then + return defines.direction.north + elseif direction == 3 then + return defines.direction.east + else + return defines.direction.south + end + elseif prototype.type == "assembling-machine" then + if direction == 0 then + return defines.direction.north + elseif direction == 1 then + return defines.direction.east + elseif direction == 2 then + return defines.direction.south + else + return defines.direction.west + end + elseif prototype.type == "storage-tank" then + if direction == 0 then + return defines.direction.north + elseif direction == 1 then + return defines.direction.east -- Only 2 directions + elseif direction == 2 then + return defines.direction.south -- Only 2 directions + else + return defines.direction.west -- Only 2 directions + end + else + return direction + end + return direction +end + + + +function get_inverse_entity_direction(entity, factorio_direction) + local prototype = game.entity_prototypes[entity] + + if not factorio_direction then + return 0 -- Assuming 0 is the default direction in your system + end + + if prototype and prototype.name == "offshore-pump" then + if factorio_direction == defines.direction.west then + return defines.direction.east + elseif factorio_direction == defines.direction.east then + return defines.direction.west + elseif factorio_direction == defines.direction.south then + return defines.direction.north + else + return defines.direction.south + end + end + --game.print("Getting inverse direction: " .. entity .. " with direction: " .. factorio_direction) + if prototype and prototype.type == "inserter" then + if factorio_direction == defines.direction.south then + return defines.direction.north + elseif factorio_direction == defines.direction.west then + return defines.direction.east + elseif factorio_direction == defines.direction.north then + return defines.direction.south + elseif factorio_direction == defines.direction.east then + return defines.direction.west + else + return -1 + end + else + --game.print("Returning direction: " .. math.floor(factorio_direction / 2) .. ', '.. factorio_direction) + -- For other entity types, convert Factorio's direction to 0-3 range + return factorio_direction + end +end + +-- Helper function to check if a position is valid (not colliding with water or other impassable tiles) +global.utils.is_valid_connection_point = function(surface, position) + -- Get the tile at the position + local tile = surface.get_tile(position.x, position.y) + + -- Check if the tile is water or other impassable tiles + local invalid_tiles = { + ["water"] = true, + ["deepwater"] = true, + ["water-green"] = true, + ["deepwater-green"] = true, + ["water-shallow"] = true, + ["water-mud"] = true, + } + + -- Return false if the tile is invalid, true otherwise + return not invalid_tiles[tile.name] +end + +-- Helper function to filter connection points +local function filter_connection_points(entity, points) + if not points then return nil end + + local filtered_points = {} + for _, point in ipairs(points) do + if global.utils.is_valid_connection_point(entity.surface, point) then + table.insert(filtered_points, point) + end + end + + -- If all points were filtered out, return nil + if #filtered_points == 0 then + return nil + end + + return filtered_points +end + +---- Modified pipe position functions to include filtering +--local function get_pipe_positions_filtered(entity) +-- local positions = global.utils.get_generator_connection_positions(entity) +-- return filter_connection_points(entity, positions) +--end +-- +--local function get_pumpjack_pipe_position_filtered(entity) +-- local positions = global.utils.get_pumpjack_connection_points(entity) +-- return filter_connection_points(entity, positions) +--end +-- +--local function get_boiler_pipe_positions_filtered(entity) +-- local positions = global.utils.get_boiler_connection_points(entity) +-- +-- -- Special handling for boiler since it has a different structure +-- if not positions then return nil end +-- +-- local filtered = { +-- water_inputs = filter_connection_points(entity, positions.water_inputs), +-- steam_output = positions.steam_output -- Usually steam output doesn't need filtering as it connects to pipes above ground +-- } +-- +-- -- If all water inputs were filtered out, return nil +-- if not filtered.water_inputs or #filtered.water_inputs == 0 then +-- return nil +-- end +-- +-- return filtered +--end +-- +--local function get_offshore_pump_pipe_position_filtered(entity) +-- local positions = global.utils.get_offshore_pump_connection_points(entity) +-- return filter_connection_points(entity, positions) +--end + + +global.entity_status_names = { + [defines.entity_status.working] = "working", + [defines.entity_status.normal] = "normal", + [defines.entity_status.no_power] = "no_power", + [defines.entity_status.low_power] = "low_power", + [defines.entity_status.no_fuel] = "no_fuel", + [defines.entity_status.disabled_by_control_behavior] = "disabled_by_control_behavior", + [defines.entity_status.opened_by_circuit_network] = "opened_by_circuit_network", + [defines.entity_status.closed_by_circuit_network] = "closed_by_circuit_network", + [defines.entity_status.disabled_by_script] = "disabled_by_script", + [defines.entity_status.marked_for_deconstruction] = "marked_for_deconstruction", + [defines.entity_status.not_plugged_in_electric_network] = "not_plugged_in_electric_network", + [defines.entity_status.networks_connected] = "networks_connected", + [defines.entity_status.networks_disconnected] = "networks_disconnected", + [defines.entity_status.charging] = "charging", + [defines.entity_status.discharging] = "discharging", + [defines.entity_status.fully_charged] = "fully_charged", + [defines.entity_status.out_of_logistic_network] = "out_of_logistic_network", + [defines.entity_status.no_recipe] = "no_recipe", + [defines.entity_status.no_ingredients] = "no_ingredients", + [defines.entity_status.no_input_fluid] = "no_input_fluid", + [defines.entity_status.no_research_in_progress] = "no_research_in_progress", + [defines.entity_status.no_minable_resources] = "no_minable_resources", + [defines.entity_status.low_input_fluid] = "low_input_fluid", + [defines.entity_status.fluid_ingredient_shortage] = "fluid_ingredient_shortage", + [defines.entity_status.full_output] = "full_output", + [defines.entity_status.full_burnt_result_output] = "full_burnt_result_output", + [defines.entity_status.item_ingredient_shortage] = "item_ingredient_shortage", + [defines.entity_status.missing_required_fluid] = "missing_required_fluid", + [defines.entity_status.missing_science_packs] = "missing_science_packs", + [defines.entity_status.waiting_for_source_items] = "waiting_for_source_items", + [defines.entity_status.waiting_for_space_in_destination] = "waiting_for_space_in_destination", + [defines.entity_status.preparing_rocket_for_launch] = "preparing_rocket_for_launch", + [defines.entity_status.waiting_to_launch_rocket] = "waiting_to_launch_rocket", + [defines.entity_status.launching_rocket] = "launching_rocket", + [defines.entity_status.no_modules_to_transmit] = "no_modules_to_transmit", + [defines.entity_status.recharging_after_power_outage] = "recharging_after_power_outage", + [defines.entity_status.waiting_for_target_to_be_built] = "waiting_for_target_to_be_built", + [defines.entity_status.waiting_for_train] = "waiting_for_train", + [defines.entity_status.no_ammo] = "no_ammo", + [defines.entity_status.low_temperature] = "low_temperature", + [defines.entity_status.disabled] = "disabled", + [defines.entity_status.turned_off_during_daytime] = "turned_off_during_daytime", + [defines.entity_status.not_connected_to_rail] = "not_connected_to_rail", + [defines.entity_status.cant_divide_segments] = "cant_divide_segments", +} + +global.utils.get_entity_direction = get_entity_direction + +global.utils.serialize_entity = function(entity) + + if entity == nil then + return {} + end + --game.print("Serializing entity: " .. entity.name .. " with direction: " .. entity.direction) + local direction = entity.direction + + if direction ~= nil then + direction = get_entity_direction(entity.name, entity.direction) + else + direction = 0 + end + + + --game.print("Serialized direction: ", {skip=defines.print_skip.never}) + local serialized = { + name = "\""..entity.name.."\"", + position = entity.position, + direction = direction, + health = entity.health, + energy = entity.energy, + type = "\""..entity.type.."\"", + status = global.entity_status_names[entity.status] or "\"normal\"", + } + + if entity.grid then + serialized.grid = global.utils.serialize_equipment_grid(entity.grid) + end + --game.print(serpent.line(entity.get_inventory(defines.inventory.turret_ammo))) + serialized.warnings = get_issues(entity) + --if entity.get_inventory then + -- for i = 1, #defines.inventory do + -- local inventory = entity.get_inventory(i) + -- if inventory and #inventory > 0 then + -- serialized["inventory_" .. i] = global.utils.serialize_inventory(inventory) + -- end + -- end + --end + + + local inventory_types = { + {name = "fuel", define = defines.inventory.fuel}, + {name = "burnt_result", define = defines.inventory.burnt_result}, + {name = "inventory", define = defines.inventory.chest}, + {name = "furnace_source", define = defines.inventory.furnace_source}, + {name = "furnace_result", define = defines.inventory.furnace_result}, + {name = "furnace_modules", define = defines.inventory.furnace_modules}, + {name = "assembling_machine_input", define = defines.inventory.assembling_machine_input}, + {name = "assembling_machine_output", define = defines.inventory.assembling_machine_output}, + {name = "assembling_machine_modules", define = defines.inventory.assembling_machine_modules}, + {name = "lab_input", define = defines.inventory.lab_input}, + {name = "lab_modules", define = defines.inventory.lab_modules}, + {name = "turret_ammo", define = defines.inventory.turret_ammo} + } + + for _, inv_type in ipairs(inventory_types) do + local inventory = entity.get_inventory(inv_type.define) + if inventory then + serialized[inv_type.name] = inventory.get_contents() + end + end + + -- Add dimensions of the entity + local prototype = game.entity_prototypes[entity.name] + local collision_box = prototype.collision_box + serialized.dimensions = { + width = math.abs(collision_box.right_bottom.x - collision_box.left_top.x), + height = math.abs(collision_box.right_bottom.y - collision_box.left_top.y), + } + --- Add specific check for gun turrets + --if entity.type == "ammo-turret" then + -- local ammo_inventory = entity.get_inventory(defines.inventory.turret_ammo) + -- if ammo_inventory and #ammo_inventory > 0 then + -- serialized.ammo_inventory = global.utils.serialize_inventory(ammo_inventory) + -- end + --end + serialized.neighbours = serialize_neighbours(entity) + + + -- Add input and output locations if the entity is a transport belt + --if entity.type == "transport-belt" or entity.type == "underground-belt" then + -- -- input_position is the position upstream of the belt + -- --local direction = entity.direction + -- + -- local x, y = entity.position.x, entity.position.y + -- if entity.direction == defines.direction.north then + -- y = y + 1 + -- elseif entity.direction == defines.direction.south then + -- y = y - 1 + -- elseif entity.direction == defines.direction.east then + -- x = x - 1 + -- elseif entity.direction == defines.direction.west then + -- x = x + 1 + -- elseif entity.direction == defines.direction.northeast then + -- game.print("NORTHEAST") + -- x = x - 1 + -- y = y + 1 + -- elseif entity.direction == defines.direction.southeast then + -- x = x - 1 + -- y = y - 1 + -- elseif entity.direction == defines.direction.northwest then + -- x = x + 1 + -- y = y + 1 + -- elseif entity.direction == defines.direction.southwest then + -- x = x + 1 + -- y = y - 1 + -- end + -- + -- serialized.input_position = {x = x, y = y} + -- + -- -- output_position is the position downstream of the belt + -- local x, y = entity.position.x, entity.position.y + -- if entity.direction == defines.direction.north then + -- y = y - 1 + -- elseif entity.direction == defines.direction.south then + -- y = y + 1 + -- elseif entity.direction == defines.direction.east then + -- x = x + 1 + -- elseif entity.direction == defines.direction.west then + -- x = x - 1 + -- elseif entity.direction == defines.direction.northeast then + -- x = x + 1 + -- y = y - 1 + -- elseif entity.direction == defines.direction.southeast then + -- x = x + 1 + -- y = y + 1 + -- elseif entity.direction == defines.direction.northwest then + -- x = x - 1 + -- y = y - 1 + -- elseif entity.direction == defines.direction.southwest then + -- x = x - 1 + -- y = y + 1 + -- end + -- --create_beam_point_with_direction(global.agent_characters[1], entity.direction , {x = x, y = y}) + -- serialized.output_position = {x = x, y = y} + -- serialized.position = {x = entity.position.x, y = entity.position.y} + -- --serialized.inventory = entity.get_transport_line(1).get_contents() + -- + -- -- Get contents from both lines + -- local line1 = entity.get_transport_line(1) + -- local line2 = entity.get_transport_line(2) + -- + -- -- Calculate if belt is full at the end (can't insert more items) + -- local is_full = not line1.can_insert_at_back() and not line2.can_insert_at_back() + -- + -- serialized.belt_status = { + -- status = is_full and "full_output" or "normal" + -- } + -- + -- -- Get and merge contents from both lines + -- serialized.inventory = {} + -- local line1_contents = line1.get_contents() + -- local line2_contents = line2.get_contents() + -- + -- serialized.is_terminus = #entity.belt_neighbours["outputs"] == 0 + -- serialized.is_source = #entity.belt_neighbours["inputs"] == 0 + -- + -- for item_name, count in pairs(line1_contents) do + -- serialized.inventory[item_name] = (serialized.inventory[item_name] or 0) + count + -- end + -- for item_name, count in pairs(line2_contents) do + -- serialized.inventory[item_name] = (serialized.inventory[item_name] or 0) + count + -- end + -- + -- -- Add warning if belt is full + -- if not serialized.warnings then + -- serialized.warnings = {} + -- end + -- if is_full then + -- table.insert(serialized.warnings, "Belt output is full") + -- end + -- + -- if entity.type == "underground-belt" then + -- serialized.is_input = entity.belt_to_ground_type == "input" + -- if serialized.is_input then + -- serialized.is_terminus = entity.neighbours == nil + -- else + -- serialized.is_source = entity.neighbours == nil + -- end + -- if entity.neighbours ~= nil then + -- --game.print(serpent.block(entity.neighbours)) + -- serialized.connected_to = entity.neighbours.unit_number + -- end + -- end + --end + -- Add input and output locations if the entity is a transport belt + if entity.type == "transport-belt" or entity.type == "underground-belt" then + local x, y = entity.position.x, entity.position.y + + -- Initialize positions with default offsets based on belt direction + local input_offset = { + [defines.direction.north] = {x = 0, y = 1}, + [defines.direction.south] = {x = 0, y = -1}, + [defines.direction.east] = {x = -1, y = 0}, + [defines.direction.west] = {x = 1, y = 0} + } + + local output_offset = { + [defines.direction.north] = {x = 0, y = -1}, + [defines.direction.south] = {x = 0, y = 1}, + [defines.direction.east] = {x = 1, y = 0}, + [defines.direction.west] = {x = -1, y = 0} + } + + -- Set input position based on upstream connections + local input_pos = {x = x, y = y} + if #entity.belt_neighbours["inputs"] > 0 then + -- Use the position of the first connected input belt + local input_belt = entity.belt_neighbours["inputs"][1] + input_pos = {x = input_belt.position.x, y = input_belt.position.y} + else + -- No input connection, use default offset + local offset = input_offset[entity.direction] + input_pos.x = x + offset.x + input_pos.y = y + offset.y + end + serialized.input_position = input_pos + + -- Set output position based on downstream connections + local output_pos = {x = x, y = y} + if #entity.belt_neighbours["outputs"] > 0 then + -- Use the position of the first connected output belt + local output_belt = entity.belt_neighbours["outputs"][1] + output_pos = {x = output_belt.position.x, y = output_belt.position.y} + else + -- No output connection, use default offset + local offset = output_offset[entity.direction] + output_pos.x = x + offset.x + output_pos.y = y + offset.y + end + serialized.output_position = output_pos + + -- Store the belt's own position + serialized.position = {x = x, y = y} + + -- Handle belt contents and status + local line1 = entity.get_transport_line(1) + local line2 = entity.get_transport_line(2) + + -- Calculate if belt is full at the output + local is_full = not line1.can_insert_at_back() and not line2.can_insert_at_back() + + serialized.belt_status = { + status = is_full and "\"full_output\"" or "\"normal\"" + } + + -- Get and merge contents from both lines + serialized.inventory = {} + local line1_contents = line1.get_contents() + local line2_contents = line2.get_contents() + + -- Set terminus and source flags based on connections + serialized.is_terminus = #entity.belt_neighbours["outputs"] == 0 + serialized.is_source = #entity.belt_neighbours["inputs"] == 0 + + -- Merge contents from both belt lines + for item_name, count in pairs(line1_contents) do + serialized.inventory[item_name] = (serialized.inventory[item_name] or 0) + count + end + for item_name, count in pairs(line2_contents) do + serialized.inventory[item_name] = (serialized.inventory[item_name] or 0) + count + end + + -- Add warning if belt is full + if not serialized.warnings then + serialized.warnings = {} + end + if is_full then + table.insert(serialized.warnings, "Belt output is full") + end + + -- Special handling for underground belts + if entity.type == "underground-belt" then + serialized.is_input = entity.belt_to_ground_type == "input" + if serialized.is_input then + serialized.is_terminus = entity.neighbours == nil + else + serialized.is_source = entity.neighbours == nil + end + if entity.neighbours ~= nil then + serialized.connected_to = entity.neighbours.unit_number + end + end + end + + serialized.id = entity.unit_number + -- Special handling for power poles + if entity.type == "electric-pole" then + local stats = entity.electric_network_statistics + local contents_count = 0 + for name, count in pairs(stats.input_counts) do + contents_count = contents_count + count + end + + serialized.flow_rate = contents_count --stats.get_flow_count{name=…, input=…, precision_index=} + end + + -- Add input and output positions if the entity is a splitter + if entity.type == "splitter" then + -- Initialize positions based on entity center + local x, y = entity.position.x, entity.position.y + + -- Calculate the offset for left/right positions (0.5 tiles) + local lateral_offset = 0.5 + + if entity.direction == defines.direction.north then + -- Input positions (south side) + serialized.input_positions = { + {x = x - lateral_offset, y = y + 1}, + {x = x + lateral_offset, y = y + 1} + } + -- Output positions (north side) + serialized.output_positions = { + {x = x - lateral_offset, y = y - 1}, + {x = x + lateral_offset, y = y - 1} + } + elseif entity.direction == defines.direction.south then + -- Input positions (north side) + serialized.input_positions = { + {x = x + lateral_offset, y = y - 1}, + {x = x - lateral_offset, y = y - 1} + } + -- Output positions (south side) + serialized.output_positions = { + {x = x + lateral_offset, y = y + 1}, + {x = x - lateral_offset, y = y + 1} + } + elseif entity.direction == defines.direction.east then + -- Input positions (west side) + serialized.input_positions = { + {x = x - 1, y = y - lateral_offset}, + {x = x - 1, y = y + lateral_offset} + } + -- Output positions (east side) + serialized.output_positions = { + {x = x + 1, y = y - lateral_offset}, + {x = x + 1, y = y + lateral_offset} + } + elseif entity.direction == defines.direction.west then + -- Input positions (east side) + serialized.input_positions = { + {x = x + 1, y = y + lateral_offset}, + {x = x + 1, y = y - lateral_offset} + } + -- Output positions (west side) + serialized.output_positions = { + {x = x - 1, y = y + lateral_offset}, + {x = x - 1, y = y - lateral_offset} + } + end + + -- Get the contents of both output lines + serialized.inventory = { + entity.get_transport_line(1).get_contents(), + entity.get_transport_line(2).get_contents() + } + end + + -- Add input and output locations if the entity is an inserter + if entity.type == "inserter" then + serialized.pickup_position = entity.pickup_position + serialized.drop_position = entity.drop_position + + ---- round to the nearest 0.5 + serialized.pickup_position.x = math.round(serialized.pickup_position.x * 2 ) / 2 + serialized.pickup_position.y = math.round(serialized.pickup_position.y * 2 ) / 2 + serialized.drop_position.x = math.round(serialized.drop_position.x * 2 ) / 2 + serialized.drop_position.y = math.round(serialized.drop_position.y * 2 ) / 2 + + -- if pickup_position is nil, compute it from the entity's position and direction + if not serialized.pickup_position then + --local direction = entity.direction + local x, y = entity.position.x, entity.position.y + if entity.direction == defines.direction.north then + serialized.pickup_position = {x = x, y = y - 1} + elseif entity.direction == defines.direction.south then + serialized.pickup_position = {x = x, y = y + 1} + elseif entity.direction == defines.direction.east then + serialized.pickup_position = {x = x + 1, y = y} + elseif entity.direction == defines.direction.west then + serialized.pickup_position = {x = x - 1, y = y} + end + end + + local burner = entity.burner + if burner then + add_burner_inventory(serialized, burner) + end + end + + -- Add input and output locations if the entity is a splitter + --if entity.type == "splitter" then + -- serialized.input_position = entity.input_position + -- serialized.output_position = entity.output_position + --end + + -- Add input and output locations if the entity is a pipe + if entity.type == "pipe" then + serialized.connections = {} + local fluid_name = nil + for _, connection in pairs(entity.fluidbox.get_pipe_connections(1)) do + table.insert(serialized.connections, connection.position) + end + local contents_count = 0 + for name, count in pairs(entity.fluidbox.get_fluid_system_contents(1)) do + contents_count = contents_count + count + fluid_name = "\""..name.."\"" + end + serialized.contents = contents_count + serialized.fluid = fluid_name + serialized.fluidbox_id = entity.fluidbox.get_fluid_system_id(1) + serialized.flow_rate = entity.fluidbox.get_flow(1) + end + + -- Add input and output locations if the entity is a pipe-to-ground + if entity.type == "pipe-to-ground" then + serialized.connections = {} + local fluid_name = nil + for _, connection in pairs(entity.fluidbox.get_pipe_connections(1)) do + table.insert(serialized.connections, connection.position) + end + local contents_count = 0 + for name, count in pairs(entity.fluidbox.get_fluid_system_contents(1)) do + contents_count = contents_count + count + fluid_name = "\""..name.."\"" + end + serialized.fluidbox_id = entity.fluidbox.get_fluid_system_id(1) + serialized.flow_rate = entity.fluidbox.get_flow(1) + serialized.contents = contents_count + serialized.fluid = fluid_name + --serialized.input_position = entity.fluidbox.get_connections(1)[1].position + --serialized.output_position = entity.fluidbox.get_connections(2)[1].position + end + + -- Add input and output locations if the entity is a pump + if entity.type == "pump" then + serialized.input_position = entity.fluidbox.get_connections(1)[1].position + serialized.output_position = entity.fluidbox.get_connections(2)[1].position + end + + -- Add the current research to the lab + if entity.name == "lab" then + if global.agent_characters[1].force.current_research ~= nil then + serialized.research = global.agent_characters[1].force.current_research.name + else + serialized.research = nil + end + end + + -- Add input and output locations if the entity is a offshore pump + if entity.type == "offshore-pump" then + local burner = entity.burner + if burner then + add_burner_inventory(serialized, burner) + end + end + + + if entity.name == "oil-refinery" then + local x, y = entity.position.x, entity.position.y + serialized.input_connection_points = {} + serialized.output_connection_points = {} + + local recipe = entity.get_recipe() + local mappings = global.utils.get_refinery_fluid_mappings(entity, recipe) + if mappings then + serialized.input_connection_points = mappings.inputs + serialized.output_connection_points = mappings.outputs + end + --if entity.direction == defines.direction.north then + -- -- Two crude oil inputs at the bottom + -- table.insert(serialized.input_connection_points, + -- {x = x + 1, y = y + 3 + -- }) + -- table.insert(serialized.input_connection_points, + -- {x = x - 1, y = y + 3 + -- }) + -- -- Three outputs at the top (petroleum, light oil, heavy oil) + -- table.insert(serialized.output_connection_points, + -- {x = x - 2, y = y - 3 + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x, y = y - 3 + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x + 2, y = y - 3 + -- }) + --elseif entity.direction == defines.direction.south then + -- -- Two crude oil inputs at the top + -- table.insert(serialized.input_connection_points, + -- {x = x + 1, y = y - 3 + -- }) + -- table.insert(serialized.input_connection_points, + -- {x = x - 1, y = y - 3 + -- }) + -- -- Three outputs at the bottom + -- table.insert(serialized.output_connection_points, + -- {x = x - 2, y = y + 3 + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x, y = y + 3 + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x + 2, y = y + 3 + -- }) + --elseif entity.direction == defines.direction.east then + -- -- Two crude oil inputs on the left + -- table.insert(serialized.input_connection_points, + -- {x = x - 3, y = y + 1 + -- }) + -- table.insert(serialized.input_connection_points, + -- {x = x - 3, y = y - 1 + -- }) + -- -- Three outputs on the right + -- table.insert(serialized.output_connection_points, + -- {x = x + 3, y = y - 2 + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x + 3, y = y + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x + 3, y = y + 2 + -- }) + --elseif entity.direction == defines.direction.west then + -- -- Two crude oil inputs on the right + -- table.insert(serialized.input_connection_points, + -- {x = x + 3, y = y + 1 + -- }) + -- table.insert(serialized.input_connection_points, + -- {x = x + 3, y = y - 1 + -- }) + -- -- Three outputs on the left + -- table.insert(serialized.output_connection_points, + -- {x = x - 3, y = y - 2 + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x - 3, y = y + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x - 3, y = y + 2 + -- }) + --end + + ---- Filter out any invalid connection points + --local filtered_input_points = {} + --for _, point in ipairs(serialized.input_connection_points) do + -- if global.utils.is_valid_connection_point(game.surfaces[1], point.position) then + -- table.insert(filtered_input_points, point.positio) + -- end + --end + --serialized.input_connection_points = filtered_input_points + -- + --local filtered_output_points = {} + --for _, point in ipairs(serialized.output_connection_points) do + -- if global.utils.is_valid_connection_point(game.surfaces[1], point.position) then + -- table.insert(filtered_output_points, point.position) + -- end + --end + --serialized.output_connection_points = filtered_output_points + end + + if entity.name == "chemical-plant" then + local x, y = entity.position.x, entity.position.y + serialized.input_connection_points = {} + serialized.output_connection_points = {} + + local recipe = entity.get_recipe() + local mappings = global.utils.get_chemical_plant_fluid_mappings(entity, recipe) + if mappings then + serialized.input_connection_points = mappings.inputs + serialized.output_connection_points = mappings.outputs + end + + --if direction == defines.direction.north then + -- -- Input pipes at the bottom + -- table.insert(serialized.input_connection_points, + -- {x = x - 1, y = y + 1.5 + -- }) + -- table.insert(serialized.input_connection_points, + -- {x = x + 1, y = y + 1.5 + -- }) + -- -- Output pipes at the top + -- table.insert(serialized.output_connection_points, + -- {x = x - 1, y = y - 1.5 + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x + 1, y = y - 1.5 + -- }) + --elseif direction == defines.direction.south then + -- -- Input pipes at the top + -- table.insert(serialized.input_connection_points, + -- {x = x - 1, y = y - 1.5 + -- }) + -- table.insert(serialized.input_connection_points, + -- {x = x + 1, y = y - 1.5 + -- }) + -- -- Output pipes at the bottom + -- table.insert(serialized.output_connection_points, + -- {x = x - 1, y = y + 1.5 + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x + 1, y = y + 1.5 + -- }) + --elseif direction == defines.direction.east then + -- -- Input pipes on the left + -- table.insert(serialized.input_connection_points, + -- {x = x - 1.5, y = y - 1 + -- }) + -- table.insert(serialized.input_connection_points, + -- {x = x - 1.5, y = y + 1, + -- }) + -- -- Output pipes on the right + -- table.insert(serialized.output_connection_points, + -- {x = x + 1.5, y = y - 1, + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x + 1.5, y = y + 1 + -- }) + --elseif direction == defines.direction.west then + -- -- Input pipes on the right + -- table.insert(serialized.input_connection_points, + -- {x = x + 1.5, y = y - 1, + -- }) + -- table.insert(serialized.input_connection_points, + -- {x = x + 1.5, y = y + 1 + -- }) + -- -- Output pipes on the left + -- table.insert(serialized.output_connection_points, + -- {x = x - 1.5, y = y - 1 + -- }) + -- table.insert(serialized.output_connection_points, + -- {x = x - 1.5, y = y + 1 + -- }) + --end + + -- Filter out any invalid connection points + local filtered_input_points = {} + for _, point in ipairs(serialized.input_connection_points) do + if global.utils.is_valid_connection_point(game.surfaces[1], point) then + table.insert(filtered_input_points, point) + end + end + serialized.input_connection_points = filtered_input_points + + -- Filter out any invalid connection points + local filtered_output_points = {} + for _, point in ipairs(serialized.output_connection_points) do + if global.utils.is_valid_connection_point(game.surfaces[1], point) then + table.insert(filtered_output_points, point) + end + end + serialized.output_connection_points = filtered_output_points + + end + + + if entity.type == "storage-tank" then + -- Get and filter connection points + local connection_points = global.utils.get_storage_tank_connection_points(entity) + local filtered_points = {} + + -- Filter out invalid connection points (e.g., those in water) + for _, point in ipairs(connection_points) do + if global.utils.is_valid_connection_point(entity.surface, point) then + table.insert(filtered_points, point) + end + end + + -- Add connection points to serialized data + serialized.connection_points = filtered_points + + -- Add fluid box information + if entity.fluidbox and #entity.fluidbox > 0 then + game.print("There is a fluidbox") + local fluid = entity.fluidbox[1] + if fluid then + serialized.fluid = string.format("\"%s\"", fluid.name) + serialized.fluid_amount = fluid.amount + serialized.fluid_temperature = fluid.temperature + serialized.fluid_system_id = entity.fluidbox.get_fluid_system_id(1) + end + end + + -- Add warning if some connection points were filtered + if #filtered_points < #connection_points then + if not serialized.warnings then + serialized.warnings = {} + end + table.insert(serialized.warnings, "\"some connection points were filtered due to being blocked by water\"") + end + end + + + if entity.type == "assembling-machine" then + local x, y = entity.position.x, entity.position.y + serialized.connection_points = {} + + -- Assembling machine connection points are similar to chemical plants + if entity.direction == defines.direction.north then + table.insert(serialized.connection_points, + {x = x, y = y - 2 + }) + elseif entity.direction == defines.direction.south then + table.insert(serialized.connection_points, + {x = x, y = y + 2 + }) + elseif entity.direction == defines.direction.east then + table.insert(serialized.connection_points, + {x = x + 2, y = y + }) + elseif entity.direction == defines.direction.west then + table.insert(serialized.connection_points, + {x = x - 2, y = y + }) + end + + -- Filter out any invalid connection points + local filtered_connection_points = {} + for _, point in ipairs(serialized.connection_points) do + game.print(serpent.line(point)) + if global.utils.is_valid_connection_point(game.surfaces[1], point) then + table.insert(filtered_connection_points, point) + end + end + + serialized.connection_points = filtered_connection_points + end + -- Add tile dimensions of the entity + serialized.tile_dimensions = { + tile_width = prototype.tile_width, + tile_height = prototype.tile_height, + } + + -- Add drop position if the entity is a mining drill + --game.print("Entity type: " .. entity.type) + --game.print("Entity has burner: " .. tostring(entity.burner ~= nil)) + --game.print("Burner is burning: " .. tostring(entity.burner and entity.burner.currently_burning ~= nil)) + + --if entity.type == "mining-drill" then + -- serialized.drop_position = { + -- x = entity.drop_position.x, + -- y = entity.drop_position.y + -- } + -- serialized.drop_position.x = math.round(serialized.drop_position.x * 2 ) / 2 + -- serialized.drop_position.y = math.round(serialized.drop_position.y * 2 ) / 2 + -- game.print("Mining drill drop position: " .. serpent.line(serialized.drop_position)) + -- local burner = entity.burner + -- if burner then + -- add_burner_inventory(serialized, burner) + -- end + --end + + if entity.type == "mining-drill" then + serialized.drop_position = { + x = entity.drop_position.x, + y = entity.drop_position.y + } + serialized.drop_position.x = math.round(serialized.drop_position.x * 2) / 2 + serialized.drop_position.y = math.round(serialized.drop_position.y * 2) / 2 + -- game.print("Mining drill drop position: " .. serpent.line(serialized.drop_position)) + + -- Get the mining area + local prototype = game.entity_prototypes[entity.name] + local mining_area = prototype.mining_drill_radius * 2 + + local position = entity.position + + -- Initialize resources table + serialized.resources = {} + + -- Calculate the area to check based on mining drill radius + local start_x = position.x - mining_area/2 + local start_y = position.y - mining_area/2 + local end_x = position.x + mining_area/2 + local end_y = position.y + mining_area/2 + + local resources = game.surfaces[1].find_entities_filtered{ + area = {{start_x, start_y}, {end_x, end_y}}, + type = "resource", + } + + for _, resource in pairs(resources) do + if not serialized.resources[resource.name] then + serialized.resources[resource.name] = { + name = "\""..resource.name.."\"", + count = 0, + } + end + + -- Add the resource amount and position + serialized.resources[resource.name].count = serialized.resources[resource.name].count + resource.amount + end + + + -- Convert resources table to array for consistent ordering + local resources_array = {} + for _, resource_data in pairs(serialized.resources) do + table.insert(resources_array, resource_data) + end + serialized.resources = resources_array + + -- Add mining status + if #resources_array == 0 then + serialized.status = "\"no_minable_resources\"" + if not serialized.warnings then serialized.warnings = {} end + table.insert(serialized.warnings, "\"nothing to mine\"") + end + + -- Add burner info if applicable + local burner = entity.burner + if burner then + add_burner_inventory(serialized, burner) + end + end + + -- Add recipes if the entity is a crafting machine + if entity.type == "assembling-machine" or entity.type == "furnace" then + if entity.get_recipe() then + serialized.recipe = global.utils.serialize_recipe(entity.get_recipe()) + end + end + + -- Add fluid input point if the entity is a boiler + if entity.type == "boiler" then + local burner = entity.burner + if burner then + add_burner_inventory(serialized, burner) + end + + --local direction = entity.direction + local x, y = entity.position.x, entity.position.y + + if entity.direction == defines.direction.north then + -- game.print("Boiler direction is north") + serialized.connection_points = {{x = x - 2, y = y + 0.5}, {x = x + 2, y = y + 0.5}} + serialized.steam_output_point = {x = x, y = y - 1.5} + elseif entity.direction == defines.direction.south then + -- game.print("Boiler direction is south") + serialized.connection_points = {{x = x - 2, y = y - 0.5}, {x = x + 2, y = y - 0.5}} + serialized.steam_output_point = {x = x, y = y + 1.5} + elseif entity.direction == defines.direction.east then + -- game.print("Boiler direction is east") + serialized.connection_points = {{x = x - 0.5, y = y - 2}, {x = x - 0.5, y = y + 2}} + serialized.steam_output_point = {x = x + 1.5, y = y} + elseif entity.direction == defines.direction.west then + -- game.print("Boiler direction is west") + serialized.connection_points = {{x = x + 0.5, y = y - 2}, {x = x + 0.5, y = y + 2}} + serialized.steam_output_point = {x = x - 1.5, y = y} + end + end + + if entity.type == "rocket-silo" then + -- Basic rocket silo properties + serialized.rocket_parts = 0 -- Will be updated with actual count + serialized.rocket_progress = 0.0 -- Will be updated with actual progress + serialized.launch_count = entity.launch_count or 0 + + -- Get the current rocket details if one exists + --local rocket_inventory = entity.get_inventory(defines.inventory.rocket_silo_rocket) + --local rocket = entity.rocket or nil + + --if rocket then + -- -- If there's a rocket, get its state + -- serialized.rocket = { + -- status = global.entity_status_names[rocket.status] or "normal", + -- launch_progress = rocket.launch_progress * 100.0 -- Convert to percentage + -- } + -- + -- -- Check for payload + -- local payload_inventory = rocket.get_inventory(defines.inventory.cargo_unit) + -- if payload_inventory and not payload_inventory.is_empty() then + -- serialized.rocket.payload = payload_inventory.get_contents() + -- end + --end + + -- Get part construction progress + local parts_inventory = entity.get_inventory(defines.inventory.rocket_silo_input) + if parts_inventory then + serialized.rocket_parts = parts_inventory.get_item_count("rocket-part") + -- Each rocket needs 100 parts, calculate progress + serialized.rocket_progress = (serialized.rocket_parts / 100.0) * 100.0 + end + + -- Get input inventories for rocket components + local rocket_inventory = { + rocket_part = entity.get_inventory(defines.inventory.rocket_silo_input), + result = entity.get_inventory(defines.inventory.rocket_silo_output) + } + + -- Serialize the component inventories + for name, inventory in pairs(rocket_inventory) do + if inventory and not inventory.is_empty() then + serialized[name .. "_inventory"] = inventory.get_contents() + end + end + + -- Update status based on rocket state + if serialized.rocket then + if serialized.rocket.launch_progress > 0 then + serialized.status = "\"launching_rocket\"" + elseif serialized.rocket.payload then + serialized.status = "\"waiting_to_launch_rocket\"" + end + elseif serialized.rocket_parts < 100 then + if serialized.rocket_parts > 0 then + serialized.status = "\"preparing_rocket_for_launch\"" + end + end + + -- Add warnings based on state + if not serialized.warnings then + serialized.warnings = {} + end + if serialized.rocket_parts < 100 and serialized.rocket_parts > 0 then + table.insert(serialized.warnings, "\"waiting for rocket parts\"") + elseif serialized.status == "\"waiting_to_launch_rocket\"" then + table.insert(serialized.warnings, "\"ready to launch\"") + end + end + + if entity.type == "solar-panel" then + serialized.electric_output_flow_limit = entity.electric_output_flow_limit + end + + if entity.type == 'accumulator' then + --serialized.energy_source = entity.energy_source + --serialized.power_usage = entity.power_usage + --serialized.emissions = entity.emissions + serialized.energy = entity.energy + end + + if entity.type == "generator" then + serialized.connection_points = global.utils.get_generator_connection_positions(entity) + serialized.energy_generated_last_tick = entity.energy_generated_last_tick + --serialized.power_production = entity.power_production + end + + if entity.name == "pumpjack" then + serialized.connection_points = global.utils.get_pumpjack_connection_points(entity) + end + + -- Add fuel and input ingredients if the entity is a furnace or burner + if entity.type == "furnace" or entity.type == "burner" then + local burner = entity.burner + if burner then + add_burner_inventory(serialized, burner) + end + local input_inventory = entity.get_inventory(defines.inventory.furnace_source) + if input_inventory and #input_inventory > 0 then + serialized.input_inventory = {} + for i = 1, #input_inventory do + local item = input_inventory[i] + if item and item.valid_for_read then + table.insert(serialized.input_inventory, {name = "\""..item.name.."\"", count = item.count}) + end + end + end + end + + -- Add fluid box if the entity is an offshore pump + if entity.type == "offshore-pump" then + serialized.connection_points = global.utils.get_offshore_pump_connection_points(entity) + end + + -- If entity has a fluidbox + if entity.fluidbox then + local fluid_box = entity.fluidbox + if fluid_box and #fluid_box > 0 then + serialized.fluid_box = {} + for i = 1, #fluid_box do + -- game.print("Fluid!") + local fluid = fluid_box[i] + if fluid then + table.insert(serialized.fluid_box, {name = "\""..fluid.name.."\"", amount = fluid.amount, temperature = fluid.temperature}) + end + end + end + end + + -- Add fluid handler status check + if is_fluid_handler(entity.type) then + -- Check if the entity has a fluidbox + if not entity.fluidbox or #entity.fluidbox == 0 then + serialized.status = "\"not_connected\"" + if not serialized.warnings then + serialized.warnings = {} + end + table.insert(serialized.warnings, "\"missing fluid connection\"") + else + -- Additional fluid-specific checks + local fluid_systems = {} + local has_fluid = false + local fluid_contents = nil + for i = 1, #entity.fluidbox do + if entity.fluidbox[i] then + local system_id = entity.fluidbox.get_fluid_system_id(i) + if system_id then + table.insert(fluid_systems, system_id) + end + has_fluid = true + fluid_contents = "\""..entity.fluidbox[i].name.."\"" + break + end + end + serialized.fluid_systems = fluid_systems + + if not has_fluid then + serialized.status = "not_connected" + if not serialized.warnings then + serialized.warnings = {} + end + table.insert(serialized.warnings, "\"no fluid present in connections\"") + else + serialized.fluid = fluid_contents + end + --serialized.fluidbox = global.utils.serialize_fluidbox(entity.fluidbox) + end + end + + if entity.electric_network_id then + serialized.electrical_id = entity.electric_network_id + end + + serialized.direction = get_inverse_entity_direction(entity.name, entity.direction) --api_direction_map[entity.direction] + -- Post-process connection points if they exist + if serialized.connection_points then + local filtered_points = {} + for _, point in ipairs(serialized.connection_points) do + if global.utils.is_valid_connection_point(game.surfaces[1], point) then + table.insert(filtered_points, point) + end + end + + -- Update connection points or remove if all were filtered + if #filtered_points > 0 then + serialized.connection_points = filtered_points + else + serialized.connection_points = nil + end + + -- Add warning if points were filtered + if not serialized.warnings then + serialized.warnings = {} + end + + if serialized.connection_points ~= nil then + if #filtered_points < #serialized.connection_points then + table.insert(serialized.warnings, "\"some connection points were filtered due to being blocked by water\"") + end + end + end + + -- Handle special case for boilers which have separate steam output points + if serialized.steam_output_point then + if not global.utils.is_valid_connection_point(game.surfaces[1], serialized.steam_output_point) then + serialized.steam_output_point = nil + if not serialized.warnings then + serialized.warnings = {} + end + table.insert(serialized.warnings, "\"steam output point was filtered due to being blocked by water\"") + end + end + + return serialized +end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/util.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/util.lua new file mode 100644 index 000000000..c383f2033 --- /dev/null +++ b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/util.lua @@ -0,0 +1,690 @@ +util = +{ + table = {} +} + +-- Helper to check if a tile is water +function is_water_tile(tile_name) + return tile_name == "water" or + tile_name == "deepwater" or + tile_name == "water-green" or + tile_name == "deepwater-green" or + tile_name == "water-shallow" or + tile_name == "water-mud" +end + +function table.deepcopy(object) + local lookup_table = {} + local function _copy(object) + if type(object) ~= "table" then + return object + -- don't copy factorio rich objects + elseif object.__self then + return object + elseif lookup_table[object] then + return lookup_table[object] + end + local new_table = {} + lookup_table[object] = new_table + for index, value in pairs(object) do + new_table[_copy(index)] = _copy(value) + end + return setmetatable(new_table, getmetatable(object)) + end + return _copy(object) +end + +function table.compare( tbl1, tbl2 ) + if tbl1 == tbl2 then return true end + for k, v in pairs( tbl1 ) do + if type(v) == "table" and type(tbl2[k]) == "table" then + if not table.compare( v, tbl2[k] ) then return false end + else + if ( v ~= tbl2[k] ) then return false end + end + end + for k, v in pairs( tbl2 ) do + if tbl1[k] == nil then return false end + end + return true +end + +util.table.deepcopy = table.deepcopy +util.table.compare = table.compare +util.copy = util.table.deepcopy + +function util.distance(position1, position2) + local x1 = position1[1] or position1.x + local y1 = position1[2] or position1.y + local x2 = position2[1] or position2.x + local y2 = position2[2] or position2.y + return ((x1 - x2) ^ 2 + (y1 - y2) ^ 2) ^ 0.5 +end + +function util.positiontostr(pos) + return string.format("[%g, %g]", pos[1] or pos.x, pos[2] or pos.y) +end + +function util.formattime(ticks) + local seconds = ticks / 60 + local minutes = math.floor((seconds)/60) + local seconds = math.floor(seconds - 60*minutes) + return string.format("%d:%02d", minutes, seconds) +end + +function util.color(hex) -- supports 'rrggbb', 'rgb', 'rrggbbaa', 'rgba', 'ww', 'w' + local function h(i,j) + return j and tonumber("0x"..hex:sub(i,j)) / 255 or tonumber("0x"..hex:sub(i,i)) / 15 + end + + hex = hex:gsub("#","") + return #hex == 6 and {r = h(1,2), g = h(3,4), b = h(5,6)} + or #hex == 3 and {r = h(1), g = h(2), b = h(3)} + or #hex == 8 and {r = h(1,2), g = h(3,4), b = h(5,6), a = h(7,8)} + or #hex == 4 and {r = h(1), g = h(2), b = h(3), a = h(4)} + or #hex == 2 and {r = h(1,2), g = h(1,2), b = h(1,2)} + or #hex == 1 and {r = h(1), g = h(1), b = h(1)} + or {r=1, g=1, b=1} +end + +function util.premul_color(color) + local r = color.r or color[1] + local g = color.g or color[2] + local b = color.b or color[3] + local a = color.a or color[4] or 1 + return + { + r = r and (r * a), + g = g and (g * a), + b = b and (b * a), + a = a + } +end + +function util.mix_color(c1, c2) + return + { + (c1.r or c1[1] or 0) * (c2.r or c2[1] or 0), + (c1.g or c1[2] or 0) * (c2.g or c2[2] or 0), + (c1.b or c1[3] or 0) * (c2.b or c2[3] or 0), + (c1.a or c1[4] or 1) * (c2.a or c2[4] or 1) + } +end + +function util.multiply_color(c1, n) + return + { + (c1.r or c1[1] or 0) * (n or 0), + (c1.g or c1[2] or 0) * (n or 0), + (c1.b or c1[3] or 0) * (n or 0), + (c1.a or c1[4] or 1) * (n or 1) + } +end + +function util.get_color_with_alpha(color, alpha, normalized_alpha) + local new_color = + { + r = color.r or color[1] or 0, + g = color.g or color[2] or 0, + b = color.b or color[3] or 0 + } + new_color.a = normalized_alpha and (new_color.r > 1 or new_color.g > 1 or new_color.b > 1) and alpha * 255 or alpha + return new_color +end + +function util.moveposition(position, direction, distance) + + if direction == defines.direction.north then + return {position[1], position[2] - distance} + end + + if direction == defines.direction.south then + return {position[1], position[2] + distance} + end + + if direction == defines.direction.east then + return {position[1] + distance, position[2]} + end + + if direction == defines.direction.west then + return {position[1] - distance, position[2]} + end +end + +function util.oppositedirection(direction) + if not tonumber(direction) then error(direction .. " is not a valid direction") end + return (direction + 4) % 8 +end + +function util.multiplystripes(count, stripes) + local ret = {} + for _, stripe in ipairs(stripes) do + for _ = 1, count do + ret[#ret + 1] = stripe + end + end + return ret +end + +function util.by_pixel(x,y) + return {x / 32, y / 32} +end + +function util.by_pixel_hr(x,y) + return {x / 64, y / 64} +end + +function util.foreach_sprite_definition(table_, fun_) + --for k, tab in pairs(table_) do + fun_(table_) + if table_.hr_version then + fun_(table_.hr_version) + end + --end + return table_ +end + +function util.add_shift(a, b) + if not (a and b) then + return a or b + end + return { a[1] + b[1], a[2] + b[2] } +end + +function util.add_shift_offset(offset_, table_) + return + util.foreach_sprite_definition(table_, function(tab) + tab.shift = util.add_shift(tab.shift, offset_) + end) +end + +function util.mul_shift(shift, scale) + if not (shift and scale) then + return shift + end + return {shift[1] * scale, shift[2] * scale} +end + +function util.format_number(amount, append_suffix) + local suffix = "" + if append_suffix then + local suffix_list = + { + ["T"] = 1000000000000, + ["B"] = 1000000000, + ["M"] = 1000000, + ["k"] = 1000 + } + for letter, limit in pairs (suffix_list) do + if math.abs(amount) >= limit then + amount = math.floor(amount/(limit/10))/10 + suffix = letter + break + end + end + end + local formatted, k = amount + while true do + formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') + if (k==0) then + break + end + end + return formatted..suffix +end + +function util.increment(t, k, v) + t[k] = t[k] + (v or 1) +end + +function util.conditional_return(value, data) + return value and data +end + +-- Recursively merges and/or deep-copies tables. +-- Entries in later tables override entries in earlier ones, unless +-- both entries are themselves tables, in which case they are recursively merged. +-- Non-merged tables are deep-copied, so that the result is brand new. +function util.merge(tables) + local ret = {} + for i, tab in ipairs(tables) do + for k, v in pairs(tab) do + if (type(v) == "table") then + if (type(ret[k] or false) == "table") then + ret[k] = util.merge{ret[k], v} + else + ret[k] = table.deepcopy(v) + end + else + ret[k] = v + end + end + end + return ret +end + +util.insert_safe = function(entity, item_dict) + if not (entity and entity.valid and item_dict) then return end + local items = game.item_prototypes + local insert = entity.insert + for name, count in pairs (item_dict) do + if items[name] then + insert{name = name, count = count} + else + log("Item to insert not valid: "..name) + end + end +end + +util.remove_safe = function(entity, item_dict) + if not (entity and entity.valid and item_dict) then return end + local items = game.item_prototypes + local remove = entity.remove_item + for name, count in pairs (item_dict) do + if items[name] then + remove{name = name, count = count} + else + log("Item to remove not valid: "..name) + end + end +end + +util.split_whitespace = function(string) + if not string then return {} end + + local result = {} + for w in string:gmatch("%S+") do + table.insert(result, w) + end + return result +end + +util.split = function(inputstr, sep) + local result = {} + + for str in string.gmatch(inputstr, "([^"..sep.."]+)") do + table.insert(result, str) + end + + return result +end + +util.string_starts_with = function(str, start) + return str.sub(str, 1, string.len(start)) == start +end + +util.online_players = function() + log("But why?") + return game.connected_players +end + +util.clamp = function(x, lower, upper) + return math.max(lower, math.min(upper, x)) +end + +local walkable_mask = {"item-layer", "object-layer", "player-layer", "water-tile"} + +local is_walkable = function(mask) + for k, layer in pairs (walkable_mask) do + if mask[layer] then return false end + end + return true +end + +util.get_walkable_tile = function() + for name, tile in pairs (game.tile_prototypes) do + if is_walkable(tile.collision_mask) and not tile.items_to_place_this then + return name + end + end + error("No walkable tile in prototype list") +end + +-- This function takes 2 icons tables, and adds the second to the first, but applies scale, shift and tint to the entire second set. +-- This allows you to manipulate the entire second icons table in the same way as you would manipulate a single icon when adding to the icons table. +function util.combine_icons(icons1, icons2, inputs, default_icon_size) + scale = inputs.scale or 1 + shift = inputs.shift or {0, 0} + tint = inputs.tint or {r = 1, g = 1, b = 1, a = 1} + + local icons = table.deepcopy(icons1) + for _,icon_to_add in pairs(icons2) do + local icon = {} + icon.icon = icon_to_add.icon + icon.icon_size = icon_to_add.icon_size or default_icon_size or error("No icon size defined for icon \n"..serpent.block(icon)) + icon.scale = scale * (icon_to_add.scale or 32.0 / icon.icon_size) + icon.icon_mipmaps = icon_to_add.icon_mipmaps + if icon_to_add.shift then + icon.shift = {icon_to_add.shift[1] * scale + shift[1], icon_to_add.shift[2] * scale + shift[2]} + else + icon.shift = shift + end + if icon_to_add.tint then + icon.tint = util.mix_color(tint, icon_to_add.tint) + else + icon.tint = tint + end + table.insert(icons,icon) + end + return icons +end + +local energy_chars = +{ + k = 10^3, + K = 10^3, + M = 10^6, + G = 10^9, + T = 10^12, + P = 10^15, + E = 10^18, + Z = 10^21, + Y = 10^24 +} + +function util.technology_icon_constant_damage(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-damage.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_speed(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-speed.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_movement_speed(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-movement-speed.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_range(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-range.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_equipment(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-equipment.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_followers(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-count.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_capacity(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-capacity.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_stack_size(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-capacity.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_productivity(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-mining-productivity.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_braking_force(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-braking-force.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end +function util.technology_icon_constant_mining(technology_icon) + local icons = + { + { + icon = technology_icon, + icon_size = 256, icon_mipmaps = 4 + }, + { + icon = "__core__/graphics/icons/technology/constants/constant-mining.png", + icon_size = 128, + icon_mipmaps = 3, + shift = {100, 100} + } + } + return icons +end + +function util.parse_energy(energy) + + local ending = energy:sub(energy:len()) + if not (ending == "J" or ending == "W") then + error(ending.. " is not a valid unit of energy") + end + + local multiplier = (ending == "W" and 1 / 60) or 1 + local magnitude = energy:sub(energy:len() - 1, energy:len() - 1) + + if tonumber(magnitude) then + return tonumber(energy:sub(1, energy:len()-1)) * multiplier + end + + multiplier = multiplier * (energy_chars[magnitude] or error(magnitude.. " is not valid magnitude")) + return tonumber(energy:sub(1, energy:len()-2)) * multiplier + +end + +function util.product_amount(product) + return product.probability * (product.amount or ((product.amount_min + product.amount_max) / 2)) +end + +function util.empty_sprite(animation_length) + return + { + filename = "__core__/graphics/empty.png", + priority = "extra-high", + width = 1, + height = 1, + frame_count = 1, + repeat_count = animation_length, + direction_count = 1 + } +end + +function util.draw_as_glow(layer) + layer.draw_as_glow = true + if layer.hr_version then + layer.hr_version.draw_as_glow = true + end + return layer +end + +function util.remove_tile_references(data, array_of_tiles_to_remove) + -- Does not handle: + -- explicit tile filters in "selection-tool" items + -- ItemPrototype::place_as_tile + -- TilePrototype::next_direction + -- TilePrototype::transition_merges_with_tile + -- general tile transitions, only removes tile names from water_tile_type_names + + if (type(array_of_tiles_to_remove) ~= "table") then + error("The second parameter of util.remove_tile_reference() is expected to be array of strings.") + end + + local tiles_to_remove = {} + for i, n in pairs(array_of_tiles_to_remove) do + tiles_to_remove[n] = true + end + + local remove_from_mapping = function(mapping) + if not mapping then + return + end + + for _, item in pairs(mapping) do + if item.tiles then + for i, t in pairs(item.tiles) do + if t and tiles_to_remove[t] then + item.tiles[i] = nil + end + end + end + end + end + + for k,e in pairs(data.raw["character"]) do + remove_from_mapping(e.footstep_particle_triggers) + remove_from_mapping(e.synced_footstep_particle_triggers) + remove_from_mapping(e.footprint_particles) + end + + for k,e in pairs(data.raw["car"]) do + remove_from_mapping(e.track_particle_triggers) + end + + if data.raw["fire"] then + for k, fire in pairs(data.raw["fire"]) do + if fire.burnt_patch_alpha_variations then + for i, v in pairs(fire.burnt_patch_alpha_variations) do + if v and v.tile and tiles_to_remove[v.tile] then + fire.burnt_patch_alpha_variations[i] = nil + end + end + end + end + end + + if water_tile_type_names then + for i = #water_tile_type_names, 1, -1 do + if tiles_to_remove[water_tile_type_names[i]] then + table.remove(water_tile_type_names, i) + end + end + end +end + +local remove = table.remove +util.remove_from_list = function(list, value) + for k, v in pairs (list) do + if v == value then + remove(list, k) + return true + end + end + return false +end + +util.list_to_map = function(list) + local map = {} + --Here I am trusting you not to give a list of junk. + for k, value in pairs(list) do + map[value] = true + end + return map +end + +return util \ No newline at end of file diff --git a/fle/env/tools/__init__.py b/fle/env/factorio/tools/__init__.py similarity index 91% rename from fle/env/tools/__init__.py rename to fle/env/factorio/tools/__init__.py index 34927fdda..f421299b8 100644 --- a/fle/env/tools/__init__.py +++ b/fle/env/factorio/tools/__init__.py @@ -12,9 +12,9 @@ - Init: Initialization tool for setting up the game environment """ -from fle.env.tools.controller import Controller -from fle.env.tools.init import Init -from fle.env.tools.tool import Tool +from fle.env.factorio.tools.controller import Controller +from fle.env.factorio.tools.init import Init +from fle.env.factorio.tools.tool import Tool # Public API - expose the main base classes __all__ = [ diff --git a/fle/env/tools/admin/clear_collision_boxes/client.py b/fle/env/factorio/tools/admin/clear_collision_boxes/client.py similarity index 88% rename from fle/env/tools/admin/clear_collision_boxes/client.py rename to fle/env/factorio/tools/admin/clear_collision_boxes/client.py index 1493d7f4d..ad183d70d 100644 --- a/fle/env/tools/admin/clear_collision_boxes/client.py +++ b/fle/env/factorio/tools/admin/clear_collision_boxes/client.py @@ -1,4 +1,4 @@ -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class ClearCollisionBoxes(Tool): diff --git a/fle/env/factorio/tools/admin/clear_collision_boxes/server.lua b/fle/env/factorio/tools/admin/clear_collision_boxes/server.lua new file mode 100644 index 000000000..6d24e29d7 --- /dev/null +++ b/fle/env/factorio/tools/admin/clear_collision_boxes/server.lua @@ -0,0 +1,42 @@ +global.actions.clear_collision_boxes = function(player_index) + local player = global.agent_characters[player_index] + if not player then return end + + -- Clean up temporary entities + local clearance_entities = global.clearance_entities[player_index] + if not clearance_entities then return end + + -- Define search area around player (500 tile radius) + local search_area = { + left_top = { + x = player.position.x - 500, + y = player.position.y - 500 + }, + right_bottom = { + x = player.position.x + 500, + y = player.position.y + 500 + } + } + + -- Find all simple-entity-with-owner entities in the area + local entities = player.surface.find_entities_filtered{ + type = "simple-entity-with-owner", + area = search_area + } + + -- Destroy found entities + local count = 0 + for _, entity in pairs(entities) do + if entity.valid then + entity.destroy() + count = count + 1 + end + end + + -- Destroy all created entities + for _, entity in pairs(clearance_entities) do + if entity.valid then + entity.destroy() + end + end +end \ No newline at end of file diff --git a/fle/env/tools/admin/clear_entities/client.py b/fle/env/factorio/tools/admin/clear_entities/client.py similarity index 85% rename from fle/env/tools/admin/clear_entities/client.py rename to fle/env/factorio/tools/admin/clear_entities/client.py index 940be43ef..1edd9cf7b 100644 --- a/fle/env/tools/admin/clear_entities/client.py +++ b/fle/env/factorio/tools/admin/clear_entities/client.py @@ -1,4 +1,4 @@ -from fle.env.tools.init import Init +from fle.env.factorio.tools.init import Init class ClearEntities(Init): diff --git a/fle/env/factorio/tools/admin/clear_entities/server.lua b/fle/env/factorio/tools/admin/clear_entities/server.lua new file mode 100644 index 000000000..ea8224337 --- /dev/null +++ b/fle/env/factorio/tools/admin/clear_entities/server.lua @@ -0,0 +1,65 @@ +global.actions.clear_entities = function(player_index) + local function clear_area_of_entities(player, area, force_filter) + local surface = player.surface + local entities = surface.find_entities_filtered{ + area = area, + force = force_filter, + type = { + "accumulator", "ammo-turret", "arithmetic-combinator", "artillery-turret", + "assembling-machine", "beacon", "boiler", "constant-combinator", + "container", "curved-rail", "decider-combinator", "electric-pole", + "electric-turret", "fluid-turret", "furnace", "gate", "generator", + "heat-interface", "heat-pipe", "inserter", "lab", "lamp", + "land-mine", "linked-belt", "linked-container", "loader", + "loader-1x1", "market", "mining-drill", "offshore-pump", + "pipe", "pipe-to-ground", "power-switch", "programmable-speaker", + "pump", "radar", "rail-chain-signal", "rail-signal", + "reactor", "roboport", "rocket-silo", "solar-panel", + "splitter", "storage-tank", "straight-rail", "train-stop", + "transport-belt", "underground-belt", "wall" + } + } + + for _, entity in ipairs(entities) do + if entity and entity.valid and entity ~= player then + entity.destroy() + end + end + + -- Clear dropped items separately + local dropped_items = surface.find_entities_filtered{ + area = area, + name = "item-on-ground" + } + for _, item in ipairs(dropped_items) do + if item and item.valid then + item.destroy() + end + end + end + + local function reset_character_inventory(player) + for inventory_id, inventory in pairs(defines.inventory) do + local character_inventory = player.get_inventory(inventory) + if character_inventory then + character_inventory.clear() + end + end + end + + -- Main execution + local player = global.agent_characters[player_index] + local area = { + {player.position.x - 1000, player.position.y - 1000}, + {player.position.x + 1000, player.position.y + 1000} + } + + -- Clear player force entities + clear_area_of_entities(player, area, player.force) + -- Clear neutral force entities + clear_area_of_entities(player, area, "neutral") + + reset_character_inventory(player) + player.force.reset() + return 1 +end \ No newline at end of file diff --git a/fle/env/tools/admin/extend_collision_boxes/client.py b/fle/env/factorio/tools/admin/extend_collision_boxes/client.py similarity index 94% rename from fle/env/tools/admin/extend_collision_boxes/client.py rename to fle/env/factorio/tools/admin/extend_collision_boxes/client.py index a404d0ba9..1037773fe 100644 --- a/fle/env/tools/admin/extend_collision_boxes/client.py +++ b/fle/env/factorio/tools/admin/extend_collision_boxes/client.py @@ -1,5 +1,5 @@ from fle.env.entities import Position -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class ExtendCollisionBoxes(Tool): diff --git a/fle/env/factorio/tools/admin/extend_collision_boxes/server.lua b/fle/env/factorio/tools/admin/extend_collision_boxes/server.lua new file mode 100644 index 000000000..02c859524 --- /dev/null +++ b/fle/env/factorio/tools/admin/extend_collision_boxes/server.lua @@ -0,0 +1,201 @@ +-- Helper function for pumpjack fluid positions +--local function get_pumpjack_connection_positions(pumpjack) +-- local positions = {} +-- local x, y = pumpjack.position.x, pumpjack.position.y +-- +-- -- Pumpjack output position changes based on direction +-- if pumpjack.direction == defines.direction.north then +-- table.insert(positions, {x = x, y = y - 2}) +-- elseif pumpjack.direction == defines.direction.south then +-- table.insert(positions, {x = x, y = y + 2}) +-- elseif pumpjack.direction == defines.direction.east then +-- table.insert(positions, {x = x + 2, y = y}) +-- elseif pumpjack.direction == defines.direction.west then +-- table.insert(positions, {x = x - 2, y = y}) +-- end +-- +-- return positions +--end + +---- Helper function for boiler fluid positions +--local function get_boiler_connection_positions(boiler) +-- local positions = {} +-- local x, y = boiler.position.x, boiler.position.y +-- +-- if boiler.direction == defines.direction.north then +-- -- Water input points +-- table.insert(positions, {x = x - 2, y = y + 0.5}) +-- table.insert(positions, {x = x + 2, y = y + 0.5}) +-- -- Steam output point +-- table.insert(positions, {x = x, y = y - 1.5}) +-- elseif boiler.direction == defines.direction.south then +-- -- Water input points +-- table.insert(positions, {x = x - 2, y = y - 0.5}) +-- table.insert(positions, {x = x + 2, y = y - 0.5}) +-- -- Steam output point +-- table.insert(positions, {x = x, y = y + 1.5}) +-- elseif boiler.direction == defines.direction.east then +-- -- Water input points +-- table.insert(positions, {x = x - 0.5, y = y - 2}) +-- table.insert(positions, {x = x - 0.5, y = y + 2}) +-- -- Steam output point +-- table.insert(positions, {x = x + 1.5, y = y}) +-- elseif boiler.direction == defines.direction.west then +-- -- Water input points +-- table.insert(positions, {x = x + 0.5, y = y - 2}) +-- table.insert(positions, {x = x + 0.5, y = y + 2}) +-- -- Steam output point +-- table.insert(positions, {x = x - 1.5, y = y}) +-- end +-- +-- return positions +--end + + + +local function add_clearance_entities(surface, force, region, start_pos, end_pos) + local created_entities = {} + local all_positions = {} + + local epsilon = 0.707 -- Small value for floating point comparison + + -- Helper function to check if a position is start or end + local function is_excluded_position(pos) + return (math.abs(pos.x - start_pos.x) < epsilon and math.abs(pos.y - start_pos.y) < epsilon) or + (math.abs(pos.x - end_pos.x) < epsilon and math.abs(pos.y - end_pos.y) < epsilon) + end + + -- Find all relevant entities in the region + local entities = { + pipes = surface.find_entities_filtered{name = "pipe", force = force, area = region}, + boilers = surface.find_entities_filtered{name = "boiler", force = force, area = region}, + drills = surface.find_entities_filtered{type = "mining-drill", force = force, area = region}, + pumpjacks = surface.find_entities_filtered{name = "pumpjack", force = force, area = region}, + refineries = surface.find_entities_filtered{name = "oil-refinery", force = force, area = region}, + chemical_plants = surface.find_entities_filtered{name = "chemical-plant", force = force, area = region}, + storage_tanks = surface.find_entities_filtered{name = "storage-tank", force = force, area = region} + } + + + -- Draw debug circles for start and end positions + rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 1, g = 0, b = 0}, surface = surface, radius = 0.5, filled = false, target = start_pos, time_to_live = 60000} + rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 1, b = 0}, surface = surface, radius = 0.5, filled = false, target = end_pos, time_to_live = 60000} + + -- Collect positions from pipes + for _, pipe in pairs(entities.pipes) do + local pipe_positions = { + {x = pipe.position.x + 1, y = pipe.position.y}, + {x = pipe.position.x - 1, y = pipe.position.y}, + {x = pipe.position.x, y = pipe.position.y + 1}, + {x = pipe.position.x, y = pipe.position.y - 1} + } + for _, pos in pairs(pipe_positions) do + if not is_excluded_position(pos) then + table.insert(all_positions, pos) + end + end + end + + -- Collect positions from boilers + for _, boiler in pairs(entities.boilers) do + for _, pos in pairs(global.utils.get_boiler_connection_points(boiler)) do + if not is_excluded_position(pos) then + table.insert(all_positions, pos) + end + end + end + + -- Collect positions from pumpjacks + for _, pumpjack in pairs(entities.pumpjacks) do + for _, pos in pairs(global.utils.get_pumpjack_connection_points(pumpjack)) do + if not is_excluded_position(pos) then + table.insert(all_positions, pos) + end + end + end + + -- Collect positions from refineries + for _, refinery in pairs(entities.refineries) do + for _, pos in pairs(global.utils.get_refinery_connection_points(refinery)) do + if not is_excluded_position(pos) then + table.insert(all_positions, pos) + end + end + end + + -- Collect positions from chemical plants + for _, plant in pairs(entities.chemical_plants) do + for _, pos in pairs(global.utils.get_chemical_plant_connection_points(plant)) do + if not is_excluded_position(pos) then + table.insert(all_positions, pos) + end + end + end + + -- Collect positions from storage tanks + for _, tank in pairs(entities.storage_tanks) do + for _, pos in pairs(global.utils.get_storage_tank_connection_points(tank)) do + if not is_excluded_position(pos) then + table.insert(all_positions, pos) + end + end + end + -- game.print("There are "..#entities.drills) + for _, drill in pairs(entities.drills) do + -- game.print(serpent.block(drill)) + end + -- Collect positions from mining drills + for _, drill in pairs(entities.drills) do + -- game.print("Drop position ".. serpent.line(drill.drop_position)) + local drop_pos = drill.drop_position--{x=math.round(drill.drop_position.x*2)/2, y=math.round(drill.drop_position.y*2)/2} + if not is_excluded_position(drop_pos) then + table.insert(all_positions, drop_pos) + end + end + + -- Create entities at filtered positions + for _, pos in pairs(all_positions) do + -- Draw debug circles for connection points + rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 1, b = 1}, surface = surface, radius = 0.33, filled = false, target = pos, time_to_live = 60000} + + local entity = surface.create_entity{ + name = "simple-entity-with-owner", + position = pos, + force = force, + graphics_variation = 255, + render_player_index = 65535, + raise_built = false + } + if entity then + entity.destructible = false + entity.graphics_variation = 255 + entity.color = {r = 0, g = 0, b = 0, a = 0} + table.insert(created_entities, entity) + end + end + + return created_entities +end + +global.actions.extend_collision_boxes = function(player_index, start_x, start_y, goal_x, goal_y) + local player = global.agent_characters[player_index] + local start_pos = {x=start_x, y=start_y} + local end_pos = {x=goal_x, y=goal_y} + -- Define region for entity checking (add some margin around start/goal) + local region = { + left_top = { + x = math.min(start_x, goal_x) - 20, + y = math.min(start_y, goal_y) - 20 + }, + right_bottom = { + x = math.max(start_x, goal_x) + 20, + y = math.max(start_y, goal_y) + 20 + } + } + + -- Add buffer entities around all pipes, boilers, and drill drop positions + local created = add_clearance_entities(player.surface, player.force, region, start_pos, end_pos) + global.clearance_entities[player_index] = created + + return true +end \ No newline at end of file diff --git a/fle/env/tools/admin/get_factory_centroid/client.py b/fle/env/factorio/tools/admin/get_factory_centroid/client.py similarity index 98% rename from fle/env/tools/admin/get_factory_centroid/client.py rename to fle/env/factorio/tools/admin/get_factory_centroid/client.py index 53b03723c..4c05d9ba9 100644 --- a/fle/env/tools/admin/get_factory_centroid/client.py +++ b/fle/env/factorio/tools/admin/get_factory_centroid/client.py @@ -1,5 +1,5 @@ from fle.env.entities import BoundingBox, Position -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool from fle.env.utils.camera import Camera diff --git a/fle/env/factorio/tools/admin/get_factory_centroid/server.lua b/fle/env/factorio/tools/admin/get_factory_centroid/server.lua new file mode 100644 index 000000000..edb18b325 --- /dev/null +++ b/fle/env/factorio/tools/admin/get_factory_centroid/server.lua @@ -0,0 +1,198 @@ +-- Initialize global camera tracking variables in your script's initialization +-- Put this in your script's on_init or when you initialize your global table +function initialize_camera_tracking(initial_position) + initial_position = initial_position or {x = 0, y = 0} + + global.camera = global.camera or {} + global.camera.position = global.camera.position or {x = initial_position.x, y = initial_position.y} + global.camera.velocity = global.camera.velocity or {x = 0, y = 0} + global.camera.target = global.camera.target or {x = initial_position.x, y = initial_position.y} + global.camera.zoom = global.camera.zoom or 1 + global.camera.target_zoom = global.camera.target_zoom or 1 + + -- Smoothing coefficients (adjust these values to change camera behavior) + global.camera.position_smoothing = 0.05 -- How quickly to move toward target (0-1) + global.camera.zoom_smoothing = 0.1 -- How quickly to zoom toward target (0-1) + global.camera.velocity_damping = 0.6 -- How quickly velocity decays (0-1) + + -- Maximum velocity to prevent overshooting + global.camera.max_velocity = 3.0 +end + +-- Helper function to calculate factory bounds (unchanged) +function calculate_factory_bounds(force) + local min_x = math.huge + local max_x = -math.huge + local min_y = math.huge + local max_y = -math.huge + + -- Entity types to exclude from bounds calculation + local excluded_types = { + ["player"] = true, + ["character"] = true, + ["character-corpse"] = true, + ["item-entity"] = true, + ["particle"] = true, + ["projectile"] = true, + ["resource"] = true, + ["tree"] = true, + ["simple-entity"] = true + } + + -- Iterate through all surfaces + for _, surface in pairs(game.surfaces) do + local entities = surface.find_entities_filtered{ + force = force + } + + for _, entity in pairs(entities) do + if entity.valid and not excluded_types[entity.type] then + -- Get entity bounding box + local box = entity.bounding_box + + -- Update min/max coordinates + min_x = math.min(min_x, box.left_top.x) + max_x = math.max(max_x, box.right_bottom.x) + min_y = math.min(min_y, box.left_top.y) + max_y = math.max(max_y, box.right_bottom.y) + end + end + end + + -- Check if we found any entities + if min_x == math.huge then + return nil + end + + return { + left_top = {x = min_x, y = min_y}, + right_bottom = {x = max_x, y = max_y}, + width = max_x - min_x, + height = max_y - min_y + } +end + +-- Updated function to get factory centroid and update camera smoothly +global.actions.get_factory_centroid = function(player) + -- Default to player force if none specified + local force = "player" + + -- Get all surfaces in the game + local surfaces = game.surfaces + + -- Variables to track totals + local total_x = 0 + local total_y = 0 + local entity_count = 0 + + -- Entity types to exclude from centroid calculation + local excluded_types = { + ["player"] = true, + ["character"] = true, + ["character-corpse"] = true, + ["item-entity"] = true, -- dropped items + ["particle"] = true, + ["projectile"] = true, + ["resource"] = true, -- ore patches + ["tree"] = true, + ["simple-entity"] = true -- rocks and other decorative elements + } + + -- Iterate through all surfaces + for _, surface in pairs(surfaces) do + -- Get all entities on the surface belonging to the specified force + local entities = surface.find_entities_filtered{ + force = force + } + + -- Add up positions of valid entities + for _, entity in pairs(entities) do + if entity.valid and not excluded_types[entity.type] then + total_x = total_x + entity.position.x + total_y = total_y + entity.position.y + entity_count = entity_count + 1 + end + end + end + local centroid = {x=0, y=0} + -- Check if we found any entities + if entity_count > 0 then + -- Calculate average position (centroid) + centroid = { + x = total_x / entity_count, + y = total_y / entity_count + } + end + + -- Calculate bounds of the factory + local bounds = calculate_factory_bounds(force) + + -- Initialize camera tracking if it hasn't been initialized + if not global.camera then + initialize_camera_tracking(centroid) + -- Set initial camera position to centroid + --global.camera.position = {x = centroid.x, y = centroid.y} + --global.camera.target = {x = centroid.x, y = centroid.y} + end + + -- Update camera target + global.camera.target.x = centroid.x + global.camera.target.y = centroid.y + + -- Calculate appropriate zoom based on factory size + if bounds then + -- Calculate the diagonal size of the factory + local diagonal = math.sqrt(bounds.width^2 + bounds.height^2) + -- Determine a good zoom level based on factory size + -- Lower values = zoomed out more + global.camera.target_zoom = math.max(0.15, math.min(0.75, 35 / diagonal)) + end + + -- Update camera position with smooth interpolation + update_camera_position() + + -- Calculate additional statistics + local stats = { + centroid = centroid, + raw_centroid = centroid, + entity_count = entity_count, + bounds = bounds, + camera = { + position = global.camera.position, + zoom = global.camera.zoom + } + } + + return stats +end + +-- Function to update camera position with physics-based smoothing +function update_camera_position() + -- Calculate delta between current position and target + local dx = global.camera.target.x - global.camera.position.x + local dy = global.camera.target.y - global.camera.position.y + + -- Apply acceleration based on distance to target + global.camera.velocity.x = global.camera.velocity.x + dx * global.camera.position_smoothing + global.camera.velocity.y = global.camera.velocity.y + dy * global.camera.position_smoothing + + -- Apply velocity damping + global.camera.velocity.x = global.camera.velocity.x * global.camera.velocity_damping + global.camera.velocity.y = global.camera.velocity.y * global.camera.velocity_damping + + -- Clamp velocity to maximum + local velocity_magnitude = math.sqrt(global.camera.velocity.x^2 + global.camera.velocity.y^2) + if velocity_magnitude > global.camera.max_velocity then + local factor = global.camera.max_velocity / velocity_magnitude + global.camera.velocity.x = global.camera.velocity.x * factor + global.camera.velocity.y = global.camera.velocity.y * factor + end + + -- Update position based on velocity + global.camera.position.x = global.camera.position.x + global.camera.velocity.x + global.camera.position.y = global.camera.position.y + global.camera.velocity.y + + -- Smoothly interpolate zoom + local dz = global.camera.target_zoom - global.camera.zoom + global.camera.zoom = global.camera.zoom + dz * global.camera.zoom_smoothing +end diff --git a/fle/env/tools/admin/get_factory_centroid/server2.lua b/fle/env/factorio/tools/admin/get_factory_centroid/server2.lua similarity index 100% rename from fle/env/tools/admin/get_factory_centroid/server2.lua rename to fle/env/factorio/tools/admin/get_factory_centroid/server2.lua diff --git a/fle/env/factorio/tools/admin/get_messages/server.lua b/fle/env/factorio/tools/admin/get_messages/server.lua new file mode 100644 index 000000000..b6f2de2f6 --- /dev/null +++ b/fle/env/factorio/tools/admin/get_messages/server.lua @@ -0,0 +1 @@ +-- Left blank intentionally - this functionality is handled by the A2A protocol \ No newline at end of file diff --git a/fle/env/tools/admin/get_path/client.py b/fle/env/factorio/tools/admin/get_path/client.py similarity index 97% rename from fle/env/tools/admin/get_path/client.py rename to fle/env/factorio/tools/admin/get_path/client.py index a2b1d287d..4569389eb 100644 --- a/fle/env/tools/admin/get_path/client.py +++ b/fle/env/factorio/tools/admin/get_path/client.py @@ -2,7 +2,7 @@ from typing import List from fle.env.entities import Position -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class GetPath(Tool): diff --git a/fle/env/factorio/tools/admin/get_path/server.lua b/fle/env/factorio/tools/admin/get_path/server.lua new file mode 100644 index 000000000..b0428fdf8 --- /dev/null +++ b/fle/env/factorio/tools/admin/get_path/server.lua @@ -0,0 +1,38 @@ +-- Function to get the path as a JSON object +global.actions.get_path = function(request_id) + local request_data = global.path_requests[request_id] + if not request_data then + return game.table_to_json({status = "\"invalid_request\""}) + end + + if request_data == "pending" then + return game.table_to_json({status = "\"pending\""}) + end + + local path = global.paths[request_id] + if not path then + return game.table_to_json({status = "\"not_found\""}) + end + + if path == "busy" then + return game.table_to_json({status = "\"busy\""}) + elseif path == "not_found" then + return game.table_to_json({status = "\"not_found\""}) + else + local waypoints = {} + for _, waypoint in ipairs(path) do + table.insert(waypoints, { + x = waypoint.position.x, + y = waypoint.position.y + }) + end + -- create a beam bounding box at the start and end of the path + local start = path[1].position + local finish = path[#path].position + --create_beam_bounding_box(player, surface, 1, {x = start.x - 0.5, y = start.y - 0.5}, {x = finish.x + 0.5, y = finish.y + 0.5}) + return game.table_to_json({ + status = "\"success\"", + waypoints = waypoints + }) + end +end \ No newline at end of file diff --git a/fle/env/tools/admin/get_production_stats/client.py b/fle/env/factorio/tools/admin/get_production_stats/client.py similarity index 89% rename from fle/env/tools/admin/get_production_stats/client.py rename to fle/env/factorio/tools/admin/get_production_stats/client.py index 0087a88b2..069085d71 100644 --- a/fle/env/tools/admin/get_production_stats/client.py +++ b/fle/env/factorio/tools/admin/get_production_stats/client.py @@ -1,4 +1,4 @@ -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class GetProductionStats(Tool): diff --git a/fle/env/factorio/tools/admin/get_production_stats/server.lua b/fle/env/factorio/tools/admin/get_production_stats/server.lua new file mode 100644 index 000000000..ae38dbb2c --- /dev/null +++ b/fle/env/factorio/tools/admin/get_production_stats/server.lua @@ -0,0 +1,48 @@ +global.actions.production_stats = function(player) + local production_diff = {} + local consumption_diff = {} + local harvested_items = global.harvested_items + local crafted_items = global.crafted_items + -- Get total production counts for force + local force = game.forces.player + + local item_input_counts = force.item_production_statistics.input_counts + local item_production_counts = force.item_production_statistics.output_counts + local fluid_input_counts = force.fluid_production_statistics.input_counts + local fluid_production_counts = force.fluid_production_statistics.output_counts + + for name, count in pairs(item_input_counts) do + consumption_diff[name] = count + end + + for name, count in pairs(item_production_counts) do + production_diff[name] = count + end + + for name, count in pairs(fluid_input_counts) do + consumption_diff[name] = count + end + + for name, count in pairs(fluid_production_counts) do + production_diff[name] = count + end + return { + output = consumption_diff, + input = production_diff, + harvested = harvested_items, + crafted = crafted_items + } +end + +global.actions.reset_production_stats = function(player) + local force = game.forces.player + -- Reset item statistics + force.item_production_statistics.clear() + + -- Reset fluid statistics + force.fluid_production_statistics.clear() + + global.harvested_items = {} + global.crafted_items = {} +end + diff --git a/fle/env/tools/agent/get_connection_amount/server.lua b/fle/env/factorio/tools/admin/inspect_entities/deprecated similarity index 100% rename from fle/env/tools/agent/get_connection_amount/server.lua rename to fle/env/factorio/tools/admin/inspect_entities/deprecated diff --git a/fle/env/factorio/tools/admin/inspect_entities/server.lua b/fle/env/factorio/tools/admin/inspect_entities/server.lua new file mode 100644 index 000000000..72d7639d2 --- /dev/null +++ b/fle/env/factorio/tools/admin/inspect_entities/server.lua @@ -0,0 +1,123 @@ + +global.actions.inspect_entities = function(player_index, radius, position_x, position_y) + local player = global.agent_characters[player_index] + local position + + if position_x and position_y then + position = {x = tonumber(position_x), y = tonumber(position_y)} + else + position = player.position + end + + radius = tonumber(radius) or 5 + function find_connected_entities(entity) + local entities_to_search = { + {entity = entity, dx = 0, dy = -1, dir = defines.direction.north}, + {entity = entity, dx = 0, dy = 1, dir = defines.direction.south}, + {entity = entity, dx = 1, dy = 0, dir = defines.direction.east}, + {entity = entity, dx = -1, dy = 0, dir = defines.direction.west} + } + + local connected_entities = {} + + for _, search in ipairs(entities_to_search) do + local search_pos = {x = entity.position.x + search.dx, y = entity.position.y + search.dy} + local found_entity = entity.surface.find_entity(entity.name, search_pos) + + if found_entity then + if entity.type == "transport-belt" and found_entity.direction == search.dir then + table.insert(connected_entities, found_entity) + elseif entity.type == "pipe" then + table.insert(connected_entities, found_entity) + end + end + end + + return connected_entities + end + + + function recursive_search(entity, visited, path_ends) + if visited[entity.unit_number] then + return + end + + visited[entity.unit_number] = true + local connected_entities = find_connected_entities(entity) + + if connected_entities and #connected_entities > 0 then + local unconnected_neighbours = 0 + for _, connected_entity in ipairs(connected_entities) do + if not visited[connected_entity.unit_number] then + unconnected_neighbours = unconnected_neighbours + 1 + recursive_search(connected_entity, visited, path_ends) + end + end + + if unconnected_neighbours == 1 then + path_ends[entity.unit_number] = entity + end + else + path_ends[entity.unit_number] = entity + end + end + + function find_path_ends(entity) + local visited = {} + local path_ends = {} + recursive_search(entity, visited, path_ends) + return path_ends + end + + local entity_data = inspect(player, radius, position) + + local result = {} + + for i, data in ipairs(entity_data) do + + local warnings = {} + if not data.warnings then + data.warnings = {} + end + if data.warnings.fuel_warning then + table.insert(warnings, data.warnings.fuel_warning:gsub(" ", "_")) + end + if data.warnings.output_warning then + table.insert(warnings, data.warnings.output_warning:gsub(" ", "_")) + end + if data.warnings.input_warning then + table.insert(warnings, data.warnings.input_warning:gsub(" ", "_")) + end + + local position = {x=data.position.x, y=data.position.y} + + --game.print(data.name .. ": " .. data.direction .. " - ".. global.utils.get_entity_direction(data.name:gsub("_", "-"), data.direction)) + local entity_info = { + name = data.name:gsub("-", "_"), + position = position, + direction = global.utils.get_entity_direction(data.name:gsub("_", "-"), data.direction), + health = data.health, + force = data.force, + energy = data.energy, + contents = data.contents, + crafting_progress = data.crafting_progress, + productivity_bonus = data.productivity_bonus, + orientation = data.orientation, + ingredients = data.ingredients, + path_ends = data.path_ends, + warnings = data.warnings, + status = data.status + } + -- if crafted_items exists on data + if data.crafted_items then + entity_info.crafted_items = data.crafted_items + end + + --local serialized = global.utils.serialize_entity(data) + --table.insert(result, serialized) + + table.insert(result, entity_info) + end + + return dump(result) +end \ No newline at end of file diff --git a/fle/env/tools/admin/load_blueprint/client.py b/fle/env/factorio/tools/admin/load_blueprint/client.py similarity index 93% rename from fle/env/tools/admin/load_blueprint/client.py rename to fle/env/factorio/tools/admin/load_blueprint/client.py index 6059a39d6..f6d63d3f4 100644 --- a/fle/env/tools/admin/load_blueprint/client.py +++ b/fle/env/factorio/tools/admin/load_blueprint/client.py @@ -1,5 +1,5 @@ from fle.env.entities import Position -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class LoadBlueprint(Tool): diff --git a/fle/env/factorio/tools/admin/load_blueprint/server.lua b/fle/env/factorio/tools/admin/load_blueprint/server.lua new file mode 100644 index 000000000..8209f62f2 --- /dev/null +++ b/fle/env/factorio/tools/admin/load_blueprint/server.lua @@ -0,0 +1,64 @@ +global.actions.load_blueprint = function(player_index, bp, offset_x, offset_y) + --- https://forums.factorio.com/viewtopic.php?t=111437 + --- by CharacterOverflow @ Wed Feb 21, 2024 2:34 pm + + local s = game.get_surface('nauvis'); + local f = global.agent_characters[player_index].force + f.research_all_technologies(); + + -- Creates a blueprint entitiy in-game - it's weird, but it works. This blueprint entity has the blueprint loaded we want to run. + local bp_entity = s.create_entity { name = 'item-on-ground', position = { offset_x, offset_y }, stack = 'blueprint' } + local stack_id = bp_entity.stack.import_stack(bp) + + -- Now we call build_blueprint - this does not do *anything* like what you'd expect though. The force_build options is + -- only there simply to hold 'shift' while the blueprint is placed, meaning the blueprint itself still does not get + -- spawned, only the ghosts do. So, we then have much more actual work to do. + -- #TODO - Try to talk to factorio devs and have them either make an additional function (like auto_build_blueprint) or parameter. Would eliminate most of the lua code below, and likely be a speed boost + local bp_ghost = bp_entity.stack.build_blueprint { surface = s, force = f, position = { offset_x, offset_y }, force_build = true } + bp_entity.destroy() + + -- Go through and spawn the actual entities - except for ones that REQUIRE other entities, such as locomotives + local afterSpawns = {} + for _, entity in pairs(bp_ghost) do + + -- Should change this to go by ghost_type, if that's even a valid field. + if (entity.ghost_name == 'locomotive' or entity.ghost_name == 'cargo-wagon' or entity.ghost_name == 'fluid-wagon') then + table.insert(afterSpawns, entity) + else + if (entity ~= nil and entity.name == 'entity-ghost' and entity.ghost_type ~= nil and entity.item_requests ~= nil) then + local items = util.table.deepcopy(entity.item_requests) + -- game.print(serpent.block(entity.items)) + local p, ri = entity.revive(); + if (ri ~= nil) then + for k, v in pairs(items) do + ri.get_module_inventory().insert({ name = k, count = v }) + end + end + else + -- it's a normal thing like a belt or arm - we can just 'revive' the ghost, which will place the entity with all of the correct settings from the blueprint + entity.revive(); + end + end + + end + + -- This is used to place all locomotives and other train objects AFTER rails have been placed + for _, entity in pairs(afterSpawns) do + local r, to = entity.revive(); + end + + -- Set all trains to AUTOMATIC mode (manual = false) + for _, locomotive in pairs(game.surfaces["nauvis"].get_trains()) do + locomotive.manual_mode = false + end + + -- Add logistic bots to each roboport, based on input. One of the few variables, as some designs may be self-sufficient on bots + if (BOTS ~= nil and BOTS > 0) then + for _, roboport in pairs(game.surfaces["nauvis"].find_entities_filtered({ type = "roboport" })) do + roboport.insert({ name = "logistic-robot", count = BOTS }) + end + end + + return stack_id + +end diff --git a/fle/env/tools/admin/load_entity_state/client.py b/fle/env/factorio/tools/admin/load_entity_state/client.py similarity index 95% rename from fle/env/tools/admin/load_entity_state/client.py rename to fle/env/factorio/tools/admin/load_entity_state/client.py index 22e44812e..30a65ab7b 100644 --- a/fle/env/tools/admin/load_entity_state/client.py +++ b/fle/env/factorio/tools/admin/load_entity_state/client.py @@ -3,7 +3,7 @@ import zlib from typing import Union, List, Dict -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class LoadEntityState(Tool): diff --git a/fle/env/factorio/tools/admin/load_entity_state/server.lua b/fle/env/factorio/tools/admin/load_entity_state/server.lua new file mode 100644 index 000000000..8d78b6ec2 --- /dev/null +++ b/fle/env/factorio/tools/admin/load_entity_state/server.lua @@ -0,0 +1,289 @@ +-- Helper function to unquote strings +local function unquote_string(str) + if not str then return nil end + return string.gsub(str, '"', '') +end + +-- Main deserialization function +global.actions.load_entity_state = function(player, stored_json_data) + local player_entity = global.agent_characters[player] + local surface = player_entity.surface + local created_entities = {} + local stored_data = game.json_to_table(stored_json_data) + local character_states = {} + -- First pass: Create all non-character entities and store character states + for _, state in pairs(stored_data) do + local name = unquote_string(state.name) + + if name == "character" then + table.insert(character_states, state) + elseif name == "item-on-ground" then + local item_name = unquote_string(state.type) + local item_count = tonumber(state.count) + + if game.item_prototypes[item_name] then + local entity = surface.create_entity({ + name = name, + position = { + x = tonumber(state.position.x), + y = tonumber(state.position.y) + }, + stack = { + name = item_name, + count = item_count + }, + force = game.forces.player + }) + else + -- game.print("Warning: Unknown item type " .. item_name) + end + elseif state.type == "simple-entity-with-owner" then + -- Do nothing, we don't want to load in placeholder entities if they were somehow persisted! + else + local entity = surface.create_entity({ + name = name, + position = { + x = tonumber(state.position.x), + y = tonumber(state.position.y) + }, + direction = tonumber(state.direction), + force = game.forces.player, + raise_built = true + }) + + if entity then + created_entities[state.entity_number] = { + entity = entity, + state = state + } + end + end + end + + -- Handle characters separately + for _, character_state in ipairs(character_states) do + -- Store old character position if it exists + local old_position = nil + local agent_index = character_state.agent_index + local old_character = nil + + -- If we have a valid agent_index, get the old character from global.agent_characters + if agent_index and agent_index > 0 and global.agent_characters[agent_index] then + old_character = global.agent_characters[agent_index] + if old_character then + old_position = old_character.position + old_character.destroy() + end + end + + -- Create new character at the stored position or old position + local position = { + x = tonumber(character_state.position.x), + y = tonumber(character_state.position.y) + } + if not surface.can_place_entity{name="character", position=position} then + position = old_position or {x=0, y=0} + end + + local new_character = surface.create_entity({ + name = "character", + position = position, + direction = tonumber(character_state.direction), + force = game.forces.player, + }) + + if new_character then + -- Update global.agent_characters if we have a valid agent_index + if agent_index and agent_index > 0 then + global.agent_characters[agent_index] = new_character + end + + -- Restore character color if it exists + if character_state.color then + new_character.color = { + r = tonumber(character_state.color.r), + g = tonumber(character_state.color.g), + b = tonumber(character_state.color.b), + a = tonumber(character_state.color.a) + } + end + + -- Restore character inventory + if character_state.inventory then + local main_inventory = new_character.get_inventory(defines.inventory.character_main) + if main_inventory then + for item_name, count in pairs(character_state.inventory) do + -- Remove quotes if they exist + item_name = unquote_string(item_name) + if item_name and item_name ~= "" then + if game.item_prototypes[item_name] then + main_inventory.insert({ + name = item_name, + count = tonumber(count) + }) + else + -- game.print("Warning: Unknown item " .. item_name) + end + end + end + else + -- game.print("Warning: Could not get character main inventory") + end + end + + -- Add character to created_entities for inventory restoration + created_entities[character_state.entity_number] = { + entity = new_character, + state = character_state + } + end + end + + -- Second pass: Restore states + for unit_number, data in pairs(created_entities) do + local entity = data.entity + local state = data.state + local entity_type = entity.type + + -- game.print("Processing entity: " .. entity.name .. " (type: " .. entity_type .. ")") + + -- Restore inventories based on entity type + for inv_name, contents in pairs(state.inventories or {}) do + local inventory = nil + + -- Only try to access inventories that match the entity type + if inv_name == "chest" and entity_type == "container" then + inventory = entity.get_inventory(defines.inventory.chest) + elseif inv_name == "furnace_source" and entity_type == "furnace" then + inventory = entity.get_inventory(defines.inventory.furnace_source) + elseif inv_name == "furnace_result" and entity_type == "furnace" then + inventory = entity.get_inventory(defines.inventory.furnace_result) + elseif inv_name == "fuel" and entity.burner then + inventory = entity.get_inventory(defines.inventory.fuel) + elseif inv_name == "burnt_result" and entity.burner then + inventory = entity.get_inventory(defines.inventory.burnt_result) + elseif inv_name == "assembling_machine_input" and entity_type == "assembling-machine" then + inventory = entity.get_inventory(defines.inventory.assembling_machine_input) + elseif inv_name == "assembling_machine_output" and entity_type == "assembling-machine" then + inventory = entity.get_inventory(defines.inventory.assembling_machine_output) + elseif inv_name == "turret_ammo" and entity_type == "ammo-turret" then + inventory = entity.get_inventory(defines.inventory.turret_ammo) + elseif inv_name == "lab_input" and entity_type == "lab" then + inventory = entity.get_inventory(defines.inventory.lab_input) + end + + if inventory then + -- game.print("Found valid inventory for " .. inv_name) + for quoted_item_name, count in pairs(contents) do + local item_name = unquote_string(quoted_item_name) + if item_name and item_name ~= "" then + if game.item_prototypes[item_name] then + -- game.print("Inserting " .. count .. " " .. item_name) + inventory.insert({ + name = item_name, + count = tonumber(count) + }) + else + -- game.print("Warning: Unknown item " .. item_name) + end + else + -- game.print("Warning: Empty item name in " .. inv_name) + end + end + else + -- game.print("No valid inventory found for " .. inv_name) + end + end + + -- Restore burner + if state.burner and entity.burner then + if state.burner.currently_burning then + local burning_name = unquote_string(state.burner.currently_burning) + if game.item_prototypes[burning_name] then -- Verify burning item exists + entity.burner.currently_burning = game.item_prototypes[burning_name] + entity.burner.remaining_burning_fuel = tonumber(state.burner.remaining_burning_fuel) + entity.burner.heat = tonumber(state.burner.heat) + else + -- game.print("Warning: Unknown burning item " .. burning_name) + end + end + end + + -- Restore recipe - only for entities that can have recipes + if state.recipe then + -- Only try to set recipe on appropriate entity types + if (entity.type == "assembling-machine" or + entity.type == "furnace" or + entity.type == "rocket-silo") and + entity.get_recipe then -- Double check entity supports recipes + + local recipe_name = unquote_string(state.recipe.name) + if game.recipe_prototypes[recipe_name] then + -- game.print("Setting recipe " .. recipe_name .. " on " .. entity.name) + pcall(function() + entity.set_recipe(recipe_name) + end) + else + -- game.print("Warning: Unknown recipe " .. recipe_name) + end + else + -- game.print("Warning: Skipping recipe for incompatible entity type: " .. entity.type) + end + end + + -- Restore transport belt contents + if entity_type == "transport-belt" and state.transport_lines then + -- game.print("Has transport_lines field: " .. tostring(state.transport_lines ~= nil)) + -- Handle front line (line 1) + local line1 = entity.get_transport_line(1) + + if state.transport_lines["1"] then + -- game.print("Transport line 1") + for item_name, count in pairs(state.transport_lines["1"]) do + local name = unquote_string(item_name) + local item_count = tonumber(count) + if game.item_prototypes[name] then + for i = 1, item_count do + -- Space items evenly along the belt + local position = (i - 1) / item_count + line1.insert_at(position, { + name = name, + count = 1 + }) + end + end + end + end + + -- Handle back line (line 2) + local line2 = entity.get_transport_line(2) + if state.transport_lines["2"] then + -- game.print("Transport line 2") + for item_name, count in pairs(state.transport_lines["2"]) do + local name = unquote_string(item_name) + local item_count = tonumber(count) + if game.item_prototypes[name] then + for i = 1, item_count do + -- Space items evenly along the belt + local position = (i - 1) / item_count + line2.insert_at(position, { + name = name, + count = 1 + }) + end + end + end + end + end + + -- Restore energy and active state + if state.energy then + entity.energy = tonumber(state.energy) + end + if state.active ~= nil then + entity.active = state.active + end + end + + return true +end \ No newline at end of file diff --git a/fle/env/tools/admin/load_research_state/client.py b/fle/env/factorio/tools/admin/load_research_state/client.py similarity index 97% rename from fle/env/tools/admin/load_research_state/client.py rename to fle/env/factorio/tools/admin/load_research_state/client.py index 0500b15ce..261a8c069 100644 --- a/fle/env/tools/admin/load_research_state/client.py +++ b/fle/env/factorio/tools/admin/load_research_state/client.py @@ -1,5 +1,5 @@ from fle.commons.models.research_state import ResearchState -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class LoadResearchState(Tool): diff --git a/fle/env/factorio/tools/admin/load_research_state/server.lua b/fle/env/factorio/tools/admin/load_research_state/server.lua new file mode 100644 index 000000000..966ea298a --- /dev/null +++ b/fle/env/factorio/tools/admin/load_research_state/server.lua @@ -0,0 +1,48 @@ +-- Load research state +global.actions.load_research_state = function(player_index, research_state) + local player = global.agent_characters[player_index] + local force = player.force + + -- Reset all research first + for _, tech in pairs(force.technologies) do + tech.researched = false + force.set_saved_technology_progress(tech.name, nil) + --tech.enabled = false + end + + -- Apply saved research states + for name, tech_state in pairs(research_state.technologies) do + local tech = force.technologies[name] + if tech then + tech.researched = tech_state.researched + tech.enabled = tech_state.enabled + -- Level might need special handling for infinite research + if tech.level ~= tech_state.level then + tech.level = tech_state.level + end + end + end + + for name, progress in pairs(research_state.progress) do + force.set_saved_technology_progress(name, progress) + end + + -- Restore current research and progress + if research_state.current_research then + --if force.current_research then + -- force.cancel_current_research() + --end + force.add_research(research_state.current_research) + force.research_progress = research_state.research_progress + end + + -- Restore research queue if supported + if force.research_queue_enabled and research_state.research_queue then + force.research_queue = {} + for _, tech_name in ipairs(research_state.research_queue) do + force.add_research(tech_name) + end + end + + return true +end \ No newline at end of file diff --git a/fle/env/tools/admin/message_utils.py b/fle/env/factorio/tools/admin/message_utils.py similarity index 100% rename from fle/env/tools/admin/message_utils.py rename to fle/env/factorio/tools/admin/message_utils.py diff --git a/fle/env/tools/admin/production_stats/client.py b/fle/env/factorio/tools/admin/production_stats/client.py similarity index 97% rename from fle/env/tools/admin/production_stats/client.py rename to fle/env/factorio/tools/admin/production_stats/client.py index 117d33b7b..59e222ca1 100644 --- a/fle/env/tools/admin/production_stats/client.py +++ b/fle/env/factorio/tools/admin/production_stats/client.py @@ -1,6 +1,6 @@ from typing import Dict -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class ProductionStats(Tool): diff --git a/fle/env/factorio/tools/admin/production_stats/server.lua b/fle/env/factorio/tools/admin/production_stats/server.lua new file mode 100644 index 000000000..ae38dbb2c --- /dev/null +++ b/fle/env/factorio/tools/admin/production_stats/server.lua @@ -0,0 +1,48 @@ +global.actions.production_stats = function(player) + local production_diff = {} + local consumption_diff = {} + local harvested_items = global.harvested_items + local crafted_items = global.crafted_items + -- Get total production counts for force + local force = game.forces.player + + local item_input_counts = force.item_production_statistics.input_counts + local item_production_counts = force.item_production_statistics.output_counts + local fluid_input_counts = force.fluid_production_statistics.input_counts + local fluid_production_counts = force.fluid_production_statistics.output_counts + + for name, count in pairs(item_input_counts) do + consumption_diff[name] = count + end + + for name, count in pairs(item_production_counts) do + production_diff[name] = count + end + + for name, count in pairs(fluid_input_counts) do + consumption_diff[name] = count + end + + for name, count in pairs(fluid_production_counts) do + production_diff[name] = count + end + return { + output = consumption_diff, + input = production_diff, + harvested = harvested_items, + crafted = crafted_items + } +end + +global.actions.reset_production_stats = function(player) + local force = game.forces.player + -- Reset item statistics + force.item_production_statistics.clear() + + -- Reset fluid statistics + force.fluid_production_statistics.clear() + + global.harvested_items = {} + global.crafted_items = {} +end + diff --git a/fle/env/tools/admin/regenerate_resources/client.py b/fle/env/factorio/tools/admin/regenerate_resources/client.py similarity index 87% rename from fle/env/tools/admin/regenerate_resources/client.py rename to fle/env/factorio/tools/admin/regenerate_resources/client.py index 7cd4e7704..21b9051b4 100644 --- a/fle/env/tools/admin/regenerate_resources/client.py +++ b/fle/env/factorio/tools/admin/regenerate_resources/client.py @@ -1,4 +1,4 @@ -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class RegenerateResources(Tool): diff --git a/fle/env/factorio/tools/admin/regenerate_resources/server.lua b/fle/env/factorio/tools/admin/regenerate_resources/server.lua new file mode 100644 index 000000000..0c49c7021 --- /dev/null +++ b/fle/env/factorio/tools/admin/regenerate_resources/server.lua @@ -0,0 +1,37 @@ +global.actions.regenerate_resources = function(player_index) + local player = global.agent_characters[player_index] + local surface = player.surface + for _, ore in pairs(surface.find_entities_filtered({type="resource"})) do + -- skip oil + if ore.name ~= "crude-oil" then + ore.amount = 10000 + else + ore.amount = 300000 + end + end + player.force.reset() +end + +global.actions.regenerate_resources2 = function(player_index) + local player = global.agent_characters[player_index] + + local surface = player.surface + for _, e in pairs(surface.find_entities_filtered{type="resource"}) do + if e.prototype.infinite_resource then + e.amount = e.initial_amount + else + e.destroy() + end + end + local non_infinites = {} + for resource, prototype in pairs(game.get_filtered_entity_prototypes{{filter="type", type="resource"}}) do + if not prototype.infinite_resource then + table.insert(non_infinites, resource) + end + end + surface.regenerate_entity(non_infinites) + for _, e in pairs(surface.find_entities_filtered{type="mining-drill"}) do + e.update_connections() + end + return 1 +end \ No newline at end of file diff --git a/fle/env/tools/admin/render/agent.md b/fle/env/factorio/tools/admin/render/agent.md similarity index 100% rename from fle/env/tools/admin/render/agent.md rename to fle/env/factorio/tools/admin/render/agent.md diff --git a/fle/env/tools/admin/render/client.py b/fle/env/factorio/tools/admin/render/client.py similarity index 96% rename from fle/env/tools/admin/render/client.py rename to fle/env/factorio/tools/admin/render/client.py index 33af49052..86ab6dcf3 100644 --- a/fle/env/tools/admin/render/client.py +++ b/fle/env/factorio/tools/admin/render/client.py @@ -2,10 +2,10 @@ import math from fle.env import BoundingBox, Position, BeltGroup, PipeGroup, ElectricityGroup, Layer -from fle.env.tools.admin.render.rendered_image import RenderedImage -from fle.env.tools.admin.render.renderer import Renderer -from fle.env.tools.agent.get_entities.client import GetEntities -from fle.env.tools import Tool +from fle.env.factorio.tools.admin.render.rendered_image import RenderedImage +from fle.env.factorio.tools.admin.render.renderer import Renderer +from fle.env.factorio.tools.agent.get_entities.client import GetEntities +from fle.env.factorio.tools import Tool MAX_TILES = ( 20 # Don't parameterise this, as the agent could break if it chooses a huge grid. diff --git a/fle/env/tools/admin/render/layers/connection_layers_renderer.py b/fle/env/factorio/tools/admin/render/layers/connection_layers_renderer.py similarity index 98% rename from fle/env/tools/admin/render/layers/connection_layers_renderer.py rename to fle/env/factorio/tools/admin/render/layers/connection_layers_renderer.py index 60843f021..ed70a5fb0 100644 --- a/fle/env/tools/admin/render/layers/connection_layers_renderer.py +++ b/fle/env/factorio/tools/admin/render/layers/connection_layers_renderer.py @@ -2,7 +2,7 @@ from PIL import ImageDraw from fle.env.entities import Entity, UndergroundBelt, Pipe -from fle.env.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer class ConnectionsLayerRenderer(LayerRenderer): diff --git a/fle/env/tools/admin/render/layers/entities_layer_renderer.py b/fle/env/factorio/tools/admin/render/layers/entities_layer_renderer.py similarity index 97% rename from fle/env/tools/admin/render/layers/entities_layer_renderer.py rename to fle/env/factorio/tools/admin/render/layers/entities_layer_renderer.py index ff7dc3230..6fcf1d440 100644 --- a/fle/env/tools/admin/render/layers/entities_layer_renderer.py +++ b/fle/env/factorio/tools/admin/render/layers/entities_layer_renderer.py @@ -2,7 +2,7 @@ from PIL import ImageDraw from fle.env.entities import Direction, EntityStatus -from fle.env.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer class EntitiesLayerRenderer(LayerRenderer): diff --git a/fle/env/tools/admin/render/layers/grid_layer_renderer.py b/fle/env/factorio/tools/admin/render/layers/grid_layer_renderer.py similarity index 94% rename from fle/env/tools/admin/render/layers/grid_layer_renderer.py rename to fle/env/factorio/tools/admin/render/layers/grid_layer_renderer.py index 5cadfca17..d1b72bac3 100644 --- a/fle/env/tools/admin/render/layers/grid_layer_renderer.py +++ b/fle/env/factorio/tools/admin/render/layers/grid_layer_renderer.py @@ -1,7 +1,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer class GridLayerRenderer(LayerRenderer): diff --git a/fle/env/tools/admin/render/layers/layer_renderer.py b/fle/env/factorio/tools/admin/render/layers/layer_renderer.py similarity index 93% rename from fle/env/tools/admin/render/layers/layer_renderer.py rename to fle/env/factorio/tools/admin/render/layers/layer_renderer.py index a86f7c271..3850fe962 100644 --- a/fle/env/tools/admin/render/layers/layer_renderer.py +++ b/fle/env/factorio/tools/admin/render/layers/layer_renderer.py @@ -2,7 +2,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.tools.admin.render.utils.render_config import RenderConfig +from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig class LayerRenderer(ABC): diff --git a/fle/env/tools/admin/render/layers/marker_layers_renderer.py b/fle/env/factorio/tools/admin/render/layers/marker_layers_renderer.py similarity index 96% rename from fle/env/tools/admin/render/layers/marker_layers_renderer.py rename to fle/env/factorio/tools/admin/render/layers/marker_layers_renderer.py index b5a3f92a9..86bda0f78 100644 --- a/fle/env/tools/admin/render/layers/marker_layers_renderer.py +++ b/fle/env/factorio/tools/admin/render/layers/marker_layers_renderer.py @@ -2,7 +2,7 @@ from PIL import ImageDraw from fle.env.entities import Position, Layer -from fle.env.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer class MarkersLayerRenderer(LayerRenderer): diff --git a/fle/env/tools/admin/render/layers/natural_layer_renderer.py b/fle/env/factorio/tools/admin/render/layers/natural_layer_renderer.py similarity index 99% rename from fle/env/tools/admin/render/layers/natural_layer_renderer.py rename to fle/env/factorio/tools/admin/render/layers/natural_layer_renderer.py index 3683eeca4..9bedb9e26 100644 --- a/fle/env/tools/admin/render/layers/natural_layer_renderer.py +++ b/fle/env/factorio/tools/admin/render/layers/natural_layer_renderer.py @@ -4,7 +4,7 @@ from PIL import ImageDraw from fle.env.entities import Layer -from fle.env.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer class NaturalLayerRenderer(LayerRenderer): diff --git a/fle/env/tools/admin/render/layers/resource_layer_renderer.py b/fle/env/factorio/tools/admin/render/layers/resource_layer_renderer.py similarity index 98% rename from fle/env/tools/admin/render/layers/resource_layer_renderer.py rename to fle/env/factorio/tools/admin/render/layers/resource_layer_renderer.py index 8f2d9b64c..0ebedc712 100644 --- a/fle/env/tools/admin/render/layers/resource_layer_renderer.py +++ b/fle/env/factorio/tools/admin/render/layers/resource_layer_renderer.py @@ -2,7 +2,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer class ResourcesLayerRenderer(LayerRenderer): diff --git a/fle/env/tools/admin/render/layers/water_layer_renderer.py b/fle/env/factorio/tools/admin/render/layers/water_layer_renderer.py similarity index 98% rename from fle/env/tools/admin/render/layers/water_layer_renderer.py rename to fle/env/factorio/tools/admin/render/layers/water_layer_renderer.py index 05debc8a9..9915d4d50 100644 --- a/fle/env/tools/admin/render/layers/water_layer_renderer.py +++ b/fle/env/factorio/tools/admin/render/layers/water_layer_renderer.py @@ -1,7 +1,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer class WaterLayerRenderer(LayerRenderer): diff --git a/fle/env/tools/admin/render/rendered_image.py b/fle/env/factorio/tools/admin/render/rendered_image.py similarity index 100% rename from fle/env/tools/admin/render/rendered_image.py rename to fle/env/factorio/tools/admin/render/rendered_image.py diff --git a/fle/env/tools/admin/render/renderer.py b/fle/env/factorio/tools/admin/render/renderer.py similarity index 89% rename from fle/env/tools/admin/render/renderer.py rename to fle/env/factorio/tools/admin/render/renderer.py index 703cabc4a..f5d3054f7 100644 --- a/fle/env/tools/admin/render/renderer.py +++ b/fle/env/factorio/tools/admin/render/renderer.py @@ -2,34 +2,34 @@ from typing import List, Dict, Optional from fle.env.entities import Entity, Position, BoundingBox, Layer, EntityStatus -from fle.env.tools.admin.render.layers.connection_layers_renderer import ( +from fle.env.factorio.tools.admin.render.layers.connection_layers_renderer import ( ConnectionsLayerRenderer, ) -from fle.env.tools.admin.render.layers.marker_layers_renderer import ( +from fle.env.factorio.tools.admin.render.layers.marker_layers_renderer import ( MarkersLayerRenderer, ) -from fle.env.tools.admin.render.layers.resource_layer_renderer import ( +from fle.env.factorio.tools.admin.render.layers.resource_layer_renderer import ( ResourcesLayerRenderer, ) -from fle.env.tools.admin.render.utils.electricity_renderer import ( +from fle.env.factorio.tools.admin.render.utils.electricity_renderer import ( ElectricityLayerRenderer, ) -from fle.env.tools.admin.render.utils.render_config import RenderConfig -from fle.env.tools.admin.render.utils.entity_categoriser import EntityCategoriser -from fle.env.tools.admin.render.utils.colour_manager import ColourManager -from fle.env.tools.admin.render.utils.shape_renderer import ShapeRenderer -from fle.env.tools.admin.render.utils.legend_renderer import LegendRenderer -from fle.env.tools.admin.render.utils.connection_renderer import ConnectionRenderer -from fle.env.tools.admin.render.utils.image_calculator import ImageCalculator +from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig +from fle.env.factorio.tools.admin.render.utils.entity_categoriser import EntityCategoriser +from fle.env.factorio.tools.admin.render.utils.colour_manager import ColourManager +from fle.env.factorio.tools.admin.render.utils.shape_renderer import ShapeRenderer +from fle.env.factorio.tools.admin.render.utils.legend_renderer import LegendRenderer +from fle.env.factorio.tools.admin.render.utils.connection_renderer import ConnectionRenderer +from fle.env.factorio.tools.admin.render.utils.image_calculator import ImageCalculator # Import layer renderers -from fle.env.tools.admin.render.layers.grid_layer_renderer import GridLayerRenderer -from fle.env.tools.admin.render.layers.water_layer_renderer import WaterLayerRenderer -from fle.env.tools.admin.render.layers.natural_layer_renderer import ( +from fle.env.factorio.tools.admin.render.layers.grid_layer_renderer import GridLayerRenderer +from fle.env.factorio.tools.admin.render.layers.water_layer_renderer import WaterLayerRenderer +from fle.env.factorio.tools.admin.render.layers.natural_layer_renderer import ( NaturalLayerRenderer, ) -from fle.env.tools.admin.render.layers.entities_layer_renderer import ( +from fle.env.factorio.tools.admin.render.layers.entities_layer_renderer import ( EntitiesLayerRenderer, ) diff --git a/fle/env/factorio/tools/admin/render/server.lua b/fle/env/factorio/tools/admin/render/server.lua new file mode 100644 index 000000000..0f7481627 --- /dev/null +++ b/fle/env/factorio/tools/admin/render/server.lua @@ -0,0 +1,157 @@ +global.actions.render = function(player_index, method, arg1, arg2, arg3, arg4) + local player = global.agent_characters[player_index] + local position, bounding_box, radius + + if method == "bounding_box" then + -- If using bounding box method + bounding_box = { + left_top = {x = tonumber(arg1), y = tonumber(arg2)}, + right_bottom = {x = tonumber(arg3), y = tonumber(arg4)} + } + elseif method == "radius" then + -- If using radius method + local pos_x = tonumber(arg1) + local pos_y = tonumber(arg2) + radius = tonumber(arg3) or 40 + + position = {x = pos_x, y = pos_y} + bounding_box = { + left_top = {x = position.x - radius, y = position.y - radius}, + right_bottom = {x = position.x + radius, y = position.y + radius} + } + else + return "\"Error: Invalid method\"" + end + + -- Get all entities + local area = { + {bounding_box.left_top.x, bounding_box.left_top.y}, + {bounding_box.right_bottom.x, bounding_box.right_bottom.y} + } + + -- Get water tiles + local water_tiles = {} + for x = math.floor(area[1][1]), math.ceil(area[2][1]) do + for y = math.floor(area[1][2]), math.ceil(area[2][2]) do + local tile = player.surface.get_tile(x, y) + if tile and tile.valid and tile.name and (tile.name:find("water") or tile.name == "deepwater" or tile.name == "water") then + table.insert(water_tiles, { + x = tile.position.x, + y = tile.position.y, + name = "\""..tile.name.."\"" + }) + end + end + end + + -- Get resource entities + local resource_types = {"iron-ore", "copper-ore", "coal", "stone", "uranium-ore", "crude-oil"} + local resources = {} + + for _, resource_type in ipairs(resource_types) do + local resource_entities = player.surface.find_entities_filtered{ + area = area, + name = resource_type + } + + for _, entity in ipairs(resource_entities) do + local resource_data = { + name = "\""..entity.name.."\"", + position = { + x = entity.position.x, + y = entity.position.y + }, + amount = entity.amount + } + + table.insert(resources, resource_data) + end + end + + -- Get trees + local trees = {} + local tree_entities = player.surface.find_entities_filtered{ + area = area, + type = "tree" + } + + for _, tree in ipairs(tree_entities) do + local tree_data = { + name = "\""..tree.name.."\"", + position = { + x = tree.position.x, + y = tree.position.y + } + } + + -- Add tree size if available + if tree.prototype and tree.prototype.tree_color_count then + tree_data.size = tree.prototype.tree_color_count + end + + table.insert(trees, tree_data) + end + + -- Get rocks + local rocks = {} + local rock_entities = player.surface.find_entities_filtered{ + area = area, + type = "simple-entity" + } + + for _, rock in ipairs(rock_entities) do + -- Check if it's actually a rock (naming convention for rocks typically includes "rock" or "stone") + if rock.name and (rock.name:find("rock") or rock.name:find("stone")) then + local rock_data = { + name = "\""..rock.name.."\"", + position = { + x = rock.position.x, + y = rock.position.y + } + } + + table.insert(rocks, rock_data) + end + end + + -- Get electricity network information + local electricity_networks = {} + local electric_poles = player.surface.find_entities_filtered{ + area = area, + type = {"electric-pole", "power-switch"} + } + + -- Process electric poles to get network information + for _, pole in ipairs(electric_poles) do + if pole.valid and pole.electric_network_id then + local network_id = pole.electric_network_id + local supply_area = pole.prototype.supply_area_distance or 0 + + -- Get the area this pole covers + local pole_data = { + position = { + x = pole.position.x, + y = pole.position.y + }, + network_id = network_id, + supply_area = supply_area, + name = "\""..pole.name.."\"" + } + + table.insert(electricity_networks, pole_data) + end + end + + -- Combine all data for response + local render_data = { + water_tiles = water_tiles, + resources = resources, + trees = trees, + rocks = rocks, + electricity_networks = electricity_networks, + bounding_box = bounding_box, + position = position + } + + return dump(render_data) +end diff --git a/fle/env/tools/admin/render/utils/colour_manager.py b/fle/env/factorio/tools/admin/render/utils/colour_manager.py similarity index 97% rename from fle/env/tools/admin/render/utils/colour_manager.py rename to fle/env/factorio/tools/admin/render/utils/colour_manager.py index 5c4062f85..2e8d2e58b 100644 --- a/fle/env/tools/admin/render/utils/colour_manager.py +++ b/fle/env/factorio/tools/admin/render/utils/colour_manager.py @@ -4,8 +4,8 @@ from fle.env.entities import Entity, EntityStatus from fle.env.game_types import prototype_by_name -from fle.env.tools.admin.render.utils.render_config import RenderConfig -from fle.env.tools.admin.render.utils.entity_categoriser import EntityCategoriser +from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig +from fle.env.factorio.tools.admin.render.utils.entity_categoriser import EntityCategoriser class ColourManager: diff --git a/fle/env/tools/admin/render/utils/connection_renderer.py b/fle/env/factorio/tools/admin/render/utils/connection_renderer.py similarity index 98% rename from fle/env/tools/admin/render/utils/connection_renderer.py rename to fle/env/factorio/tools/admin/render/utils/connection_renderer.py index 78af526dc..468aef5bc 100644 --- a/fle/env/tools/admin/render/utils/connection_renderer.py +++ b/fle/env/factorio/tools/admin/render/utils/connection_renderer.py @@ -3,7 +3,7 @@ from PIL import ImageDraw from fle.env import UndergroundBelt, Pipe -from fle.env.tools.admin.render.utils.colour_manager import ColourManager +from fle.env.factorio.tools.admin.render.utils.colour_manager import ColourManager class ConnectionRenderer: diff --git a/fle/env/tools/admin/render/utils/electricity_renderer.py b/fle/env/factorio/tools/admin/render/utils/electricity_renderer.py similarity index 98% rename from fle/env/tools/admin/render/utils/electricity_renderer.py rename to fle/env/factorio/tools/admin/render/utils/electricity_renderer.py index 797266c09..37c0bde2d 100644 --- a/fle/env/tools/admin/render/utils/electricity_renderer.py +++ b/fle/env/factorio/tools/admin/render/utils/electricity_renderer.py @@ -3,7 +3,7 @@ from PIL import ImageDraw from fle.env.entities import Layer -from fle.env.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer class ElectricityLayerRenderer(LayerRenderer): diff --git a/fle/env/tools/admin/render/utils/entity_categoriser.py b/fle/env/factorio/tools/admin/render/utils/entity_categoriser.py similarity index 100% rename from fle/env/tools/admin/render/utils/entity_categoriser.py rename to fle/env/factorio/tools/admin/render/utils/entity_categoriser.py diff --git a/fle/env/tools/admin/render/utils/image_calculator.py b/fle/env/factorio/tools/admin/render/utils/image_calculator.py similarity index 98% rename from fle/env/tools/admin/render/utils/image_calculator.py rename to fle/env/factorio/tools/admin/render/utils/image_calculator.py index 81ed84e47..b3e0d4e5f 100644 --- a/fle/env/tools/admin/render/utils/image_calculator.py +++ b/fle/env/factorio/tools/admin/render/utils/image_calculator.py @@ -1,7 +1,7 @@ from typing import List, Optional, Dict, Callable from fle.env.entities import Entity, Position, BoundingBox, Direction -from fle.env.tools.admin.render.utils.render_config import RenderConfig +from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig class ImageCalculator: diff --git a/fle/env/tools/admin/render/utils/legend_renderer.py b/fle/env/factorio/tools/admin/render/utils/legend_renderer.py similarity index 99% rename from fle/env/tools/admin/render/utils/legend_renderer.py rename to fle/env/factorio/tools/admin/render/utils/legend_renderer.py index cba32e177..88672311d 100644 --- a/fle/env/tools/admin/render/utils/legend_renderer.py +++ b/fle/env/factorio/tools/admin/render/utils/legend_renderer.py @@ -3,10 +3,10 @@ import math from fle.env.entities import EntityStatus -from fle.env.tools.admin.render.utils.render_config import RenderConfig -from fle.env.tools.admin.render.utils.colour_manager import ColourManager -from fle.env.tools.admin.render.utils.entity_categoriser import EntityCategoriser -from fle.env.tools.admin.render.utils.shape_renderer import ShapeRenderer +from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig +from fle.env.factorio.tools.admin.render.utils.colour_manager import ColourManager +from fle.env.factorio.tools.admin.render.utils.entity_categoriser import EntityCategoriser +from fle.env.factorio.tools.admin.render.utils.shape_renderer import ShapeRenderer class LegendRenderer: diff --git a/fle/env/tools/admin/render/utils/natural_renderer.py b/fle/env/factorio/tools/admin/render/utils/natural_renderer.py similarity index 99% rename from fle/env/tools/admin/render/utils/natural_renderer.py rename to fle/env/factorio/tools/admin/render/utils/natural_renderer.py index 526477a45..ef9835c48 100644 --- a/fle/env/tools/admin/render/utils/natural_renderer.py +++ b/fle/env/factorio/tools/admin/render/utils/natural_renderer.py @@ -3,7 +3,7 @@ import math import random -from fle.env.tools.admin.render.utils.render_config import RenderConfig +from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig class NaturalRenderer: diff --git a/fle/env/tools/admin/render/utils/render_config.py b/fle/env/factorio/tools/admin/render/utils/render_config.py similarity index 100% rename from fle/env/tools/admin/render/utils/render_config.py rename to fle/env/factorio/tools/admin/render/utils/render_config.py diff --git a/fle/env/tools/admin/render/utils/shape_renderer.py b/fle/env/factorio/tools/admin/render/utils/shape_renderer.py similarity index 99% rename from fle/env/tools/admin/render/utils/shape_renderer.py rename to fle/env/factorio/tools/admin/render/utils/shape_renderer.py index 068900a01..5ec2302b8 100644 --- a/fle/env/tools/admin/render/utils/shape_renderer.py +++ b/fle/env/factorio/tools/admin/render/utils/shape_renderer.py @@ -3,7 +3,7 @@ from PIL import ImageDraw from fle.env.entities import Direction, EntityStatus -from fle.env.tools.admin.render.utils.render_config import RenderConfig +from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig class ShapeRenderer: diff --git a/fle/env/tools/admin/render/utils/tile_renderer.py b/fle/env/factorio/tools/admin/render/utils/tile_renderer.py similarity index 99% rename from fle/env/tools/admin/render/utils/tile_renderer.py rename to fle/env/factorio/tools/admin/render/utils/tile_renderer.py index ba7ae2d15..5478ba6ac 100644 --- a/fle/env/tools/admin/render/utils/tile_renderer.py +++ b/fle/env/factorio/tools/admin/render/utils/tile_renderer.py @@ -2,7 +2,7 @@ from PIL import ImageDraw import math -from fle.env.tools.admin.render.utils.render_config import RenderConfig +from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig class TileRenderer: diff --git a/fle/env/tools/admin/render_message/client.py b/fle/env/factorio/tools/admin/render_message/client.py similarity index 95% rename from fle/env/tools/admin/render_message/client.py rename to fle/env/factorio/tools/admin/render_message/client.py index 27f2b3c4f..6aeb02f5a 100644 --- a/fle/env/tools/admin/render_message/client.py +++ b/fle/env/factorio/tools/admin/render_message/client.py @@ -1,4 +1,4 @@ -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class RenderMessage(Tool): diff --git a/fle/env/factorio/tools/admin/render_message/server.lua b/fle/env/factorio/tools/admin/render_message/server.lua new file mode 100644 index 000000000..2a1df3622 --- /dev/null +++ b/fle/env/factorio/tools/admin/render_message/server.lua @@ -0,0 +1,18 @@ +global.actions.render_message = function(player_index, message) + -- Get color based on player index + local color = {r = 1, g = 1, b = 1} -- Default white + if player_index == 1 then + color = {r = 0.6, g = 1, b = 0.6} -- Light green + message = "Agent 1: " .. message + elseif player_index == 2 then + color = {r = 0.6, g = 0.6, b = 1} -- Light blue + message = "Agent 2: " .. message + elseif player_index == 3 then + color = {r = 1, g = 0.6, b = 0.6} -- Light red + message = "Agent 3: " .. message + end + + -- Print message with color + game.print(message, color) + return true +end diff --git a/fle/env/tools/admin/request_path/client.py b/fle/env/factorio/tools/admin/request_path/client.py similarity index 97% rename from fle/env/tools/admin/request_path/client.py rename to fle/env/factorio/tools/admin/request_path/client.py index 526692d0f..ca53d3601 100644 --- a/fle/env/tools/admin/request_path/client.py +++ b/fle/env/factorio/tools/admin/request_path/client.py @@ -1,5 +1,5 @@ from fle.env.entities import Position -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class RequestPath(Tool): diff --git a/fle/env/factorio/tools/admin/request_path/server.lua b/fle/env/factorio/tools/admin/request_path/server.lua new file mode 100644 index 000000000..072fc3805 --- /dev/null +++ b/fle/env/factorio/tools/admin/request_path/server.lua @@ -0,0 +1,118 @@ +-- Store created entities globally +if not global.clearance_entities then + global.clearance_entities = {} +end + +global.actions.request_path = function(player_index, start_x, start_y, goal_x, goal_y, radius, allow_paths_through_own_entities, entity_size) + local player = global.agent_characters[player_index] + if not player then return nil end + local size = entity_size/2 - 0.01 + + local surface = player.surface + local force = player.force + + -- Define region for pipe checking (add some margin around start/goal) + local region = { + left_top = { + x = math.min(start_x, goal_x) - 10, + y = math.min(start_y, goal_y) - 10 + }, + right_bottom = { + x = math.max(start_x, goal_x) + 10, + y = math.max(start_y, goal_y) + 10 + } + } + rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0.5, g = 0, b = 0.5}, surface = player.surface, radius = 0.303, filled = false, target = {x=start_x, y=start_y}, time_to_live = 12000} + rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 0.5, b = 0.5}, surface = player.surface, radius = 0.303, filled = false, target = {x=goal_x, y=goal_y }, time_to_live = 12000} + + -- Add temporary collision entities + local clearance_entities = {} + global.utils.avoid_entity(player_index, "iron-chest", {y = goal_y, x = goal_x}) + + local goal_position = player.surface.find_non_colliding_position( + "iron-chest", + {y = goal_y, x = goal_x}, + 10, + 0.5, + true + ) + + local start_position = {y = start_y, x = start_x} + + local path_request = { + bounding_box = {{-size, -size}, {size, size}}, + collision_mask = { + "player-layer", + "train-layer", + "consider-tile-transitions", + "water-tile", + "object-layer", + "transport-belt-layer", + "water-tile" + }, + start = start_position, + goal = goal_position, + force = force, + radius = radius or 0, + entity_to_ignore = player, + can_open_gates = true, + path_resolution_modifier = resolution, + pathfind_flags = { + cache = false, + no_break = true, + prefer_straight_paths = true, + allow_paths_through_own_entities = allow_paths_through_own_entities + } + } + + local request_id = surface.request_path(path_request) + + global.clearance_entities[request_id] = clearance_entities + + if not global.path_requests then + global.path_requests = {} + end + if not global.paths then + global.paths = {} + end + + global.path_requests[request_id] = player_index + + return request_id +end + +-- Modify the pathfinding finished handler to clean up entities +--script.on_event(defines.events.on_script_path_request_finished, function(event) +-- -- Clean up clearance entities +-- if global.clearance_entities[event.id] then +-- for _, entity in pairs(global.clearance_entities[event.id]) do +-- if entity.valid then +-- entity.destroy() +-- end +-- end +-- global.clearance_entities[event.id] = nil +-- end +--end) + +script.on_event(defines.events.on_script_path_request_finished, function(event) + local request_data = global.path_requests[event.id] + if not request_data then + return + end + + local player = global.agent_characters[request_data] + if not player then + return + end + + if event.path then + global.paths[event.id] = event.path + log("Path found for request ID: " .. event.id) + elseif event.try_again_later then + global.paths[event.id] = "busy" + log("Pathfinder busy for request ID: " .. event.id) + else + global.paths[event.id] = "not_found" + log("Path not found for request ID: " .. event.id) + end +end) \ No newline at end of file diff --git a/fle/env/tools/admin/save_blueprint/client.py b/fle/env/factorio/tools/admin/save_blueprint/client.py similarity index 93% rename from fle/env/tools/admin/save_blueprint/client.py rename to fle/env/factorio/tools/admin/save_blueprint/client.py index eab20583a..a435dec12 100644 --- a/fle/env/tools/admin/save_blueprint/client.py +++ b/fle/env/factorio/tools/admin/save_blueprint/client.py @@ -1,7 +1,7 @@ from typing import Tuple from fle.env.entities import Position -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class SaveBlueprint(Tool): diff --git a/fle/env/factorio/tools/admin/save_blueprint/server.lua b/fle/env/factorio/tools/admin/save_blueprint/server.lua new file mode 100644 index 000000000..09be818ec --- /dev/null +++ b/fle/env/factorio/tools/admin/save_blueprint/server.lua @@ -0,0 +1,61 @@ +global.actions.save_blueprint = function(player_index) + local player = global.agent_characters[player_index] + local force = player.force + if not force then + return nil, "Force not found" + end + + local surface = game.surfaces[1] -- Get primary surface + local entities = surface.find_entities_filtered{force = force} + + if #entities == 0 then + return nil, "No entities found for force" + end + + -- Calculate bounding box containing all force entities + local min_x, min_y = math.huge, math.huge + local max_x, max_y = -math.huge, -math.huge + + for _, entity in pairs(entities) do + local pos = entity.position + min_x = math.min(min_x, pos.x) + min_y = math.min(min_y, pos.y) + max_x = math.max(max_x, pos.x) + max_y = math.max(max_y, pos.y) + end + + -- Calculate center point of the blueprint + local center_x = (min_x + max_x) / 2 + local center_y = (min_y + max_y) / 2 + + -- Add some padding to the box + local area = { + left_top = {x = min_x - 1, y = min_y - 1}, + right_bottom = {x = max_x + 1, y = max_y + 1} + } + + -- Create blueprint item stack + local bp = player.cursor_stack + bp.set_stack({name = "blueprint"}) + bp.label = "Saved Entities" + bp.allow_manual_label_change = true + + -- Create blueprint from area + bp.create_blueprint{ + surface = surface, + force = force, + area = area, + include_entities = true, + include_modules = true, + include_station_names = true, + include_trains = true, + include_fuel = true + } + + local stack_string = bp.export_stack() + + -- Clear cursor and delete blueprint + bp.clear() + + return dump({blueprint='\"'..stack_string..'\"', center_x=center_x, center_y=center_y}) +end \ No newline at end of file diff --git a/fle/env/tools/admin/save_entity_state/client.py b/fle/env/factorio/tools/admin/save_entity_state/client.py similarity index 97% rename from fle/env/tools/admin/save_entity_state/client.py rename to fle/env/factorio/tools/admin/save_entity_state/client.py index 9016f4c25..405e8f716 100644 --- a/fle/env/tools/admin/save_entity_state/client.py +++ b/fle/env/factorio/tools/admin/save_entity_state/client.py @@ -3,7 +3,7 @@ import zlib from typing import Dict, List, Union -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class SaveEntityState(Tool): diff --git a/fle/env/factorio/tools/admin/save_entity_state/server.lua b/fle/env/factorio/tools/admin/save_entity_state/server.lua new file mode 100644 index 000000000..a3c7b9c95 --- /dev/null +++ b/fle/env/factorio/tools/admin/save_entity_state/server.lua @@ -0,0 +1,383 @@ +-- Helper to ensure all numbers are serializable +local function serialize_number(num) + if num == math.huge then + return "inf" + elseif num == -math.huge then + return "-inf" + else + return tostring(num) + end +end + +-- Helper to serialize position to fixed format +local function serialize_position(pos) + return { + x = serialize_number(pos.x), + y = serialize_number(pos.y) + } +end + +local function serialize_recipe_info(recipe) + if not recipe then return nil end + + local ingredients = {} + for _, ingredient in pairs(recipe.ingredients) do + table.insert(ingredients, { + name = '"' .. ingredient.name .. '"', + amount = serialize_number(ingredient.amount), + type = '"' .. ingredient.type .. '"' + }) + end + + local products = {} + for _, product in pairs(recipe.products) do + table.insert(products, { + name = '"' .. product.name .. '"', + amount = serialize_number(product.amount), + type = '"' .. product.type .. '"', + probability = product.probability and serialize_number(product.probability) or "1" + }) + end + + return { + name = '"' .. recipe.name .. '"', + category = '"' .. recipe.category .. '"', + enabled = recipe.enabled, + energy = serialize_number(recipe.energy), + ingredients = ingredients, + products = products + } +end + +-- Main serialization function +global.actions.save_entity_state = function(player_index, distance, player_entities, resource_entities, items_on_ground) + local surface = global.agent_characters[player_index].surface + if player_entities then + entities = surface.find_entities_filtered({area={{-distance, -distance}, {distance, distance}}, force=global.agent_characters[player_index].force}) + else + if resource_entities then + entities = surface.find_entities({{-distance, -distance}, {distance, distance}}) + else + entities = surface.find_entities_filtered({area={{-distance, -distance}, {distance, distance}}, force=global.agent_characters[player_index].force}) + end + end + + -- Always get items on ground if requested + if items_on_ground then + local ground_items = surface.find_entities_filtered({ + area = {{-distance, -distance}, {distance, distance}}, + name = "item-on-ground" + }) + -- Merge the arrays + for _, item in pairs(ground_items) do + table.insert(entities, item) + end + end + + local entity_states = {} + local entity_array = {} + for _, entity in pairs(entities) do + + -- Serialize inventories by type + local inventory_defines = { + chest = defines.inventory.chest, + furnace_source = defines.inventory.furnace_source, + furnace_result = defines.inventory.furnace_result, + fuel = defines.inventory.fuel, + burnt_result = defines.inventory.burnt_result, + assembling_machine_input = defines.inventory.assembling_machine_input, + assembling_machine_output = defines.inventory.assembling_machine_output, + turret_ammo = defines.inventory.turret_ammo, + lab_input = defines.inventory.lab_input, + lab_modules = defines.inventory.lab_modules, + assembling_machine_modules = defines.inventory.assembling_machine_modules + } + + if entity.name == "item-on-ground" then + -- Capture the item details + local item = entity.stack + if item and item.valid then + local state = { + name = '\"item-on-ground\"', + type = '"' .. item.name .. '"', + count = serialize_number(item.count), + position = serialize_position(entity.position), + } + table.insert(entity_array, state) + end + elseif entity.name ~= "character" then + local state = { + name = '"' .. entity.name .. '"', + position = serialize_position(entity.position), + direction = entity.direction, + entity_number = entity.unit_number or -1, + type = '"' .. entity.type .. '"', + health = serialize_number(entity.health), + energy = serialize_number(entity.energy or 0), + active = entity.active, + status = '"' .. (global.entity_status_names[entity.status] or "normal") .. '"', + warnings = {}, + inventories = {} + } + + -- Add any warnings + for _, warning in pairs(get_issues(entity) or {}) do + table.insert(state.warnings, '"' .. warning .. '"') + end + + -- Handle dimensions + local prototype = game.entity_prototypes[entity.name] + if prototype then + local collision_box = prototype.collision_box + state.dimensions = { + width = serialize_number(math.abs(collision_box.right_bottom.x - collision_box.left_top.x)), + height = serialize_number(math.abs(collision_box.right_bottom.y - collision_box.left_top.y)) + } + state.tile_dimensions = { + tile_width = serialize_number(prototype.tile_width), + tile_height = serialize_number(prototype.tile_height) + } + end + + for name, define in pairs(inventory_defines) do + local inventory = entity.get_inventory(define) + if inventory then + state.inventories[name] = {} + -- Get contents with proper item names + local contents = inventory.get_contents() + for item_name, count in pairs(contents) do + if item_name and item_name ~= "" then -- Ensure valid item name + state.inventories[name][tostring(item_name)] = serialize_number(count) + end + end + end + end + + -- Handle fluids + if entity.fluidbox then + state.fluid_box = {} + for i = 1, #entity.fluidbox do + local fluid = entity.fluidbox[i] + if fluid then + table.insert(state.fluid_box, { + name = '"' .. fluid.name .. '"', + amount = serialize_number(fluid.amount), + temperature = serialize_number(fluid.temperature) + }) + end + end + end + + -- Handle burner state + if entity.burner then + state.burner = { + currently_burning = entity.burner.currently_burning and + '"' .. entity.burner.currently_burning.name .. '"' or nil, + remaining_burning_fuel = serialize_number(entity.burner.remaining_burning_fuel or 0), + heat = serialize_number(entity.burner.heat or 0) + } + + -- Add burner inventory with proper item names + local burner_inventory = entity.burner.inventory + if burner_inventory then + state.burner.inventory = {} + local contents = burner_inventory.get_contents() + --game.print("get_contents() results:") + for item_name, count in pairs(contents) do + if item_name and item_name ~= "" then -- Ensure valid item name + --game.print("Item: '" .. tostring(item_name) .. "' Count: " .. tostring(count)) + --state.burner.inventory['\"' .. tostring(item_name) .. '\"'] = serialize_number(count) + state.burner.inventory[tostring(item_name)] = serialize_number(count) + end + end + end + end + + -- Handle recipe - only for crafting machines and furnaces + if (entity.type == "assembling-machine" or + entity.type == "furnace" or + entity.type == "rocket-silo") and + entity.get_recipe then + local recipe = entity.get_recipe() + if recipe then + state.recipe = serialize_recipe_info(recipe) + end + end + + -- Handle specific entity types + --if entity.type == "transport-belt" then + -- state.input_position = serialize_position(entity.position) + -- state.output_position = serialize_position(entity.position) + -- -- Add belt contents + -- state.inventory = {} + -- for name, count in pairs(entity.get_transport_line(1).get_contents()) do + -- state.inventory[tostring(name)] = serialize_number(count) + -- end + if entity.type == "transport-belt" then + --state.input_position = serialize_position(entity.input_position) + --state.output_position = serialize_position(entity.output_position) + state.position = serialize_position(entity.position) + + -- Store contents of each transport line separately + state.transport_lines = {} + + -- Front line (line 1) + state.transport_lines[1] = {} + local contents1 = entity.get_transport_line(1).get_contents() + for name, count in pairs(contents1) do + state.transport_lines[1][tostring(name)] = serialize_number(count) + end + + -- Back line (line 2) + state.transport_lines[2] = {} + local contents2 = entity.get_transport_line(2).get_contents() + for name, count in pairs(contents2) do + state.transport_lines[2][tostring(name)] = serialize_number(count) + end + + -- Total counts (for backwards compatibility) + state.inventory = {} + for name, count in pairs(contents1) do + state.inventory[tostring(name)] = serialize_number(count) + end + for name, count in pairs(contents2) do + local existing = state.inventory[tostring(name)] + if existing then + state.inventory[tostring(name)] = serialize_number(tonumber(existing) + count) + else + state.inventory[tostring(name)] = serialize_number(count) + end + end + + elseif entity.type == "inserter" then + state.pickup_position = serialize_position(entity.pickup_position) + state.drop_position = serialize_position(entity.drop_position) + + elseif entity.type == "splitter" then + -- Calculate splitter positions based on orientation + local x, y = entity.position.x, entity.position.y + state.input_positions = {} + state.output_positions = {} + local lateral_offset = 0.5 + + if entity.direction == defines.direction.north then + state.input_positions = { + serialize_position({x = x - lateral_offset, y = y + 1}), + serialize_position({x = x + lateral_offset, y = y + 1}) + } + state.output_positions = { + serialize_position({x = x - lateral_offset, y = y - 1}), + serialize_position({x = x + lateral_offset, y = y - 1}) + } + elseif entity.direction == defines.direction.south then + state.input_positions = { + serialize_position({x = x + lateral_offset, y = y - 1}), + serialize_position({x = x - lateral_offset, y = y - 1}) + } + state.output_positions = { + serialize_position({x = x + lateral_offset, y = y + 1}), + serialize_position({x = x - lateral_offset, y = y + 1}) + } + elseif entity.direction == defines.direction.east then + state.input_positions = { + serialize_position({x = x - 1, y = y - lateral_offset}), + serialize_position({x = x - 1, y = y + lateral_offset}) + } + state.output_positions = { + serialize_position({x = x + 1, y = y - lateral_offset}), + serialize_position({x = x + 1, y = y + lateral_offset}) + } + elseif entity.direction == defines.direction.west then + state.input_positions = { + serialize_position({x = x + 1, y = y + lateral_offset}), + serialize_position({x = x + 1, y = y - lateral_offset}) + } + state.output_positions = { + serialize_position({x = x - 1, y = y + lateral_offset}), + serialize_position({x = x - 1, y = y - lateral_offset}) + } + end + + -- Serialize splitter inventories + state.inventory = {} + for i = 1, 2 do + state.inventory[i] = {} + for name, count in pairs(entity.get_transport_line(i).get_contents()) do + state.inventory[i][tostring(name)] = serialize_number(count) + end + end + + elseif entity.type == "mining-drill" then + state.drop_position = serialize_position(entity.drop_position) + + elseif entity.type == "boiler" then + -- Add connection points + local x, y = entity.position.x, entity.position.y + if entity.direction == defines.direction.north then + state.connection_points = { + serialize_position({x = x - 2, y = y + 0.5}), + serialize_position({x = x + 2, y = y + 0.5}) + } + state.steam_output_point = serialize_position({x = x, y = y - 2}) + elseif entity.direction == defines.direction.south then + state.connection_points = { + serialize_position({x = x - 2, y = y - 0.5}), + serialize_position({x = x + 2, y = y - 0.5}) + } + state.steam_output_point = serialize_position({x = x, y = y + 2}) + elseif entity.direction == defines.direction.east then + state.connection_points = { + serialize_position({x = x - 0.5, y = y - 2}), + serialize_position({x = x - 0.5, y = y + 2}) + } + state.steam_output_point = serialize_position({x = x + 2, y = y}) + elseif entity.direction == defines.direction.west then + state.connection_points = { + serialize_position({x = x + 0.5, y = y - 2}), + serialize_position({x = x + 0.5, y = y + 2}) + } + state.steam_output_point = serialize_position({x = x - 2, y = y}) + end + end + + table.insert(entity_array, state) + else + -- Find the index of this character in global.agent_characters + agent_index = -1 + for idx, agent in pairs(global.agent_characters) do + if agent == entity then + agent_index = idx + break + end + end + local state = { + name = '"' .. entity.name .. '"', + position = serialize_position(entity.position), + direction = entity.direction, + entity_number = entity.unit_number or -1, + inventories = {}, + agent_index = agent_index, + color = { + r = serialize_number(entity.color.r), + g = serialize_number(entity.color.g), + b = serialize_number(entity.color.b), + a = serialize_number(entity.color.a) + } + } + -- Get the character's inventory using defines + local inventory = entity.get_inventory(defines.inventory.character_main) + + if inventory then + state.inventory = {} + local contents = inventory.get_contents() + for item_name, count in pairs(contents) do + if item_name and item_name ~= "" then + state.inventory[tostring(item_name)] = serialize_number(count) + end + end + end + table.insert(entity_array, state) + end + end + return entity_array +end diff --git a/fle/env/tools/admin/save_research_state/client.py b/fle/env/factorio/tools/admin/save_research_state/client.py similarity index 98% rename from fle/env/tools/admin/save_research_state/client.py rename to fle/env/factorio/tools/admin/save_research_state/client.py index c88d68da8..83b311244 100644 --- a/fle/env/tools/admin/save_research_state/client.py +++ b/fle/env/factorio/tools/admin/save_research_state/client.py @@ -1,6 +1,6 @@ from fle.commons.models.research_state import ResearchState from fle.commons.models.technology_state import TechnologyState -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class SaveResearchState(Tool): diff --git a/fle/env/factorio/tools/admin/save_research_state/server.lua b/fle/env/factorio/tools/admin/save_research_state/server.lua new file mode 100644 index 000000000..728708532 --- /dev/null +++ b/fle/env/factorio/tools/admin/save_research_state/server.lua @@ -0,0 +1,62 @@ +global.actions.save_research_state = function(player_index) + local player = global.agent_characters[player_index] + local force = player.force + + -- Helper to serialize technology state + local function serialize_technology(tech) + local prerequisites = {} + for name, _ in pairs(tech.prerequisites) do + table.insert(prerequisites, "\""..name.."\"") + end + + local ingredients = {} + for _, ingredient in pairs(tech.research_unit_ingredients) do + table.insert(ingredients, { + name = "\""..ingredient.name.."\"", + amount = ingredient.amount + }) + end + + return { + name = "\""..tech.name.."\"", + researched = tech.researched, + enabled = tech.enabled, + -- visible = tech.visible, + level = tech.level, + research_unit_count = tech.research_unit_count, + research_unit_energy = tech.research_unit_energy, + prerequisites = prerequisites, + ingredients = ingredients, + --saved_progress = saved_progress -- This will be nil if no progress is saved + } + end + + local research_state = { + technologies = {}, + current_research = nil, + research_progress = 0, + research_queue = {}, + progress = {} + } + + -- Save all technology states + for name, tech in pairs(force.technologies) do + research_state.technologies[name] = serialize_technology(tech) + end + + -- Save current research and progress + if force.current_research then + research_state.current_research = "\""..force.current_research.name.."\"" + research_state.research_progress = force.research_progress + + research_state.progress[force.current_research.name] = force.research_progress or 0 + end + + -- Save research queue if it exists + if force.research_queue then + for _, tech in pairs(force.research_queue) do + table.insert(research_state.research_queue, "\""..tech.name.."\"") + end + end + return research_state +end \ No newline at end of file diff --git a/fle/env/tools/agent.md b/fle/env/factorio/tools/agent.md similarity index 100% rename from fle/env/tools/agent.md rename to fle/env/factorio/tools/agent.md diff --git a/fle/env/tools/agent/can_place_entity/agent.md b/fle/env/factorio/tools/agent/can_place_entity/agent.md similarity index 100% rename from fle/env/tools/agent/can_place_entity/agent.md rename to fle/env/factorio/tools/agent/can_place_entity/agent.md diff --git a/fle/env/tools/agent/can_place_entity/client.py b/fle/env/factorio/tools/agent/can_place_entity/client.py similarity index 97% rename from fle/env/tools/agent/can_place_entity/client.py rename to fle/env/factorio/tools/agent/can_place_entity/client.py index fe73dcf34..1dee042a7 100644 --- a/fle/env/tools/agent/can_place_entity/client.py +++ b/fle/env/factorio/tools/agent/can_place_entity/client.py @@ -1,6 +1,6 @@ from fle.env import entities as ent from fle.env.game_types import Prototype -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class CanPlaceEntity(Tool): diff --git a/fle/env/tools/agent/can_place_entity/server.lua b/fle/env/factorio/tools/agent/can_place_entity/server.lua similarity index 100% rename from fle/env/tools/agent/can_place_entity/server.lua rename to fle/env/factorio/tools/agent/can_place_entity/server.lua diff --git a/fle/env/tools/agent/connect_entities/agent.md b/fle/env/factorio/tools/agent/connect_entities/agent.md similarity index 100% rename from fle/env/tools/agent/connect_entities/agent.md rename to fle/env/factorio/tools/agent/connect_entities/agent.md diff --git a/fle/env/tools/agent/connect_entities/client.py b/fle/env/factorio/tools/agent/connect_entities/client.py similarity index 97% rename from fle/env/tools/agent/connect_entities/client.py rename to fle/env/factorio/tools/agent/connect_entities/client.py index 7401f191a..9e373302a 100644 --- a/fle/env/tools/agent/connect_entities/client.py +++ b/fle/env/factorio/tools/agent/connect_entities/client.py @@ -21,31 +21,31 @@ ) from fle.env import DirectionInternal from fle.env.game_types import Prototype -from fle.env.tools.admin.clear_collision_boxes.client import ClearCollisionBoxes -from fle.env.tools.admin.extend_collision_boxes.client import ExtendCollisionBoxes -from fle.env.tools.admin.get_path.client import GetPath -from fle.env.tools.admin.request_path.client import RequestPath -from fle.env.tools.agent.connect_entities.path_result import PathResult -from fle.env.tools.agent.connect_entities.resolver import ConnectionType, Resolver -from fle.env.tools.agent.connect_entities.resolvers.fluid_connection_resolver import ( +from fle.env.factorio.tools.admin.clear_collision_boxes.client import ClearCollisionBoxes +from fle.env.factorio.tools.admin.extend_collision_boxes.client import ExtendCollisionBoxes +from fle.env.factorio.tools.admin.get_path.client import GetPath +from fle.env.factorio.tools.admin.request_path.client import RequestPath +from fle.env.factorio.tools.agent.connect_entities.path_result import PathResult +from fle.env.factorio.tools.agent.connect_entities.resolver import ConnectionType, Resolver +from fle.env.factorio.tools.agent.connect_entities.resolvers.fluid_connection_resolver import ( FluidConnectionResolver, ) -from fle.env.tools.agent.connect_entities.resolvers.power_connection_resolver import ( +from fle.env.factorio.tools.agent.connect_entities.resolvers.power_connection_resolver import ( PowerConnectionResolver, ) -from fle.env.tools.agent.connect_entities.resolvers.transport_connection_resolver import ( +from fle.env.factorio.tools.agent.connect_entities.resolvers.transport_connection_resolver import ( TransportConnectionResolver, ) -from fle.env.tools.agent.connect_entities.groupable_entities import ( +from fle.env.factorio.tools.agent.connect_entities.groupable_entities import ( _deduplicate_entities, agglomerate_groupable_entities, ) -from fle.env.tools.agent.get_entities.client import GetEntities -from fle.env.tools.agent.get_entity.client import GetEntity -from fle.env.tools.agent.inspect_inventory.client import InspectInventory -from fle.env.tools.agent.pickup_entity.client import PickupEntity -from fle.env.tools.agent.rotate_entity.client import RotateEntity -from fle.env.tools import Tool +from fle.env.factorio.tools.agent.get_entities.client import GetEntities +from fle.env.factorio.tools.agent.get_entity.client import GetEntity +from fle.env.factorio.tools.agent.inspect_inventory.client import InspectInventory +from fle.env.factorio.tools.agent.pickup_entity.client import PickupEntity +from fle.env.factorio.tools.agent.rotate_entity.client import RotateEntity +from fle.env.factorio.tools import Tool from collections.abc import Set as AbstractSet diff --git a/fle/env/tools/agent/connect_entities/groupable_entities.py b/fle/env/factorio/tools/agent/connect_entities/groupable_entities.py similarity index 100% rename from fle/env/tools/agent/connect_entities/groupable_entities.py rename to fle/env/factorio/tools/agent/connect_entities/groupable_entities.py diff --git a/fle/env/tools/agent/connect_entities/path_result.py b/fle/env/factorio/tools/agent/connect_entities/path_result.py similarity index 100% rename from fle/env/tools/agent/connect_entities/path_result.py rename to fle/env/factorio/tools/agent/connect_entities/path_result.py diff --git a/fle/env/tools/agent/connect_entities/resolver.py b/fle/env/factorio/tools/agent/connect_entities/resolver.py similarity index 100% rename from fle/env/tools/agent/connect_entities/resolver.py rename to fle/env/factorio/tools/agent/connect_entities/resolver.py diff --git a/fle/env/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py b/fle/env/factorio/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py similarity index 99% rename from fle/env/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py rename to fle/env/factorio/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py index c810190fc..1a0e44cee 100644 --- a/fle/env/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py +++ b/fle/env/factorio/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py @@ -14,7 +14,7 @@ PumpJack, ) from fle.env.game_types import Prototype, prototype_by_name -from fle.env.tools.agent.connect_entities.resolver import Resolver +from fle.env.factorio.tools.agent.connect_entities.resolver import Resolver class FluidConnectionResolver(Resolver): diff --git a/fle/env/tools/agent/connect_entities/resolvers/power_connection_resolver.py b/fle/env/factorio/tools/agent/connect_entities/resolvers/power_connection_resolver.py similarity index 98% rename from fle/env/tools/agent/connect_entities/resolvers/power_connection_resolver.py rename to fle/env/factorio/tools/agent/connect_entities/resolvers/power_connection_resolver.py index f19ea47eb..8a2b4664b 100644 --- a/fle/env/tools/agent/connect_entities/resolvers/power_connection_resolver.py +++ b/fle/env/factorio/tools/agent/connect_entities/resolvers/power_connection_resolver.py @@ -1,6 +1,6 @@ from typing import Union, Tuple, List, Set from fle.env.entities import Position, Entity, ElectricityGroup -from fle.env.tools.agent.connect_entities.resolver import Resolver +from fle.env.factorio.tools.agent.connect_entities.resolver import Resolver class PowerConnectionResolver(Resolver): diff --git a/fle/env/tools/agent/connect_entities/resolvers/transport_connection_resolver.py b/fle/env/factorio/tools/agent/connect_entities/resolvers/transport_connection_resolver.py similarity index 98% rename from fle/env/tools/agent/connect_entities/resolvers/transport_connection_resolver.py rename to fle/env/factorio/tools/agent/connect_entities/resolvers/transport_connection_resolver.py index b92a2fa04..c376ec7bf 100644 --- a/fle/env/tools/agent/connect_entities/resolvers/transport_connection_resolver.py +++ b/fle/env/factorio/tools/agent/connect_entities/resolvers/transport_connection_resolver.py @@ -18,7 +18,7 @@ Boiler, Generator, ) -from fle.env.tools.agent.connect_entities.resolver import Resolver +from fle.env.factorio.tools.agent.connect_entities.resolver import Resolver class TransportConnectionResolver(Resolver): diff --git a/fle/env/factorio/tools/agent/connect_entities/server.lua b/fle/env/factorio/tools/agent/connect_entities/server.lua new file mode 100644 index 000000000..cac4a5468 --- /dev/null +++ b/fle/env/factorio/tools/agent/connect_entities/server.lua @@ -0,0 +1,1307 @@ +-- connect_entities + +local MAX_SERIALIZATION_ITERATIONS = 1000 -- Maximum iterations for serializing belt groups +local MAX_PLACEMENT_ATTEMPTS = 50 -- Maximum attempts to find placeable positions +local MAX_PATH_LENGTH = 1000 -- Maximum number of path points to process +local MAX_STRAIGHT_SECTIONS = 100 +local MAX_UNDERGROUND_SEGMENTS = 50 + +-- Wire reach values for different electric pole types +local wire_reach = { + ['small-electric-pole'] = 4, + ['medium-electric-pole'] = 9, + ['big-electric-pole'] = 30, + ['substation'] = 18 +} + +local default_connect_types = { + ['pipe-to-ground'] = 'pipe', + ['underground-belt'] = 'transport-belt', + ['express-underground-belt'] = 'express-transport-belt', + ['fast-underground-belt'] = 'fast-underground-belt', +} + +local underground_ranges = { + ['pipe-to-ground'] = 8, + ['underground-belt'] = 4, + ['fast-underground-belt'] = 4, + ['express-underground-belt'] = 4 +} + +local function is_within_pole_range(position, pole) + local dx = position.x - pole.position.x + local dy = position.y - pole.position.y + local wire_reach = wire_reach[pole.name] or 4 + return (dx * dx + dy * dy) <= wire_reach * wire_reach +end + +local function get_electric_network_at_position(position) + -- Get all electric poles near the position + local nearby_poles = game.surfaces[1].find_entities_filtered{ + position = position, + radius = 9, -- Maximum pole reach is 9 for medium poles + type = "electric-pole" + } + + -- Check if any pole's wire reaches this position + for _, pole in pairs(nearby_poles) do + if is_within_pole_range(position, pole) then + return pole.electric_network_id + end + end + return nil +end + +local function are_positions_in_same_network(pos1, pos2) + local network1 = get_electric_network_at_position(pos1) + local network2 = get_electric_network_at_position(pos2) + return network1 and network2 and network1 == network2 +end + +local function is_position_saturated(position, reach) + -- Get nearby poles + local nearby_poles = game.surfaces[1].find_entities_filtered{ + position = position, + radius = 9, + type = "electric-pole" + } + + -- Check each corner of a 1x1 tile centered on the position + local corners = { + {x = position.x - reach/2, y = position.y - reach/2}, + {x = position.x - reach/2, y = position.y + reach/2}, + {x = position.x + reach/2, y = position.y + reach/2}, + {x = position.x + reach/2, y = position.y - reach/2} + } + + -- For each corner, check if it's within range of any existing pole + for _, corner in pairs(corners) do + local corner_covered = false + for _, pole in pairs(nearby_poles) do + if is_within_pole_range(corner, pole) then + corner_covered = true + break + end + end + if not corner_covered then + return false -- Found an uncovered corner + end + end + + return true -- All corners are covered +end + + +function get_step_size(connection_type) + return wire_reach[connection_type] or 1 +end + +function math.round(x) + return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) +end + +local function has_valid_fluidbox(entity) + return entity.fluidbox and #entity.fluidbox > 0 and entity.fluidbox[1] and entity.fluidbox[1].get_fluid_system_id +end + +local function are_fluidboxes_connected(entity1, entity2) + if has_valid_fluidbox(entity1) and has_valid_fluidbox(entity2) then + return entity1.fluidbox[1].get_fluid_system_id() == entity2.fluidbox[1].get_fluid_system_id() + end + return false +end + +-- Calculate maximum possible underground sections based on inventory +local function calculate_max_underground_sections(player, underground_type) + local available_count = 0 + for _, inv in pairs({defines.inventory.character_main}) do + if player.get_inventory(inv) then + available_count = available_count + player.get_inventory(inv).get_item_count(underground_type) + end + end + -- Each underground section requires 2 entities (entrance and exit) + return math.floor(available_count / 2) +end + +-- Helper function to check if an entity type is an underground variant +local function is_underground_type(entity_type) + return underground_ranges[entity_type] ~= nil +end + +-- Modified split_section_into_underground_segments to respect inventory limits +local function split_section_into_underground_segments(section, path, range, max_segments) + local segments = {} + + -- Add margin of 1 at start and end of section + local effective_start = section.start_index + 1 + local effective_end = section.end_index - 1 + + -- Check if section is still long enough after adding margins + if effective_end - effective_start < 2 then + return segments -- Return empty segments if section is too short + end + + local current_start = effective_start + local segment_count = 0 + local iteration_count = 0 + local MAX_ITERATIONS = math.min(section.length * 2, MAX_UNDERGROUND_SEGMENTS) -- Prevent excessive iterations + + + while current_start + 1 < effective_end and segment_count < max_segments do + iteration_count = iteration_count + 1 + if iteration_count > MAX_ITERATIONS then + -- game.print("Warning: Maximum iterations reached while splitting underground segments") + break + end + -- Calculate end index for this segment + local end_index = math.min(current_start + range, effective_end) + + -- Only create segment if there's at least 1 tile gap between entrance and exit + if end_index > current_start then + table.insert(segments, { + entrance_index = current_start, + exit_index = end_index, + direction = section.direction + }) + + segment_count = segment_count + 1 + + -- Move to position after the exit for next iteration + current_start = end_index + 1 + else + -- If we can't create a valid segment, move forward + current_start = current_start + 1 + end + end + + + return segments +end + +-- Find straight sections in path suitable for underground entities +local function find_straight_sections(path, min_length) + local sections = {} + local current_section = { + start_index = 1, + direction = nil, + length = 0 + } + + for i = 1, #path - 1 do --Always start slightly into the path, and away from the end to prevent connection issues. + local current_pos = path[i].position + local next_pos = path[i + 1].position + local dx = next_pos.x - current_pos.x + local dy = next_pos.y - current_pos.y + local current_direction = {dx = dx, dy = dy} + + -- Normalize direction + if math.abs(dx) > math.abs(dy) then + current_direction = {dx = dx/math.abs(dx), dy = 0} + else + current_direction = {dx = 0, dy = dy/math.abs(dy)} + end + + if not current_section.direction then + current_section.direction = current_direction + end + + -- Check if we're still going in the same direction + if current_section.direction.dx == current_direction.dx and + current_section.direction.dy == current_direction.dy then + current_section.length = current_section.length + 1 + else + -- Direction changed, check if previous section was long enough + if current_section.length >= min_length then + table.insert(sections, { + start_index = current_section.start_index, + end_index = i, + direction = current_section.direction, + length = current_section.length + }) + end + -- Start new section + current_section = { + start_index = i, + direction = current_direction, + length = 1 + } + end + end + + -- Check final section + if current_section.length >= min_length then + table.insert(sections, { + start_index = current_section.start_index, + end_index = #path, + direction = current_section.direction, + length = current_section.length + }) + end + + return sections +end + +-- Helper function to serialize a belt group +local function serialize_belt_group(entity) + if not entity or not entity.valid or entity.type ~= "transport-belt" then + return nil + end + + local serialized = {} + local seen = {} + local iteration_count = 0 + + local function get_connected_belt_entities(belt, is_output) + local connected_entities = {} + local seen_owners = {} + + -- Get connected lines + local connected_lines = {} + if is_output then + if #belt.belt_neighbours['outputs'] then + for _, line in pairs(belt.belt_neighbours['outputs']) do + table.insert(connected_lines, line) + end + end + else + if #belt.belt_neighbours['inputs'] then + for _, line in pairs(belt.belt_neighbours['inputs']) do + table.insert(connected_lines, line) + end + end + end + + -- game.print("connected lines "..#connected_lines) + -- Convert lines to unique belt entities + for _, line in pairs(connected_lines) do + if line and line.valid and not seen_owners[line.unit_number] then + seen_owners[line.unit_number] = true + table.insert(connected_entities, line) + end + end + -- game.print("connected entities "..#connected_entities) + return connected_entities + end + + local function serialize_connected_belts(belt, is_output) + iteration_count = iteration_count + 1 + if iteration_count > MAX_SERIALIZATION_ITERATIONS then + -- game.print("Warning: Belt serialization reached iteration limit") + return + end + + if not belt or not belt.valid or seen[belt.unit_number] then + return + end + + seen[belt.unit_number] = true + local belt_data = global.utils.serialize_entity(belt) + table.insert(serialized, belt_data) + + -- Get connected belt entities + local next_belts = get_connected_belt_entities(belt, is_output) + for _, connected_belt in pairs(next_belts) do + if connected_belt.valid and not seen[connected_belt.unit_number] then + serialize_connected_belts(connected_belt, is_output) + end + end + end + + -- Start serialization from the given entity + serialize_connected_belts(entity, false) -- Follow input direction + serialize_connected_belts(entity, true) -- Follow output direction + + return serialized +end + + +local function are_poles_connected(entity1, entity2) + if not (entity1 and entity2) then return false end + if not (entity1.electric_network_id and entity2.electric_network_id) then return false end + return entity1.electric_network_id == entity2.electric_network_id +end + +local function is_placeable(position) + local invalid_tiles = { + ["water"] = true, + ["deepwater"] = true, + ["water-green"] = true, + ["deepwater-green"] = true, + ["water-shallow"] = true, + ["water-mud"] = true, + } + + local entities = game.surfaces[1].find_entities_filtered{ + position = position, + collision_mask = "player-layer" + } + if #entities == 1 then + if entities[1].name == "character" then + return not invalid_tiles[game.surfaces[1].get_tile(position.x, position.y).name] + end + end + return #entities == 0 and not invalid_tiles[game.surfaces[1].get_tile(position.x, position.y).name] +end + +local function find_placeable_neighbor(pos, previous_pos) + local directions = { + {dx = 0, dy = -1}, -- up + {dx = 1, dy = 0}, -- right + {dx = 0, dy = 1}, -- down + {dx = -1, dy = 0} -- left + } + + if previous_pos then + local desired_dx = pos.x - previous_pos.x + local desired_dy = pos.y - previous_pos.y + table.sort(directions, function(a, b) + local a_score = math.abs(a.dx - desired_dx) + math.abs(a.dy - desired_dy) + local b_score = math.abs(b.dx - desired_dx) + math.abs(b.dy - desired_dy) + return a_score < b_score + end) + end + + for _, dir in ipairs(directions) do + local test_pos = {x = pos.x + dir.dx, y = pos.y + dir.dy} + if is_placeable(test_pos) then + return test_pos + end + end + return nil +end + + +local function interpolate_manhattan(pos1, pos2) + local interpolated = {} + local dx = pos2.x - pos1.x + local dy = pos2.y - pos1.y + local manhattan_distance = math.abs(dx) + math.abs(dy) + -- game.print("Distance3 "..manhattan_distance) + if manhattan_distance > 2 then + local steps = math.max(math.abs(dx), math.abs(dy)) + local x_step = math.floor((dx / steps)*2)/2 + local y_step = math.floor((dy / steps)*2)/2 + + for i = 1, steps - 1 do + local new_pos_x = {x = pos1.x + math.round(x_step * i), y = pos1.y} + if is_placeable(new_pos_x) then + table.insert(interpolated, {position = new_pos_x}) + else + local neighbor = find_placeable_neighbor(new_pos_x, pos1) + if neighbor then + table.insert(interpolated, {position = neighbor}) + end + end + + local new_pos_y = {x = pos1.x + math.round(x_step * i), y = pos1.y + math.round(y_step * i)} + if is_placeable(new_pos_y) then + table.insert(interpolated, {position = new_pos_y}) + else + local neighbor = find_placeable_neighbor(new_pos_y, pos1) + if neighbor then + table.insert(interpolated, {position = neighbor}) + end + end + end + --elseif math.abs(dx) == 1 and math.abs(dy) == 1 then + -- local mid_pos = {x = pos2.x, y = pos1.y} + -- if is_placeable(mid_pos) then + -- table.insert(interpolated, {position = mid_pos}) + -- else + -- mid_pos = {x = pos1.x, y = pos2.y} + -- if is_placeable(mid_pos) then + -- table.insert(interpolated, {position = mid_pos}) + -- else + -- mid_pos = find_placeable_neighbor(mid_pos, pos1) + -- if mid_pos then + -- table.insert(interpolated, {position = mid_pos}) + -- end + -- end + -- end + --end + elseif math.abs(dx) == 1 and math.abs(dy) == 1 then --and math.abs(dx) <= 1.5 and math.abs(dy) <= 1.5 then + -- Try first horizontal then vertical movement + local mid_pos = {x = pos2.x, y = pos1.y} + if is_placeable(mid_pos) then + table.insert(interpolated, {position = mid_pos}) + else + -- Try vertical then horizontal movement + mid_pos = {x = pos1.x, y = pos2.y} + if is_placeable(mid_pos) then + table.insert(interpolated, {position = mid_pos}) + else + -- If neither orthogonal position works, try to find a neighbor + local neighbor = find_placeable_neighbor(mid_pos, pos1) + if neighbor then + table.insert(interpolated, {position = neighbor}) + end + end + end + + end + + return interpolated +end + + + +local function place_at_position(player, connection_type, current_position, dir, serialized_entities, dry_run, counter_state, is_underground_exit) + + local is_electric_pole = wire_reach[connection_type] ~= nil + local placement_position = current_position + local existing_entity = nil + + for _, entity in pairs(serialized_entities) do + if entity.position.x == placement_position.x and entity.position.y == placement_position.y then + return + end + end + + if is_electric_pole then + if is_position_saturated(current_position, wire_reach[connection_type]) then + + return -- No need to place another pole + end + + placement_position = game.surfaces[1].find_non_colliding_position(connection_type, current_position, 2, 0.1) + if not placement_position then + error("Cannot find suitable position to place " .. connection_type) + end + else + local entities = game.surfaces[1].find_entities_filtered{ + position = current_position, + type = {"beam", "resource", "player"}, + invert=true + + } + for _, entity in pairs(entities) do + if entity.name == connection_type then + existing_entity = entity + break + end + end + end + + if existing_entity then + -- game.print("Existing entity "..existing_entity.name) + -- Get the existing network ID before any modifications + --local existing_network_id = has_valid_fluidbox(existing_entity) and existing_entity.fluidbox[1].get_fluid_system_id() + + -- Update direction if needed + if existing_entity.name ~= connection_type then + if existing_entity.direction ~= dir then + existing_entity.direction = dir + end + end + + -- For pipes, merge networks + if connection_type == 'pipe' then + for _, serialized in ipairs(serialized_entities) do + local entity = game.surfaces[1].find_entity(connection_type, serialized.position) + if entity and has_valid_fluidbox(entity) then + existing_entity.connect_neighbour(entity) + end + end + end + + -- Update or add to serialized list + for i, serialized in ipairs(serialized_entities) do + if serialized.position.x == current_position.x and serialized.position.y == current_position.y then + serialized_entities[i] = global.utils.serialize_entity(existing_entity) + return existing_entity + end + end + table.insert(serialized_entities, global.utils.serialize_entity(existing_entity)) + return existing_entity + + else + -- For underground entities, we need to set the correct type (input/output) + local entity_variant = { + name = connection_type, + position = placement_position, + direction = dir, + force = player.force, + type = is_underground_exit and "output" or "input", + move_stuck_players=true + } + -- We can just teleport away here to avoid collision as we dont adhere by distance rules in connect_entities + player.teleport({placement_position.x+2, placement_position.y+2}) + local can_place + if connection_type == 'pipe' or connection_type == 'pipe-to-ground' or connection_type == 'underground-pipe' then + -- Use permissive surface check for pipe placement to allow tight spaces + can_place = game.surfaces[1].can_place_entity({ + name = connection_type, + position = placement_position, + direction = dir, + force = player.force + }) + else + can_place = global.utils.can_place_entity(player, connection_type, placement_position, dir) + end + + --local can_place = global.utils.avoid_entity(1, connection_type, placement_position, dir) + --if not can_build then + -- error("Cannot place the entity at the specified position: x="..position.x..", y="..position.y) + --end + --local player_position = player.position + -- player.teleport({placement_position.x, placement_position.y}) + --local can_place = global.actions.can_place_entity(1, connection_type, dir, placement_position.x, placement_position.y)--game.surfaces[1].can_place_entity(entity_variant) + --player.teleport(player_position) + + --local can_place = global.utils.avoid_entity(1, connection_type, placement_position, dir) + rendering.draw_circle{only_in_alt_mode=true, width = 0.25, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = placement_position, time_to_live = 12000} + + if dry_run and can_place == false then + -- Define the area where the entity will be placed + local target_area = { + {x = placement_position.x - 1 / 2, y = placement_position.y - 1 / 2}, + {x = placement_position.x + 1 / 2, y = placement_position.y + 1 / 2} + } + + -- Check for collision with other entities + local entities = player.surface.find_entities_filtered{area = target_area, force = player.force} + for _, entity in pairs(entities) do + -- game.print("1 "..entity.name) + end + + error("Cannot connect due to placement blockage 1.") + end + + + -- Place entity + if can_place and not dry_run then + --global.utils.avoid_entity(player.index, connection_type, placement_position, dir) + + local placed_entity = game.surfaces[1].create_entity(entity_variant) + if placed_entity then + player.remove_item({name = connection_type, count = 1}) + counter_state.place_counter = counter_state.place_counter + 1 + table.insert(serialized_entities, global.utils.serialize_entity(placed_entity)) + return placed_entity + end + + elseif can_place and dry_run then + counter_state.place_counter = counter_state.place_counter + 1 + return nil + + else + -- game.print("Avoiding entity at " .. placement_position.x.. ", " .. placement_position.y) + -- global.utils.avoid_entity(player.index, connection_type, placement_position, dir) + + -- error("Cannot place entity") + --local entities = player.surface.find_entities_filtered{position=placement_position, radius=0.5, type = {"beam", "resource", "player"}, invert=true} + --local can_place = #entities == 0 + --if can_place then + -- local tile = player.surface.get_tile(placement_position.x, placement_position.y) + -- if is_water_tile(tile.name) then + -- can_place = false + -- end + --end + -- + --if can_place then + -- local placed_entity = player.surface.create_entity({ + -- name = connection_type, + -- position = placement_position, + -- direction = dir, + -- force = player.force + -- }) + -- + -- if placed_entity then + -- player.remove_item({name = connection_type, count = 1}) + -- counter_state.place_counter = counter_state.place_counter + 1 + -- table.insert(serialized_entities, global.utils.serialize_entity(placed_entity)) + -- return placed_entity + -- end + -- + --end + end + end + + -- Check inventory + local has_item = false + for _, inv in pairs({defines.inventory.character_main}) do + if player.get_inventory(inv) then + local count = player.get_inventory(inv).get_item_count(connection_type) + if count > 0 then + has_item = true + break + end + end + end + if not has_item then + error("You do not have the required item in their inventory.") + end + + -- We can just teleport away here to avoid collision as we dont adhere by distance rules in connect_entities + player.teleport({placement_position.x+2, placement_position.y+2}) + local can_place + if connection_type == 'pipe' or connection_type == 'pipe-to-ground' or connection_type == 'underground-pipe' then + -- Use permissive surface check for pipe placement to allow tight spaces + can_place = game.surfaces[1].can_place_entity({ + name = connection_type, + position = placement_position, + direction = dir, + force = player.force + }) + else + can_place = global.utils.can_place_entity(player, connection_type, placement_position, dir) + end + --local player_position = player.position + --player.teleport({placement_position.x, placement_position.y}) + --local can_place = global.actions.can_place_entity(1, connection_type, dir, placement_position.x, placement_position.y)--game.surfaces[1].can_place_entity(entity_variant) + --player.teleport(player_position) + + --local can_place = global.utils.avoid_entity(1, connection_type, placement_position, dir) + rendering.draw_circle{only_in_alt_mode=true, width = 0.25, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = placement_position, time_to_live = 12000} + + if dry_run and can_place == false then + local target_area = { + {x = placement_position.x - 1 / 2, y = placement_position.y - 1 / 2}, + {x = placement_position.x + 1 / 2, y = placement_position.y + 1 / 2} + } + + -- Check for collision with other entities + local entities = player.surface.find_entities_filtered{area = target_area, force = player.force} + for _, entity in pairs(entities) do + -- game.print("1: "..entity.name) + end + + error("Cannot connect due to placement blockage.") + end + + + -- Place entity + if can_place and not dry_run then + --global.utils.avoid_entity(player.index, connection_type, placement_position, dir) + + local placed_entity = game.surfaces[1].create_entity({ + name = connection_type, + position = placement_position, + direction = dir, + force = player.force, + move_stuck_players=true + }) + + if placed_entity then + player.remove_item({name = connection_type, count = 1}) + counter_state.place_counter = counter_state.place_counter + 1 + table.insert(serialized_entities, global.utils.serialize_entity(placed_entity)) + return placed_entity + end + + elseif can_place and dry_run then + counter_state.place_counter = counter_state.place_counter + 1 + return nil + + else + -- game.print("Avoiding entity at " .. placement_position.x.. ", " .. placement_position.y) + -- global.utils.avoid_entity(player.index, connection_type, placement_position, dir) + + -- error("Cannot place entity") + --local entities = player.surface.find_entities_filtered{position=placement_position, radius=0.5, type = {"beam", "resource", "player"}, invert=true} + --local can_place = #entities == 0 + --if can_place then + -- local tile = player.surface.get_tile(placement_position.x, placement_position.y) + -- if is_water_tile(tile.name) then + -- can_place = false + -- end + --end + -- + --if can_place then + -- local placed_entity = player.surface.create_entity({ + -- name = connection_type, + -- position = placement_position, + -- direction = dir, + -- force = player.force + -- }) + -- + -- if placed_entity then + -- player.remove_item({name = connection_type, count = 1}) + -- counter_state.place_counter = counter_state.place_counter + 1 + -- table.insert(serialized_entities, global.utils.serialize_entity(placed_entity)) + -- return placed_entity + -- end + -- + --end + end +end + +local function table_contains(tbl, element) + for _, value in ipairs(tbl) do + if value == element then + return true + end + end + return false +end + +local function connect_entities(player_index, source_x, source_y, target_x, target_y, path_handle, connection_types, dry_run) + local counter_state = {place_counter = 0} + local player = global.agent_characters[player_index] + local last_placed_entity = nil + + local start_position = {x = math.floor(source_x*2)/2, y = math.floor(source_y*2)/2} + local end_position = {x = target_x, y = target_y} + + + local raw_path = global.paths[path_handle] + -- game.print("Path length "..#raw_path) + -- game.print(serpent.line(start_position).." - "..serpent.line(end_position)) + + if not raw_path or type(raw_path) ~= "table" or #raw_path == 0 then + error("Invalid path: " .. serpent.line(path)) + end + + -- game.print("Normalising", {print_skip=defines.print_skip.never}) + local path = global.utils.normalise_path(raw_path, start_position, end_position) + + -- Get default and underground connection types + local default_connection_type = default_connect_types[connection_types[1]] or connection_types[1] + local underground_type = nil + for _, type in ipairs(connection_types) do + if is_underground_type(type) then + underground_type = type + break + end + end + + --rendering.clear() + + rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 1, g = 0, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = start_position, time_to_live = 60000} + rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = end_position, time_to_live = 60000} + + for i = 1, #path - 1 do + rendering.draw_line{only_in_alt_mode=true, surface = player.surface, from = path[i].position, to = path[i + 1].position, color = {1, 0, 1}, width = 2, dash_length=0.25, gap_length = 0.25} + end + for i = 1, #raw_path - 1 do + rendering.draw_line{only_in_alt_mode=true, surface = player.surface, from = raw_path[i].position, to = raw_path[i + 1].position, color = {1, 1, 0}, width = 0, dash_length=0.2, gap_length = 0.2} + end + + + local last_position = start_position + local step_size = wire_reach[default_connection_type] or 1 + local is_electric_pole = wire_reach[default_connection_type] ~= nil + + + for i = 1, #path-1, step_size do + global.elapsed_ticks = global.elapsed_ticks + global.utils.calculate_movement_ticks(player, last_position, path[i].position) + last_position = path[i].position + end + + local serialized_entities = {} + + -- Get source and target entities + local source_entity = global.utils.get_closest_entity(player, {x = source_x, y = source_y}) + local target_entity = global.utils.get_closest_entity(player, {x = target_x, y = target_y}) + + + if #connection_types == 1 and connection_types[1] == 'pipe-to-ground' then + + -- Calculate the direction from start to end. + local dir = global.utils.get_direction(start_position, end_position) + local entrance_dir = global.utils.get_entity_direction(underground_type, dir / 2) + + -- Place the underground entrance at the start position. + place_at_position(player, underground_type, start_position, entrance_dir, + serialized_entities, dry_run, counter_state, false) + + local exit_dir + if underground_type == "pipe-to-ground" then + -- For pipe-to-ground, rotate the direction 180° for the exit. + exit_dir = global.utils.get_entity_direction(underground_type, (dir / 2 + 2) % 4) + else + exit_dir = global.utils.get_entity_direction(underground_type, dir / 2) + end + + -- Place the underground exit at the end position. + place_at_position(player, underground_type, end_position, exit_dir, + serialized_entities, dry_run, counter_state, true) + + return { + entities = serialized_entities, + connected = true, + number_of_entities = counter_state.place_counter + } + end + + + if underground_type then + -- Calculate maximum possible underground sections based on inventory + local max_underground_sections = calculate_max_underground_sections(player, underground_type) + + -- Find straight sections suitable for underground entities + local range = underground_ranges[underground_type] + local straight_sections = find_straight_sections(path, 4) + + -- Track remaining underground sections we can create + local remaining_sections = max_underground_sections + + + + + local current_index = 1 + for _, section in ipairs(straight_sections) do + + if remaining_sections <= 0 then + break + end + + local MAX_INDEX_PLACEMENT = 200 + -- Place regular entities up to the section + while current_index < section.start_index do + local current_pos = path[current_index].position + local next_pos = path[current_index + 1].position + local dir = global.utils.get_direction(current_pos, next_pos) + place_at_position(player, default_connection_type, current_pos, + global.utils.get_entity_direction(default_connection_type, dir/2), + serialized_entities, dry_run, counter_state, false) + current_index = current_index + 1 + if current_index > MAX_INDEX_PLACEMENT then + break + end + end + + -- Place initial surface entity for direction change + local margin_pos = path[section.start_index].position + local margin_next_pos = path[section.start_index + 1].position + local margin_dir = global.utils.get_direction(margin_pos, margin_next_pos) + place_at_position(player, default_connection_type, margin_pos, + global.utils.get_entity_direction(default_connection_type, margin_dir/2), + serialized_entities, dry_run, counter_state, false) + + -- Split the section into multiple underground segments, limited by remaining_sections + local segments = split_section_into_underground_segments(section, path, range, remaining_sections) + + -- Update remaining_sections count + remaining_sections = remaining_sections - #segments + + for i, segment in ipairs(segments) do + -- Place underground entrance + local entrance_pos = path[segment.entrance_index].position + local exit_pos = path[segment.exit_index].position + local dir = global.utils.get_direction(entrance_pos, exit_pos) + + local entity_dir = global.utils.get_entity_direction(underground_type, dir/2) + + place_at_position(player, underground_type, entrance_pos, + entity_dir, + serialized_entities, dry_run, counter_state, false) + + -- Adjust direction for pipe-to-ground exit and place exit pipe + if underground_type == 'pipe-to-ground' then + entity_dir = global.utils.get_entity_direction(underground_type, (dir/2 + 2)%4) + end + + place_at_position(player, underground_type, exit_pos, + entity_dir, + serialized_entities, dry_run, counter_state, true) + + -- Update current_index to after the exit + current_index = segment.exit_index + 1 + end + -- Place final surface entity for direction change if we had segments + if #segments > 0 then + local final_margin_pos = path[section.end_index].position + local final_prev_pos = path[section.end_index - 1].position + local final_dir = global.utils.get_direction(final_prev_pos, final_margin_pos) + place_at_position(player, default_connection_type, final_margin_pos, + global.utils.get_entity_direction(default_connection_type, final_dir/2), + serialized_entities, dry_run, counter_state, false) + end + + -- If we've used all available underground sections, use regular entities for the rest + if remaining_sections <= 0 then + break + end + + end + + -- Place remaining regular entities to finish the connection + while current_index < #path do + local current_pos = path[current_index].position + local next_pos = path[current_index + 1].position + local dir = global.utils.get_direction(current_pos, next_pos) + place_at_position(player, default_connection_type, current_pos, + global.utils.get_entity_direction(default_connection_type, dir/2), + serialized_entities, dry_run, counter_state, false) + current_index = current_index + 1 + end + + -- After underground segment placement + if current_index == #path and not path[#path].has_entity then + local final_pos = path[#path].position + local prev_pos = path[#path-1].position + local dir = global.utils.get_direction(prev_pos, final_pos) + place_at_position(player, default_connection_type, final_pos, + global.utils.get_entity_direction(default_connection_type, dir/2), + serialized_entities, dry_run, counter_state, false) + end + else + -- Handle source belt orientation if it exists + if not is_electric_pole and not table_contains(connection_types, 'pipe') then + local source_pos = {x = source_x, y = source_y} + local entities = game.surfaces[1].find_entities_filtered{ + position = source_pos, + name = connection_types, + force = "player" + } + + if #entities > 0 and #path > 1 then + -- Calculate initial direction based on first two points in path + local initial_dir = global.utils.get_direction(path[1].position, path[2].position) + --local entity_dir = global.utils.get_entity_direction('pipe', initial_dir/2) + + -- Update source belt direction if needed + local source_belt = entities[1] + if source_belt and source_belt.valid and source_belt.direction ~= initial_dir then + source_belt.direction = initial_dir + table.insert(serialized_entities, global.utils.serialize_entity(source_belt)) + end + end + end + + if is_electric_pole then + -- Place poles until we achieve connectivity + local last_pole = source_entity + + for i = 1, #path, step_size do + local current_pos = path[i].position + local dir = global.utils.get_direction(current_pos, path[math.min(i + step_size, #path)].position) + local entity_dir = global.utils.get_entity_direction(default_connection_type, dir/2) + + -- Place the pole + local placed_entity = place_at_position(player, default_connection_type, current_pos, entity_dir, serialized_entities, dry_run, counter_state) + + if not dry_run then + -- Get the newly placed pole + local current_pole = placed_entity or global.utils.get_closest_entity(player, current_pos) + + -- Check if we've achieved connectivity to the target + if are_poles_connected(current_pole, target_entity) then + break -- Stop placing poles once we have connectivity + end + + last_pole = current_pole + end + end + + -- If we haven't achieved connectivity yet, place one final pole at the target + if not dry_run and last_pole and target_entity and not are_poles_connected(last_pole, target_entity) then + local final_dir = global.utils.get_direction(path[#path].position, end_position) + -- game.print("Placing final pole at "..serpent.line(end_position)) + place_at_position(player, default_connection_type, end_position, + global.utils.get_entity_direction(default_connection_type, final_dir/2), + serialized_entities, dry_run, counter_state) + end + else + if table_contains(connection_types, 'pipe') then + place_at_position(player, 'pipe', start_position, 0, serialized_entities, dry_run, counter_state) + end + + for i = 1, #path-1, step_size do + local dir = global.utils.get_direction(path[i].position, path[math.min(i + step_size, #path)].position) + local placed = place_at_position(player, default_connection_type, path[i].position, + global.utils.get_entity_direction(default_connection_type, dir/2), + serialized_entities, dry_run, counter_state) + if placed then + last_placed_entity = placed + end + end + + -- Handle final placement + if table_contains(connection_types, 'pipe') then + local preemptive_target = { + x = (target_x + path[#path].position.x)/2, + y = (target_y + path[#path].position.y)/2 + } + + -- Place intermediate and final pipes + place_at_position(player, 'pipe', path[#path].position, + global.utils.get_direction(path[#path].position, preemptive_target), + serialized_entities, dry_run, counter_state) + + place_at_position(player, 'pipe', preemptive_target, + global.utils.get_direction(path[#path].position, preemptive_target), + serialized_entities, dry_run, counter_state) + + place_at_position(player, 'pipe', end_position, + global.utils.get_direction(preemptive_target, end_position), + serialized_entities, dry_run, counter_state) + else + local last_path_index = #path + local second_to_last_index = math.max(1, last_path_index - 1) + local final_dir = global.utils.get_direction( + path[second_to_last_index].position, + path[last_path_index].position + ) + + local final_entity = place_at_position(player, default_connection_type, end_position, + global.utils.get_entity_direction(default_connection_type, final_dir/2), + serialized_entities, dry_run, counter_state) + + if final_entity then + last_placed_entity = final_entity + end + + -- After all belts are placed, serialize the entire belt group if we're not in dry run + if not dry_run and last_placed_entity and last_placed_entity.valid and default_connection_type:find("belt") then + -- Clear the existing serialized entities (which only contain individual placements) + + serialized_entities = {} + -- Serialize the entire connected belt group + local group_data = serialize_belt_group(last_placed_entity) + if group_data then + for _, serialized in ipairs(group_data) do + table.insert(serialized_entities, serialized) + end + end + end + end + end + end + + -- Check final connectivity + local is_connected = false + if source_entity and target_entity then + if is_electric_pole then + is_connected = are_poles_connected(source_entity, target_entity) + else + is_connected = are_fluidboxes_connected(source_entity, target_entity) + end + end + + -- game.print("Connection status: " .. tostring(is_connected)) + + + return { + entities = serialized_entities, + connected = is_connected, + number_of_entities = counter_state.place_counter + } +end + +global.utils.normalise_path = function(original_path, start_position, end_position) + local path = {} + local seen = {} -- To track seen positions + if original_path == nil or #original_path < 1 or original_path == "not_found" then + error("Failed to find a path.") + end + if math.ceil(start_position.x) == start_position.x then + start_position.x = start_position.x + 0.5 + --start_position.y = start_position.y + 0.5 + end + if math.ceil(start_position.y) == start_position.y then + start_position.y = start_position.y + 0.5 + end + + if math.ceil(end_position.x) == end_position.x or math.ceil(end_position.y) == end_position.y then + end_position.x = end_position.x + 0.5 + end_position.y = end_position.y + 0.5 + end + + --game.print(serpent.block(original_path)) + --for i = 1, #original_path do + -- game.print(serpent.line(original_path[i])) + --end + + if math.ceil(original_path[1].position.x) == original_path[1].position.x or math.ceil(original_path[1].position.y) == original_path[1].position.y then + original_path[1].position.x = original_path[1].position.x + 0.5 + original_path[1].position.y = original_path[1].position.y + 0.5 + end + + -- Helper function to add unique positions + local function add_unique(pos, prev_pos) + local key = pos.x .. "," .. pos.y + if not seen[key] then + if is_placeable(pos) then + table.insert(path, {position = pos}) + seen[key] = true + return pos + else + local alt_pos = find_placeable_neighbor(pos, prev_pos) + if alt_pos then + local alt_key = alt_pos.x .. "," .. alt_pos.y + if not seen[alt_key] then + table.insert(path, {position = alt_pos}) + seen[alt_key] = true + return alt_pos + end + end + end + end + return nil + end + + -- Add start position first + local previous_pos = add_unique(start_position, nil) or start_position + + -- Process each segment of the path + local current_pos = previous_pos + for i = 1, #original_path do + local target_pos = original_path[i].position + local interpolated = interpolate_manhattan(current_pos, target_pos) + + -- Add interpolated positions + for _, point in ipairs(interpolated) do + local new_pos = add_unique(point.position, current_pos) + if new_pos then + current_pos = new_pos + end + end + + -- Add the target position + local new_pos = add_unique(target_pos, current_pos) + if new_pos then + current_pos = new_pos + end + end + + -- Finally interpolate to end position if it's different from the last position + if current_pos.x ~= end_position.x or current_pos.y ~= end_position.y then + local interpolated = interpolate_manhattan(current_pos, end_position) + for _, point in ipairs(interpolated) do + local new_pos = add_unique(point.position, current_pos) + if new_pos then + current_pos = new_pos + end + end + add_unique(end_position, current_pos) + end + + return path +end + +-- Helper function to check if two positions are adjacent +local function are_positions_adjacent(pos1, pos2) + local dx = math.abs(pos1.x - pos2.x) + local dy = math.abs(pos1.y - pos2.y) + return (dx <= 1 and dy <= 1) and not (dx == 0 and dy == 0) +end + +-- Helper function to validate belt direction compatibility +local function are_belt_directions_compatible(dir1, dir2, pos1, pos2) + -- Convert positions to vectors + local dx = pos2.x - pos1.x + local dy = pos2.y - pos1.y + + -- Check if the belts are pointing in compatible directions + -- This is a simplified check - you may need to adjust based on your specific needs + if dir1 == dir2 then + -- Same direction - check if it matches position delta + local expected_dir = global.utils.get_direction(pos1, pos2) + return dir1 == expected_dir + else + -- Different directions - check if they form a valid turn + -- Add more sophisticated turn validation if needed + return are_positions_adjacent(pos1, pos2) + end +end + + +-- Function to validate belt connectivity +local function validate_belt_connectivity(path) + if not path or #path < 2 then + return false, "Invalid path length" + end + + -- Check each segment of the path + for i = 1, #path - 1 do + local current_pos = path[i].position + local next_pos = path[i + 1].position + + -- Check if positions are adjacent or properly spaced + if not are_positions_adjacent(current_pos, next_pos) then + return false, "Could not create a belt at position " .. + current_pos.x .. "," .. current_pos.y + end + + -- Calculate directions for current and next position + local current_dir = global.utils.get_direction(current_pos, next_pos) + local next_dir = i < #path - 1 and + global.utils.get_direction(next_pos, path[i + 2].position) or + current_dir + + -- Check direction compatibility + if not are_belt_directions_compatible(current_dir, next_dir, current_pos, next_pos) then + return false, "Invalid belt turn detected at position " .. + current_pos.x .. "," .. current_pos.y + end + + -- Check if position is placeable + if not is_placeable(current_pos) then + return false, "Cannot place belt at position " .. + current_pos.x .. "," .. current_pos.y + end + end + + -- Check final position + if not is_placeable(path[#path].position) then + return false, "Cannot place belt at final position" + end + + return true, nil +end + +-- Modify the connect_entities function to include validation +local function connect_entities_with_validation(player_index, source_x, source_y, target_x, target_y, path_handle, connection_types, dry_run) + local path = global.paths[path_handle] + if path == nil then + error("No path found") + end + -- Only perform validation for belt-type entities + for _,connection_type in pairs(connection_types) do + if wire_reach[connection_type] then + if are_positions_in_same_network( + {x = source_x, y = source_y}, + {x = target_x, y = target_y} + ) then + --error("Source and target positions are already connected to the same power network") + --return {} + break + end + --break + elseif connection_type:find("belt") then + -- Normalize path first + local normalized_path = global.utils.normalise_path(path, + {x = source_x, y = source_y}, + {x = target_x, y = target_y}) + + -- Validate connectivity + local is_valid, error_message = validate_belt_connectivity(normalized_path) + if not is_valid then + error(error_message) + end + break + end + end + + -- If validation passes or it's not a belt, proceed with normal connection + return connect_entities(player_index, source_x, source_y, target_x, target_y, + path_handle, connection_types, dry_run) +end + + +-- Using the new shortest_path function. +global.actions.connect_entities = function(player_index, source_x, source_y, target_x, target_y, path_handle, connection_type_string, dry_run, number_of_connection_entities) + + local connection_types = {} + for item in string.gmatch(connection_type_string, "([^,]+)") do + -- game.print(item) + table.insert(connection_types, item) + end + --First do a dry run + local result = connect_entities_with_validation(player_index, source_x, source_y, target_x, target_y, path_handle, connection_types, true) + -- then do an actual run if dry run is false + if not dry_run then + -- Check if the player has enough entities in their inventory + local required_count = result.number_of_entities + -- game.print("Required count: " .. required_count) + -- game.print("Available count: " .. number_of_connection_entities) + if number_of_connection_entities < required_count then + error("\"You do not have enough " .. connection_type_string .. " in you inventory to complete this connection. Required number - " .. required_count .. ", Available in inventory - " .. number_of_connection_entities.."\"") + end + result = connect_entities_with_validation(player_index, source_x, source_y, target_x, target_y, path_handle, connection_types, false) + end + + return result +end \ No newline at end of file diff --git a/fle/env/tools/agent/craft_item/agent.md b/fle/env/factorio/tools/agent/craft_item/agent.md similarity index 100% rename from fle/env/tools/agent/craft_item/agent.md rename to fle/env/factorio/tools/agent/craft_item/agent.md diff --git a/fle/env/tools/agent/craft_item/client.py b/fle/env/factorio/tools/agent/craft_item/client.py similarity index 92% rename from fle/env/tools/agent/craft_item/client.py rename to fle/env/factorio/tools/agent/craft_item/client.py index e9932cc40..44ff24e1c 100644 --- a/fle/env/tools/agent/craft_item/client.py +++ b/fle/env/factorio/tools/agent/craft_item/client.py @@ -1,8 +1,8 @@ from time import sleep from fle.env.game_types import Prototype -from fle.env.tools.agent.inspect_inventory.client import InspectInventory -from fle.env.tools import Tool +from fle.env.factorio.tools.agent.inspect_inventory.client import InspectInventory +from fle.env.factorio.tools import Tool class CraftItem(Tool): diff --git a/fle/env/factorio/tools/agent/craft_item/server.lua b/fle/env/factorio/tools/agent/craft_item/server.lua new file mode 100644 index 000000000..225b0b41b --- /dev/null +++ b/fle/env/factorio/tools/agent/craft_item/server.lua @@ -0,0 +1,213 @@ +global.actions.craft_item = function(player_index, entity, count) + local player = global.agent_characters[player_index] + + local function calculate_crafting_ticks(recipe, crafts_count) + -- energy_required is in seconds, multiply by 60 to get ticks + local ticks_per_craft = (recipe.energy or 0.5) * 60 + return math.ceil(ticks_per_craft * crafts_count) + end + + local function check_inventory_space(player, item_name, count) + -- Get the prototype of the item + local item_prototype = game.item_prototypes[item_name] + if not item_prototype then + return false, "Invalid item prototype" + end + + -- Calculate how many slots this would need + local stack_size = item_prototype.stack_size + local slots_needed = math.ceil(count / stack_size) + + -- Check if player has enough free inventory slots + local inventory = player.get_main_inventory() + if not inventory then + return false, "Cannot access player inventory" + end + + -- Count free slots and partially filled slots of the same item + local available_slots = 0 + for i = 1, #inventory do + local stack = inventory[i] + if not stack.valid_for_read then + -- Empty slot + available_slots = available_slots + 1 + elseif stack.name == item_name and stack.count < stack_size then + -- Partially filled slot of the same item + available_slots = available_slots + 1 + end + end + + if available_slots < slots_needed then + return false, "Inventory is full - requires " .. slots_needed .. " slots but only " .. available_slots .. " available" + end + + return true + end + + + local function get_missing_ingredients(player, recipe, count) + local missing_ingredients = {} + local crafts_needed = math.ceil(count / recipe.products[1].amount) + for _, ingredient in pairs(recipe.ingredients) do + local count_that_player_has = player.get_item_count(ingredient.name) + local needed = ingredient.amount * crafts_needed + if count_that_player_has < needed then + local difference = needed - count_that_player_has + missing_ingredients[ingredient.name] = difference + end + end + return missing_ingredients + end + + local function get_required_technology(recipe_name, force) + for _, tech in pairs(force.technologies) do + if tech.effects then + for _, effect in pairs(tech.effects) do + if effect.type == "unlock-recipe" and effect.recipe == recipe_name then + return tech.name + end + end + end + end + return nil + end + + local function can_craft_recipe(player, recipe_name) + local recipe = player.force.recipes[recipe_name] + if not recipe then + return false, "recipe for " .. recipe_name .. " doesn't exist, it is a raw resource that must be gathered first" + end + if not recipe.enabled then + local required_tech = get_required_technology(recipe_name, player.force) + local tech_message = required_tech and string.format(" (requires %s technology)", required_tech) or "" + return false, "recipe for " .. recipe_name .. " is not unlocked yet" .. tech_message .. ". You need to research the technology first" + end + if recipe.category ~= "crafting" then + return false, "Item " .. recipe_name .. " cannot be crafted. Recipe requires a crafting machine or smelting in a furnace" + end + return true, recipe + end + + local function update_production_stats(force, recipe, crafts_count) + local stats = force.item_production_statistics + local craft_stats = {crafted_count = crafts_count, inputs = {}, outputs = {}} + for _, ingredient in pairs(recipe.ingredients) do + craft_stats.inputs[ingredient.name] = ingredient.amount * crafts_count + stats.on_flow(ingredient.name, -ingredient.amount * crafts_count) + end + for _, product in pairs(recipe.products) do + if product.type == "item" then + stats.on_flow(product.name, product.amount * crafts_count) + craft_stats.outputs[product.name] = product.amount * crafts_count + end + end + table.insert(global.crafted_items, craft_stats) + end + + -- Single recursive crafting function that handles both fast and slow modes + local function attempt_craft(player, entity_name, count, attempted_recipes) + attempted_recipes = attempted_recipes or {} + + -- Prevent infinite recursion + if attempted_recipes[entity_name] then + return 0, "recursive crafting loop detected" + end + attempted_recipes[entity_name] = true + + local can_craft, recipe_or_error = can_craft_recipe(player, entity_name) + if not can_craft then + return 0, recipe_or_error + end + + local recipe = recipe_or_error + local crafts_needed = math.ceil(count / recipe.products[1].amount) + local actual_craft_count = crafts_needed * recipe.products[1].amount + + -- Check for missing ingredients + local missing_ingredients = get_missing_ingredients(player, recipe, actual_craft_count) + if next(missing_ingredients) then + -- Try to craft each missing ingredient + for ingredient_name, needed_amount in pairs(missing_ingredients) do + local crafted_amount, error_msg = attempt_craft(player, ingredient_name, needed_amount, attempted_recipes) + if crafted_amount == 0 then + return 0, "couldn't craft a required sub-ingredient for ".. entity_name .. " - " .. ingredient_name .. " - " .. error_msg .. ". Required " ..ingredient_name.. " amount for " .. entity_name .. " - " .. needed_amount .. ")" + end + end + end + + -- Calculate total ticks needed for this craft + local crafting_ticks = calculate_crafting_ticks(recipe, crafts_needed) + + + -- After potentially crafting intermediates, check if we can now craft the original item + if global.fast then + -- Only add ticks in fast mode since in slow mode they are added naturally + global.elapsed_ticks = global.elapsed_ticks + crafting_ticks + + -- Add inventory space check here + local can_insert, error_msg = check_inventory_space(player, entity_name, actual_craft_count) + if not can_insert then + return 0, error_msg + end + + -- Fast crafting implementation + local missing = get_missing_ingredients(player, recipe, actual_craft_count) + if next(missing) then + local missing_str = "" + for name, amount in pairs(missing) do + missing_str = missing_str .. name .. " x" .. amount .. ", " + end + return 0, "still missing ingredients - " .. missing_str:sub(1, -3) + end + + for _, ingredient in pairs(recipe.ingredients) do + player.remove_item({name = ingredient.name, count = ingredient.amount * crafts_needed}) + end + + local crafted = player.insert({name = entity_name, count = actual_craft_count}) + if crafted < actual_craft_count then + player.surface.spill_item_stack(player.position, {name = entity_name, count = actual_craft_count - crafted}) + end + + update_production_stats(player.force, recipe, crafted) + return crafted, nil + else + -- Slow crafting implementation + local crafted = player.begin_crafting{count=count, recipe=entity_name} + if crafted == 0 then + return 0, "unable to begin crafting - check prerequisites and inventory space" + end + update_production_stats(player.force, recipe, crafted) + return crafted, nil + end + end + + -- Main crafting logic + local total_crafted = 0 + local final_error = nil + + while total_crafted < count do + local remaining = count - total_crafted + local crafted_amount, error_msg = attempt_craft(player, entity, remaining, {}) + + if crafted_amount > 0 then + total_crafted = total_crafted + crafted_amount + if not global.fast then + break + end + else + final_error = error_msg + break + end + end + + if total_crafted >= count or (not global.fast and total_crafted > 0) then + return count + elseif total_crafted > 0 then + error(string.format("\"Successfully crafted %dx but failed to craft %dx %s because %s\"", + total_crafted, count - total_crafted, entity, final_error)) + else + error(string.format("\"Failed to craft %dx %s because %s\"", + count, entity, final_error)) + end +end diff --git a/fle/env/tools/agent/extract_item/agent.md b/fle/env/factorio/tools/agent/extract_item/agent.md similarity index 100% rename from fle/env/tools/agent/extract_item/agent.md rename to fle/env/factorio/tools/agent/extract_item/agent.md diff --git a/fle/env/tools/agent/extract_item/client.py b/fle/env/factorio/tools/agent/extract_item/client.py similarity index 97% rename from fle/env/tools/agent/extract_item/client.py rename to fle/env/factorio/tools/agent/extract_item/client.py index 311719f61..d36f55862 100644 --- a/fle/env/tools/agent/extract_item/client.py +++ b/fle/env/factorio/tools/agent/extract_item/client.py @@ -2,7 +2,7 @@ from fle.env.entities import Position, Entity from fle.env.game_types import Prototype -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class ExtractItem(Tool): diff --git a/fle/env/factorio/tools/agent/extract_item/server.lua b/fle/env/factorio/tools/agent/extract_item/server.lua new file mode 100644 index 000000000..1d00a2fda --- /dev/null +++ b/fle/env/factorio/tools/agent/extract_item/server.lua @@ -0,0 +1,195 @@ +-- Helper function to check all possible inventories of an entity +local function get_entity_item_count(entity, item_name) + local inventory_types = { + defines.inventory.chest, + defines.inventory.furnace_source, + defines.inventory.furnace_result, + defines.inventory.assembling_machine_input, + defines.inventory.assembling_machine_output, + defines.inventory.fuel, + defines.inventory.burnt_result, + defines.inventory.reactor_source, + defines.inventory.reactor_result, + defines.inventory.lab_input, + defines.inventory.lab_source, + defines.inventory.mining_drill_input, + defines.inventory.item_main, -- For cargo wagons + defines.inventory.robot_cargo, + defines.inventory.robot_repair, + defines.inventory.car_trunk, + defines.inventory.car_fuel, + defines.inventory.roboport_material, + defines.inventory.roboport_robot, + defines.inventory.storage_tank, + defines.inventory.artillery_turret_ammo, + defines.inventory.turret_ammo, + defines.inventory.beacon_modules, + defines.inventory.character_main, + defines.inventory.character_guns, + defines.inventory.character_ammo, + defines.inventory.character_armor, + defines.inventory.character_vehicle, + defines.inventory.character_trash + } + + local total_count = 0 + for _, inv_type in ipairs(inventory_types) do + local inventory = entity.get_inventory(inv_type) + if inventory then + total_count = total_count + entity.get_item_count(item_name) + end + end + return total_count +end + +-- Helper function to remove items from any valid inventory +local function remove_items_from_entity(entity, stack) + local inventory_types = { + defines.inventory.chest, + defines.inventory.furnace_source, + defines.inventory.furnace_result, + defines.inventory.assembling_machine_input, + defines.inventory.assembling_machine_output, + defines.inventory.fuel, + defines.inventory.burnt_result, + defines.inventory.reactor_source, + defines.inventory.reactor_result, + defines.inventory.lab_input, + defines.inventory.lab_source, + defines.inventory.mining_drill_input, + defines.inventory.item_main, + defines.inventory.robot_cargo, + defines.inventory.robot_repair, + defines.inventory.car_trunk, + defines.inventory.car_fuel, + defines.inventory.roboport_material, + defines.inventory.roboport_robot, + defines.inventory.storage_tank, + defines.inventory.artillery_turret_ammo, + defines.inventory.turret_ammo, + defines.inventory.beacon_modules, + defines.inventory.character_main, + defines.inventory.character_guns, + defines.inventory.character_ammo, + defines.inventory.character_armor, + defines.inventory.character_vehicle, + defines.inventory.character_trash + } + + local items_remaining = stack.count + local total_removed = 0 + + for _, inv_type in ipairs(inventory_types) do + if items_remaining <= 0 then + break + end + + local inventory = entity.get_inventory(inv_type) + if inventory then + local current_stack = {name = stack.name, count = items_remaining} + local removed = entity.remove_item(current_stack) + total_removed = total_removed + removed + items_remaining = items_remaining - removed + end + end + + return total_removed +end + +global.actions.extract_item = function(player_index, extract_item, count, x, y, source_name) + local player = global.agent_characters[player_index] + local position = {x=x, y=y} + local surface = player.surface + + -- First validate the request + if count <= 0 then + error("\"Invalid count: must be greater than 0\"") + end + + -- Find all entities in range + local search_radius = 10 + local area = {{position.x - search_radius, position.y - search_radius}, + {position.x + search_radius, position.y + search_radius}} + local buildings = nil + + if source_name ~= nil then + buildings = surface.find_entities_filtered{ + area = area, + name = source_name + } + else + buildings = surface.find_entities_filtered{ + area = area + } + end + + -- Find the closest building with the item we want + local closest_distance = math.huge + local closest_entity = nil + local found_any_items = false + + for _, building in ipairs(buildings) do + if building.name ~= 'character' then + local item_count = get_entity_item_count(building, extract_item) + if item_count > 0 then + found_any_items = true + local distance = ((position.x - building.position.x) ^ 2 + + (position.y - building.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest_entity = building + end + end + end + end + + -- Error handling in priority order + + if #buildings == 0 then + error("\"Could not find any entities in range\"") + end + + if not closest_entity then + if source_name then + error("\"Could not find a valid "..source_name.." entity containing " .. extract_item.."\"") + else + error("\"Could not find a valid entity containing " .. extract_item .. "\"") + end + end + + if closest_distance > search_radius then + error("\"Entity at ("..closest_entity.position.x..", "..closest_entity.position.y..") is too far away from your position of ("..player.position.x..","..player.position.y.."), move closer.\"") + end + + if not found_any_items then + error("\"No " .. extract_item .. " found in any nearby entities\"") + end + + -- Calculate how many items we can actually extract + local available_count = get_entity_item_count(closest_entity, extract_item) + local extract_count = math.min(count, available_count) + + -- Create the stack for extraction + local stack = {name=extract_item, count=extract_count} + + -- Attempt the extraction + local number_extracted = remove_items_from_entity(closest_entity, stack) + + if number_extracted > 0 then + -- Insert items into player inventory + local inserted = player.insert(stack) + + -- If we couldn't insert all items, put them back in the container + if inserted < number_extracted then + stack.count = number_extracted - inserted + closest_entity.insert(stack) + number_extracted = inserted + end + + -- game.print("Extracted " .. number_extracted .. " " .. extract_item) + return number_extracted + else + -- This should rarely happen given our prior checks + error("\"Failed to extract " .. extract_item .. "\"") + end +end \ No newline at end of file diff --git a/fle/env/tools/agent/get_connection_amount/agent.md b/fle/env/factorio/tools/agent/get_connection_amount/agent.md similarity index 100% rename from fle/env/tools/agent/get_connection_amount/agent.md rename to fle/env/factorio/tools/agent/get_connection_amount/agent.md diff --git a/fle/env/tools/agent/get_connection_amount/client.py b/fle/env/factorio/tools/agent/get_connection_amount/client.py similarity index 90% rename from fle/env/tools/agent/get_connection_amount/client.py rename to fle/env/factorio/tools/agent/get_connection_amount/client.py index 210505120..c174e8359 100644 --- a/fle/env/tools/agent/get_connection_amount/client.py +++ b/fle/env/factorio/tools/agent/get_connection_amount/client.py @@ -2,8 +2,8 @@ from fle.env.entities import Entity, Position, EntityGroup from fle.env.game_types import Prototype -from fle.env.tools.agent.connect_entities.client import ConnectEntities -from fle.env.tools import Tool +from fle.env.factorio.tools.agent.connect_entities.client import ConnectEntities +from fle.env.factorio.tools import Tool class GetConnectionAmount(Tool): diff --git a/fle/env/factorio/tools/agent/get_connection_amount/server.lua b/fle/env/factorio/tools/agent/get_connection_amount/server.lua new file mode 100644 index 000000000..e69de29bb diff --git a/fle/env/tools/agent/get_entities/agent.md b/fle/env/factorio/tools/agent/get_entities/agent.md similarity index 100% rename from fle/env/tools/agent/get_entities/agent.md rename to fle/env/factorio/tools/agent/get_entities/agent.md diff --git a/fle/env/tools/agent/get_entities/client.py b/fle/env/factorio/tools/agent/get_entities/client.py similarity index 98% rename from fle/env/tools/agent/get_entities/client.py rename to fle/env/factorio/tools/agent/get_entities/client.py index 034c32f51..c35c28c4c 100644 --- a/fle/env/tools/agent/get_entities/client.py +++ b/fle/env/factorio/tools/agent/get_entities/client.py @@ -2,10 +2,10 @@ from typing import List, Set, Union from fle.env.entities import Position, Entity, EntityGroup from fle.env.game_types import Prototype -from fle.env.tools.agent.connect_entities.groupable_entities import ( +from fle.env.factorio.tools.agent.connect_entities.groupable_entities import ( agglomerate_groupable_entities, ) -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class GetEntities(Tool): diff --git a/fle/env/factorio/tools/agent/get_entities/server.lua b/fle/env/factorio/tools/agent/get_entities/server.lua new file mode 100644 index 000000000..0b1440d8e --- /dev/null +++ b/fle/env/factorio/tools/agent/get_entities/server.lua @@ -0,0 +1,37 @@ +global.actions.get_entities = function(player_index, radius, entity_names_json, position_x, position_y) + local player = global.agent_characters[player_index] + local position + if position_x and position_y then + position = {x = tonumber(position_x), y = tonumber(position_y)} + else + position = player.position + end + + radius = tonumber(radius) or 5 + local entity_names = game.json_to_table(entity_names_json) or {} + local area = { + {position.x - radius, position.y - radius}, + {position.x + radius, position.y + radius} + } + + local filter = {} + if entity_names and #entity_names > 0 then + filter = {name = entity_names} + end + + local entities + if #entity_names > 0 then + entities = player.surface.find_entities_filtered{area = area, force = player.force, filter=filter} + else + entities = player.surface.find_entities_filtered{area = area, force = player.force} + end + + local result = {} + for _, entity in ipairs(entities) do + if entity.name ~= 'character' then + local serialized = global.utils.serialize_entity(entity) + table.insert(result, serialized) + end + end + return dump(result) +end \ No newline at end of file diff --git a/fle/env/tools/agent/get_entity/agent.md b/fle/env/factorio/tools/agent/get_entity/agent.md similarity index 100% rename from fle/env/tools/agent/get_entity/agent.md rename to fle/env/factorio/tools/agent/get_entity/agent.md diff --git a/fle/env/tools/agent/get_entity/client.py b/fle/env/factorio/tools/agent/get_entity/client.py similarity index 96% rename from fle/env/tools/agent/get_entity/client.py rename to fle/env/factorio/tools/agent/get_entity/client.py index c1472a1d0..a8879e95b 100644 --- a/fle/env/tools/agent/get_entity/client.py +++ b/fle/env/factorio/tools/agent/get_entity/client.py @@ -3,8 +3,8 @@ from fle.env.entities import Position, Entity from fle.env.game_types import Prototype -from fle.env.tools.agent.get_entities.client import GetEntities -from fle.env.tools import Tool +from fle.env.factorio.tools.agent.get_entities.client import GetEntities +from fle.env.factorio.tools import Tool class GetEntity(Tool): diff --git a/fle/env/factorio/tools/agent/get_entity/server.lua b/fle/env/factorio/tools/agent/get_entity/server.lua new file mode 100644 index 000000000..deb63415e --- /dev/null +++ b/fle/env/factorio/tools/agent/get_entity/server.lua @@ -0,0 +1,48 @@ +global.actions.get_entity = function(player_index, entity, x, y) + local player = global.agent_characters[player_index] + local position = {x=x, y=y} + + if game.entity_prototypes[entity] == nil then + local name = entity:gsub(" ", "_"):gsub("-", "_") + error(name .. " isnt something that exists. Did you make a typo? ") + end + + local prototype = game.entity_prototypes[entity] + --local collision_box = prototype.collision_box + local width = 1--math.abs(collision_box.right_bottom.x - collision_box.left_top.x) + local height = 1--math.abs(collision_box.right_bottom.y - collision_box.left_top.y) + + -- game.print("Width: " .. width) + -- game.print("Height: " .. height) + + local target_area = { + {position.x - width , position.y - height }, + {position.x + width , position.y + height } + } + --local entities = player.surface.find_entities_filtered{area = target_area} --, name = entity} + local entities = player.surface.find_entities_filtered{area = target_area, name = entity} + -- game.print("Number of entities found: " .. #entities) + + local closest_distance = math.huge + local closest_entity = nil + + for _, building in ipairs(entities) do + if building.name ~= 'character' then + local distance = ((position.x - building.position.x) ^ 2 + + (position.y - building.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest_entity = building + end + end + end + + if closest_entity ~= nil then + local entity = closest_entity -- get the first entity of the specified type in the area + local serialized = global.utils.serialize_entity(entity) + --local entity_json = game.table_to_json(serialized)-- game.table_to_json(entity + return serialized + else + error("\"No entity of type " .. entity .. " found at the specified position.\"") + end +end diff --git a/fle/env/tools/agent/get_prototype_recipe/agent.md b/fle/env/factorio/tools/agent/get_prototype_recipe/agent.md similarity index 100% rename from fle/env/tools/agent/get_prototype_recipe/agent.md rename to fle/env/factorio/tools/agent/get_prototype_recipe/agent.md diff --git a/fle/env/tools/agent/get_prototype_recipe/client.py b/fle/env/factorio/tools/agent/get_prototype_recipe/client.py similarity index 97% rename from fle/env/tools/agent/get_prototype_recipe/client.py rename to fle/env/factorio/tools/agent/get_prototype_recipe/client.py index aa29bb02a..5a0e5d4b5 100644 --- a/fle/env/tools/agent/get_prototype_recipe/client.py +++ b/fle/env/factorio/tools/agent/get_prototype_recipe/client.py @@ -2,7 +2,7 @@ from fle.env import Recipe, Ingredient, Product from fle.env.game_types import Prototype, RecipeName -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class GetPrototypeRecipe(Tool): diff --git a/fle/env/factorio/tools/agent/get_prototype_recipe/server.lua b/fle/env/factorio/tools/agent/get_prototype_recipe/server.lua new file mode 100644 index 000000000..e3e7c41f9 --- /dev/null +++ b/fle/env/factorio/tools/agent/get_prototype_recipe/server.lua @@ -0,0 +1,10 @@ +global.actions.get_prototype_recipe = function(player_index, recipe_name) + local player = global.agent_characters[player_index] + local recipe = player.force.recipes[recipe_name] + if not recipe then + return "recipe doesnt exist" + end + local serialized = global.utils.serialize_recipe(recipe) + return serialized +end + diff --git a/fle/env/tools/agent/get_research_progress/agent.md b/fle/env/factorio/tools/agent/get_research_progress/agent.md similarity index 100% rename from fle/env/tools/agent/get_research_progress/agent.md rename to fle/env/factorio/tools/agent/get_research_progress/agent.md diff --git a/fle/env/tools/agent/get_research_progress/client.py b/fle/env/factorio/tools/agent/get_research_progress/client.py similarity index 97% rename from fle/env/tools/agent/get_research_progress/client.py rename to fle/env/factorio/tools/agent/get_research_progress/client.py index e10c3f3f9..6ec6ddd4e 100644 --- a/fle/env/tools/agent/get_research_progress/client.py +++ b/fle/env/factorio/tools/agent/get_research_progress/client.py @@ -1,7 +1,7 @@ from typing import Optional, List from fle.env import Ingredient from fle.env.game_types import Technology -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class GetResearchProgress(Tool): diff --git a/fle/env/factorio/tools/agent/get_research_progress/server.lua b/fle/env/factorio/tools/agent/get_research_progress/server.lua new file mode 100644 index 000000000..ba15fff0f --- /dev/null +++ b/fle/env/factorio/tools/agent/get_research_progress/server.lua @@ -0,0 +1,47 @@ +global.actions.get_research_progress = function(player_index, technology_name) + local player = global.agent_characters[player_index] + local force = player.force + + -- If no technology specified, use current research + if not technology_name then + if not force.current_research then + error("\"No research currently in progress\"") + end + technology_name = force.current_research.name + end + + -- Get the technology + local tech = force.technologies[technology_name] + if not tech then + error(string.format("\"Technology %s doesn't exist\"", technology_name)) + end + + -- Get progress info and calculate remaining work + local is_current = force.current_research and force.current_research.name == technology_name + local progress = 0 + local remaining_units = 0 + + if tech.researched then + -- Already researched technologies need no resources + return {} + elseif is_current then + progress = force.research_progress + -- Calculate remaining research units based on progress + remaining_units = math.ceil(tech.research_unit_count * (1 - progress)) + else + -- Not started technologies need all resources + remaining_units = tech.research_unit_count + end + + -- Collect remaining ingredients info + local ingredients = {} + for _, ingredient in pairs(tech.research_unit_ingredients) do + table.insert(ingredients, { + name = "\""..ingredient.name.."\"", + count = ingredient.amount * remaining_units, + type = ingredient.type + }) + end + + return ingredients +end \ No newline at end of file diff --git a/fle/env/tools/agent/get_resource_patch/agent.md b/fle/env/factorio/tools/agent/get_resource_patch/agent.md similarity index 100% rename from fle/env/tools/agent/get_resource_patch/agent.md rename to fle/env/factorio/tools/agent/get_resource_patch/agent.md diff --git a/fle/env/tools/agent/get_resource_patch/client.py b/fle/env/factorio/tools/agent/get_resource_patch/client.py similarity index 98% rename from fle/env/tools/agent/get_resource_patch/client.py rename to fle/env/factorio/tools/agent/get_resource_patch/client.py index 03a488303..ad63002c2 100644 --- a/fle/env/tools/agent/get_resource_patch/client.py +++ b/fle/env/factorio/tools/agent/get_resource_patch/client.py @@ -2,7 +2,7 @@ from fle.env.entities import Position, ResourcePatch, BoundingBox from fle.env.game_types import Resource -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class GetResourcePatch(Tool): diff --git a/fle/env/factorio/tools/agent/get_resource_patch/server.lua b/fle/env/factorio/tools/agent/get_resource_patch/server.lua new file mode 100644 index 000000000..7b59db8e8 --- /dev/null +++ b/fle/env/factorio/tools/agent/get_resource_patch/server.lua @@ -0,0 +1,105 @@ +global.actions.get_resource_patch = function(player_index, resource, x, y, radius) + local player = global.agent_characters[player_index] + local position = {x = x, y = y} + local surface = player.surface + + local function render_box(box) + local left_bottom = {x=box.left_top.x, y=box.right_bottom.y} + local right_top = {y=box.left_top.y, x=box.right_bottom.x} + + rendering.clear() + rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 1, g = 0, b = 0}, surface = surface, radius = 0.5, filled = false, target = box.left_top, time_to_live = 60000} + rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 0, g = 1, b = 0}, surface = surface, radius = 0.5, filled = false, target = box.right_bottom, time_to_live = 60000} + rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 1, g = 0, b = 1}, surface = surface, radius = 0.5, filled = false, target = left_bottom, time_to_live = 60000} + rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 0, g = 1, b = 1}, surface = surface, radius = 0.5, filled = false, target = right_top, time_to_live = 60000} + + end + -- Function to expand bounding box + local function expand_bounding_box(box, pos) + box.left_top.x = math.min(box.left_top.x, pos.x) + box.left_top.y = math.min(box.left_top.y, pos.y) + box.right_bottom.x = math.max(box.right_bottom.x, pos.x) + box.right_bottom.y = math.max(box.right_bottom.y, pos.y) + + + end + + -- Initialize bounding box + local bounding_box = {left_top = {x = x, y = y}, right_bottom = {x = x, y = y}} + + if resource == "water" then + local water_tiles = surface.find_tiles_filtered{position = position, name = "water", radius = radius} + if #water_tiles == 0 then + error("No water at the specified location.") + end + + local total_water_tiles = 0 + for _, tile in pairs(water_tiles) do + expand_bounding_box(bounding_box, tile.position) + total_water_tiles = total_water_tiles + 1 + end + bounding_box.left_top.x = bounding_box.left_top.x - 0.5 + bounding_box.left_top.y = bounding_box.left_top.y - 0.5 + bounding_box.right_bottom.y = bounding_box.right_bottom.y + 1.5 + bounding_box.right_bottom.x = bounding_box.right_bottom.x + 1.5 + render_box(bounding_box) + return {bounding_box = bounding_box, size = total_water_tiles} + elseif resource == "wood" then + local trees = surface.find_entities_filtered{ + position = position, + type = "tree", + radius = radius + } + if #trees == 0 then + error("No trees at the specified location.") + end + local total_wood = 0 + for _, tree in pairs(trees) do + expand_bounding_box(bounding_box, tree.position) + -- Estimate wood amount based on tree prototype + local tree_product = tree.prototype.mineable_properties.products[1] + if tree_product and tree_product.name == "wood" then + total_wood = total_wood + tree_product.amount + else + -- If wood amount is not specified, assume 1 wood per tree + total_wood = total_wood + 1 + end + end + render_box(bounding_box) + return {bounding_box = bounding_box, size = total_wood} + else + local resource_entities = surface.find_entities_filtered{position = position, name = resource, radius = radius} + if #resource_entities == 0 then + error("\"No resource of type " .. resource .. " at the specified location.\"") + end + + -- Recursive function to explore all connected resource entities + local function explore_resource_patch(entity, visited) + local key = entity.position.x .. "," .. entity.position.y + if visited[key] then + return 0 + end + visited[key] = true + expand_bounding_box(bounding_box, entity.position) + local resource_count = entity.amount + + local neighbors = surface.find_entities_filtered{ + area = {{entity.position.x - 1, entity.position.y - 1}, {entity.position.x + 1, entity.position.y + 1}}, + type = "resource", + name = resource + } + for _, neighbor in pairs(neighbors) do + if neighbor ~= entity then + resource_count = resource_count + explore_resource_patch(neighbor, visited) + end + end + return resource_count + end + + local visited = {} + local total_resource = explore_resource_patch(resource_entities[1], visited) + + render_box(bounding_box) + return {bounding_box = bounding_box, size = total_resource} + end +end \ No newline at end of file diff --git a/fle/env/tools/agent/harvest_resource/agent.md b/fle/env/factorio/tools/agent/harvest_resource/agent.md similarity index 100% rename from fle/env/tools/agent/harvest_resource/agent.md rename to fle/env/factorio/tools/agent/harvest_resource/agent.md diff --git a/fle/env/tools/agent/harvest_resource/client.py b/fle/env/factorio/tools/agent/harvest_resource/client.py similarity index 94% rename from fle/env/tools/agent/harvest_resource/client.py rename to fle/env/factorio/tools/agent/harvest_resource/client.py index 04f24cb46..6c13c4128 100644 --- a/fle/env/tools/agent/harvest_resource/client.py +++ b/fle/env/factorio/tools/agent/harvest_resource/client.py @@ -2,10 +2,10 @@ from fle.env.entities import Position from fle.env.game_types import Resource -from fle.env.tools.agent.get_entity.client import GetEntity -from fle.env.tools.agent.move_to.client import MoveTo -from fle.env.tools.agent.nearest.client import Nearest -from fle.env.tools import Tool +from fle.env.factorio.tools.agent.get_entity.client import GetEntity +from fle.env.factorio.tools.agent.move_to.client import MoveTo +from fle.env.factorio.tools.agent.nearest.client import Nearest +from fle.env.factorio.tools import Tool class HarvestResource(Tool): diff --git a/fle/env/factorio/tools/agent/harvest_resource/server.lua b/fle/env/factorio/tools/agent/harvest_resource/server.lua new file mode 100644 index 000000000..9cea09536 --- /dev/null +++ b/fle/env/factorio/tools/agent/harvest_resource/server.lua @@ -0,0 +1,622 @@ +--- global.actions.harvest_resource(player_index, x, y, count, radius) +local function calculate_mining_ticks(entity) + local mining_time = entity.prototype.mineable_properties.mining_time or 1 + -- Convert mining time (in seconds) to ticks (60 ticks per second) + return math.ceil(mining_time * 60) +end + +local function update_production_stats(force, entity_name, amount) + local stats = force.item_production_statistics + stats.on_flow(entity_name, amount) + if global.harvested_items[entity_name] then + global.harvested_items[entity_name] = global.harvested_items[entity_name] + amount + else + global.harvested_items[entity_name] = amount + end + end + +local function get_entity_yield(entity) + local yield = 0 + if entity.valid and entity.minable then + local products = entity.prototype.mineable_properties.products + for _, product in pairs(products) do + yield = yield + (product.amount or 1) + end + end + return yield +end + +-- Function to calculate distance between two points +local function distance(pos1, pos2) + return math.sqrt((pos1.x - pos2.x)^2 + (pos1.y - pos2.y)^2) +end + +-- Function to sort entities by distance from a given position +local function sort_entities_by_distance(entities, from_position) + table.sort(entities, function(a, b) + return distance(a.position, from_position) < distance(b.position, from_position) + end) + return entities +end + +-- Helper function to start mining an entity and track yields +local function start_mining_entity(player, entity) + if entity.valid and entity.minable then + --game.print("Starting mining entity " .. entity.name .. " at " .. serpent.line(entity.position)) + + -- First select the entity + + --player.selected = entity + + -- Then set mining state with position + if not player.mining_state.mining then + player.update_selected_entity(entity.position) + player.mining_state = { + mining = true, + position = entity.position + } + end + + + -- Calculate expected yield + local expected_yield = 0 + local products = entity.prototype.mineable_properties.products + for _, product in pairs(products) do + expected_yield = expected_yield + (product.amount or 1) + end + + return { + entity = entity, + start_tick = game.tick, + expected_yield = expected_yield + } + end + return nil +end + + +local function add_entities_to_queue(queue, entities, count) + local expected_yield = 0 + local added = 0 + + sort_entities_by_distance(entities, queue.mining_position) + + for _, entity in ipairs(entities) do + if entity.valid and entity.minable then + local yield = get_entity_yield(entity) + if expected_yield + yield <= count then + table.insert(queue.entities, entity) + expected_yield = expected_yield + yield + added = added + 1 + elseif expected_yield < count then + -- Add this entity even though it will exceed count + table.insert(queue.entities, entity) + expected_yield = expected_yield + yield + break + else + break + end + end + end + + return expected_yield +end + +script.on_nth_tick(15, function(event) + -- If no queues at all, just return + if not global.harvest_queues then return end + + for player_index, queue in pairs(global.harvest_queues) do + local player = global.agent_characters[player_index] + -- Skip if player not valid + if not player or not player.valid then goto continue end + + -- Already reached or exceeded our target? + if queue.total_yield >= queue.target_yield then + -- Remove this player's queue + global.harvest_queues[player_index] = nil + goto continue + end + + -- Check if player is still in resource reach distance + local dist_x = player.position.x - queue.mining_position.x + local dist_y = player.position.y - queue.mining_position.y + local sq_dist = (dist_x * dist_x) + (dist_y * dist_y) + local sq_reach = (player.resource_reach_distance * player.resource_reach_distance) + if sq_dist > sq_reach then + -- Too far away; do nothing for now + goto continue + end + + -- If there's no current mining, pick up the next entity + if not queue.current_mining then + local next_entity = table.remove(queue.entities, 1) + if not next_entity then + -- No more entities left + global.harvest_queues[player_index] = nil + goto continue + end + + -- Start mining + queue.current_mining = { + entity = next_entity, + start_tick = game.tick + } + else + -- We have a current entity being mined + local entity = queue.current_mining.entity + if not entity or not entity.valid or not entity.minable then + -- Entity no longer valid, skip + queue.current_mining = nil + goto continue + end + + local ticks_mining = game.tick - queue.current_mining.start_tick + if ticks_mining >= 30 then + -- Time to finish mining + local inv_before = player.get_main_inventory().get_contents() + local mined_ok = player.mine_entity(entity) -- Instantly mines & adds items + if mined_ok then + local inv_after = player.get_main_inventory().get_contents() + + -- Figure out how many items we actually gained + local items_added = 0 + for name, after_count in pairs(inv_after) do + local before_count = inv_before[name] or 0 + items_added = items_added + (after_count - before_count) + end + + if items_added > 0 then + -- Add to our queue's total_yield + local new_total = queue.total_yield + items_added + + if new_total > queue.target_yield then + -- We overshot. Remove the extras from the player's inventory. + local overshoot = new_total - queue.target_yield + -- We'll try to remove it from whatever items were gained. + -- If multiple resource types might drop, you'd handle them individually. + + local overshoot_left = overshoot + for name, after_count in pairs(inv_after) do + local before_count = inv_before[name] or 0 + local gained_this_item = (after_count - before_count) + if gained_this_item > 0 then + local to_remove = math.min(overshoot_left, gained_this_item) + local actually_removed = player.remove_item({name = name, count = to_remove}) + overshoot_left = overshoot_left - actually_removed + if overshoot_left <= 0 then + break + end + end + end + new_total = queue.target_yield + end + + queue.total_yield = new_total + end + end + + -- Clear current mining + queue.current_mining = nil + end + end + ::continue:: + end +end) + + +local function check_inventory_space(player, entity, count) + -- Get player's main inventory + local inventory = player.get_main_inventory() + if not inventory then return false end + + -- Create a temporary inventory to test insertion + local test_inventory = game.create_inventory(1) + local can_insert = true + + -- Calculate total items that would be added + local total_items = 0 + local products = entity.prototype.mineable_properties.products + for _, product in pairs(products) do + local amount = (product.amount or 1) * count + -- Try to insert into test inventory + local test_stack = {name = product.name, count = amount} + if not inventory.can_insert(test_stack) then + can_insert = false + break + end + total_items = total_items + amount + end + + -- Clean up test inventory + test_inventory.destroy() + + if not can_insert then + error("Inventory is full") + end + + return true +end + + +local function find_entity_type_at_position(surface, position) + local exact_entities = surface.find_entities_filtered{ + position = position, + type = {"tree", "resource", "simple-entity"},--, "optimized-decorative"}, + radius = 1 -- Tiny radius for exact position check + } + + if #exact_entities > 0 then + -- game.print("Found type ".. exact_entities[1].name) + return exact_entities[1].type, exact_entities[1].name + end + return nil, nil +end + +local function harvest_specific_resources(player, surface, position, count, target_type, target_name) + -- game.print("Harvesting specific resources") + -- Use existing harvest/harvest_trees functions but filtered to specific type + local radius = player.resource_reach_distance + local entities = surface.find_entities_filtered{ + position = position, + radius = radius, + type = target_type, + name = target_name, + limit = count + } + + if #entities == 0 then + error("No matching resources found within range") + end + + -- Use your existing harvest functions based on type + if target_type == "tree" then + return harvest_trees(entities, count, position, player) + else + return harvest(entities, count, position, player) + end +end + + +local function find_entities_at_position(surface, position, entity_types, exact) + local radius = exact and 0.1 or nil -- Use tiny radius for exact position check + return surface.find_entities_filtered{ + position = position, + type = entity_types, + radius = radius + } +end + +local function begin_mining(queue, player) + if #queue.entities > 0 then + local first_entity = table.remove(queue.entities, 1) + queue.current_mining = start_mining_entity(player, first_entity) + if not queue.current_mining then + error("Failed to start mining entity") + end + end +end + +local function initialize_harvest_queue(player_index, position, target_yield) + if not global.harvest_queues then + global.harvest_queues = {} + end + + global.harvest_queues[player_index] = { + entities = {}, + mining_position = position, + total_mined = 0, + total_yield = 0, + current_mining = nil, + target_yield = target_yield + } + + return global.harvest_queues[player_index] +end + + +local function harvest_resource_slow(player, player_index, surface, position, count) + local exact_entities = find_entities_at_position(surface, position, {"tree", "resource"}, true) + + if #exact_entities > 0 then + local queue = initialize_harvest_queue(player_index, position, count) + local expected_yield = add_entities_to_queue(queue, exact_entities, count) + begin_mining(queue, player) + return expected_yield + end + + local radius_entities = find_entities_at_position(surface, position, {"tree", "resource"}, false) + if #radius_entities == 0 then + error("No harvestable entities found within range") + end + + local queue = initialize_harvest_queue(player_index, position, count) + local expected_yield = add_entities_to_queue(queue, radius_entities, count) + -- game.print("expected "..expected_yield) + begin_mining(queue, player) + return expected_yield +end + +function harvest(entities, count, from_position, player) + if count == 0 then return 0 end + local yield = 0 + + -- Check inventory space first using the first valid entity as reference + local reference_entity = nil + for _, entity in ipairs(entities) do + if entity.valid and entity.minable then + reference_entity = entity + break + end + end + + if reference_entity then + check_inventory_space(player, reference_entity, count) + end + + + entities = sort_entities_by_distance(entities, from_position) + + ::start:: + local has_mined = false + for _, entity in ipairs(entities) do + if entity.valid and entity.minable then + + -- Calculate mining ticks before mining the entity + if global.fast then + global.elapsed_ticks = global.elapsed_ticks + calculate_mining_ticks(entity) + end + + local products = entity.prototype.mineable_properties.products + for _, product in pairs(products) do + local amount = product.amount or 1 + yield = yield + amount + entity.mine({ignore_minable=false, raise_destroyed=true}) + player.insert({name=product.name, count=amount}) + update_production_stats(player.force, product.name, amount) + has_mined = true + if yield >= count then break end + end + if yield >= count then break end + end + end + if has_mined == true and yield < count then + goto start + end + return yield +end + +function harvest_trees(entities, count, from_position, player) + -- game.print("Harvesting "..#entities.." trees") + if count == 0 then return 0 end + local yield = 0 + entities = sort_entities_by_distance(entities, from_position) + + for _, entity in ipairs(entities) do + if yield >= count then break end + if entity.valid and entity.type == "tree" then + + -- Calculate mining ticks before mining the tree + if global.fast then + global.elapsed_ticks = global.elapsed_ticks + calculate_mining_ticks(entity) + end + + local products = entity.prototype.mineable_properties.products + for _, product in pairs(products) do + if product.name == "wood" then + local amount = product.amount or 1 + player.insert({name="wood", count=amount}) + update_production_stats(player.force, "wood", amount) + yield = yield + amount + + local tree_position = entity.position + local tree_surface = entity.surface + local stump_name = entity.name.."-stump" + entity.destroy({raise_destroy=true}) + -- Try to create stump, if fails just continue + pcall(function() + tree_surface.create_entity({name=stump_name, position=tree_position}) + end) + end + end + end + end + return yield +end + +-- Add function to handle simple entities like rocks and tree stumps +local function harvest_simple_entities(entities, count, from_position, player) + if count == 0 then return 0 end + local yield = 0 + entities = sort_entities_by_distance(entities, from_position) + + for _, entity in ipairs(entities) do + if entity.valid and entity.minable then + -- Calculate mining ticks before mining the entity + if global.fast then + global.elapsed_ticks = global.elapsed_ticks + calculate_mining_ticks(entity) + end + + local products = entity.prototype.mineable_properties.products + for _, product in pairs(products) do + local amount = product.amount or 1 + yield = yield + amount + entity.mine({ignore_minable=false, raise_destroyed=true}) + player.insert({name=product.name, count=amount}) + update_production_stats(player.force, product.name, amount) + + if yield >= count then break end + end + if yield >= count then break end + end + end + return yield +end + + +global.actions.harvest_resource = function(player_index, x, y, count, radius) + local player = global.agent_characters[player_index] + if not player then + error("Player not found") + end + + local player_position = player.position + local position = {x=x, y=y} + + local distance = math.sqrt((position.x - player_position.x)^2 + (position.y - player_position.y)^2) + if distance > player.resource_reach_distance then + error("Nothing within reach to harvest") + end + + local surface = player.surface + local target_type, target_name = find_entity_type_at_position(surface, position) + + if not target_type then + error("Nothing within reach to harvest") + end + + --if not global.fast then + -- return harvest_resource_slow(player, player_index, surface, position, count, radius) + --end + + local total_yield = 0 + if target_type then + total_yield = total_yield + harvest_specific_resources(player, surface, position, count, target_type, target_name) + if total_yield >= count then + -- game.print("Harvested " .. total_yield .. " items of " .. target_name) + return total_yield + end + end + + -- Try harvesting each type in sequence if we haven't met our quota + if total_yield < count then + -- Try trees first + local tree_entities = surface.find_entities_filtered{ + position = position, + radius = radius, + type = "tree" + } + total_yield = total_yield + harvest_trees(tree_entities, count - total_yield, position, player) + end + + if total_yield < count then + -- Then try simple entities (rocks, stumps, etc.) + local simple_entities = surface.find_entities_filtered{ + position = position, + radius = radius, + type = "simple-entity" + } + total_yield = total_yield + harvest_simple_entities(simple_entities, count - total_yield, position, player) + end + + if total_yield < count then + -- Finally try standard resources + local mineable_entities = surface.find_entities_filtered{ + position = position, + radius = radius, + type = "resource" + } + total_yield = total_yield + harvest(mineable_entities, count - total_yield, position, player) + end + + if total_yield == 0 then + error("Nothing within reach to harvest") + else + -- game.print("Harvested resources yielding " .. total_yield .. " items") + return total_yield + end +end +-- +--global.actions.harvest_resource2 = function(player_index, x, y, count, radius) +-- local player = global.agent_characters[player_index] +-- if not player then +-- error("Player not found") +-- end +-- +-- local player_position = player.position +-- local position = {x=x, y=y} +-- +-- local distance = math.sqrt((position.x - player_position.x)^2 + (position.y - player_position.y)^2) +-- if distance > player.resource_reach_distance then +-- error("Nothing within reach to harvest") +-- end +-- local surface = player.surface +-- +-- -- Check what's under the player first +-- local target_type, target_name = find_entity_type_at_position(surface, position) +-- if not target_type then +-- error("Nothing within reach to harvest") +-- end +-- if not global.fast then +-- return harvest_resource_slow(player, player_index, surface, position, count, radius) +-- end +-- +-- +-- local total_yield = 0 +-- if target_type then +-- -- If we found something at the exact position, harvest that specific type +-- total_yield = total_yield + harvest_specific_resources(player, surface, position, count, target_type, target_name) +-- if total_yield >= count then +-- game.print("Harvested " .. total_yield .. " items of " .. target_name) +-- return total_yield +-- end +-- end +-- +-- -- If nothing at exact position or couldn't get enough yield, fall back to original logic +-- local tree_entities = surface.find_entities_filtered{position=position, radius=radius, type = "tree"} +-- local tree_yield = harvest_trees(tree_entities, count - total_yield, position, player) +-- local total_yield = total_yield + tree_yield +-- +-- if tree_yield < count then +-- local mineable_entities = surface.find_entities_filtered{position=position, radius=radius, type = "resource"} +-- local resource_yield = harvest(mineable_entities, count - total_yield, position, player) +-- total_yield = total_yield + resource_yield +-- +-- if total_yield == 0 then +-- error("Could not harvest at position ("..position.x..", "..position.y..").") +-- end +-- end +-- +-- if total_yield == 0 then +-- error("Nothing within reach to harvest") +-- else +-- game.print("Harvested resources yielding " .. total_yield .. " items") +-- return total_yield +-- end +--end + + +global.actions.clear_harvest_queue = function(player_index) + if global.harvest_queues and global.harvest_queues[player_index] then + global.harvest_queues[player_index] = nil + end +end + +global.actions.get_harvest_queue_length = function(player_index) + if global.harvest_queues and global.harvest_queues[player_index] then + return #global.harvest_queues[player_index].entities + end + return 0 +end + +global.actions.get_resource_name_at_position = function(player_index, x, y) + local player = global.agent_characters[player_index] + if not player then + error("Player not found") + end + + local position = {x=x, y=y} + local surface = player.surface + + local entities = surface.find_entities_filtered{ + position = position, + radius = player.resource_reach_distance, + type = {"tree", "resource"}, + limit = 1 + } + local entity_name = nil + if #entities > 0 then + entity_name = entities[1].name + end + return entity_name +end \ No newline at end of file diff --git a/fle/env/tools/agent/insert_item/agent.md b/fle/env/factorio/tools/agent/insert_item/agent.md similarity index 100% rename from fle/env/tools/agent/insert_item/agent.md rename to fle/env/factorio/tools/agent/insert_item/agent.md diff --git a/fle/env/tools/agent/insert_item/client.py b/fle/env/factorio/tools/agent/insert_item/client.py similarity index 97% rename from fle/env/tools/agent/insert_item/client.py rename to fle/env/factorio/tools/agent/insert_item/client.py index bd69b4b15..8a271836b 100644 --- a/fle/env/tools/agent/insert_item/client.py +++ b/fle/env/factorio/tools/agent/insert_item/client.py @@ -3,8 +3,8 @@ from fle.env.entities import Entity, EntityGroup, Position, BeltGroup, PipeGroup from fle.env.game_types import Prototype -from fle.env.tools.agent.get_entities.client import GetEntities -from fle.env.tools import Tool +from fle.env.factorio.tools.agent.get_entities.client import GetEntities +from fle.env.factorio.tools import Tool class InsertItem(Tool): diff --git a/fle/env/factorio/tools/agent/insert_item/server.lua b/fle/env/factorio/tools/agent/insert_item/server.lua new file mode 100644 index 000000000..a98cf25be --- /dev/null +++ b/fle/env/factorio/tools/agent/insert_item/server.lua @@ -0,0 +1,244 @@ +-- Function to get inventory fullness information +local function get_inventory_info(entity) + if entity.get_inventory then + -- Try common inventory types + local inv = entity.get_inventory(defines.inventory.chest) or -- For chests + entity.get_inventory(defines.inventory.furnace_source) or -- For furnaces + entity.get_inventory(defines.inventory.assembling_machine_input) -- For assemblers + + if inv then + -- Get actual item count and inventory capacity + local item_count = 0 + for i = 1, #inv do + local stack = inv[i] + if stack and stack.valid_for_read then + item_count = item_count + stack.count + end + end + + -- Calculate total capacity (slots * stack size) + local capacity = #inv * game.item_prototypes[inv[1].name or "iron-plate"].stack_size + + return string.format("(%d/%d items)", item_count, capacity) + end + end + return "" +end + +global.actions.insert_item = function(player_index, insert_item, count, x, y, target_name) + local player = global.agent_characters[player_index] + local position = {x=x, y=y} + local surface = player.surface + + -- Check if player has enough items + local item_count = player.get_item_count(insert_item) + if item_count == 0 then + error('\"No '..insert_item..' to insert from your inventory\"') + end + + local closest_distance = math.huge + local closest_entity = nil + local area = {{position.x - 1, position.y - 1}, {position.x + 1, position.y + 1}} + local buildings = nil + + if target_name then + buildings = surface.find_entities_filtered{area = area, name=target_name} + else + buildings = surface.find_entities_filtered{area = area} + end + + -- Function to get inventory fullness information + --local function get_inventory_info(entity) + -- if entity.get_inventory then + -- local inv = entity.get_inventory(defines.inventory.chest) + -- if inv then + -- return string.format("(%d/%d)", #inv, #inv.get_bar()) + -- end + -- end + -- return "" + --end + + -- Function to check if an item can be inserted into an entity + local function can_insert_item(entity, item_name) + if entity.type == "transport-belt" then + -- All items that can be on ground can be on belts + local item_prototype = game.item_prototypes[item_name] + return item_prototype and not item_prototype.has_flag("only-in-cursor") + + elseif entity.type == "lab" then + -- Check if the item is a science pack + local item_prototype = game.item_prototypes[item_name] + return item_prototype and item_prototype.type == "tool" + + elseif entity.type == "assembling-machine" then + local recipe = entity.get_recipe() + if recipe then + -- Check if the item is an ingredient or the result of the recipe + for _, ingredient in pairs(recipe.ingredients) do + if ingredient.name == item_name then + return true + end + end + -- Check if the item is the result of the recipe + for _, product in pairs(recipe.products) do + if product.name == item_name then + return true + end + end + return false + end + elseif entity.type == "furnace" then + -- Check if it's a fuel + if game.item_prototypes[item_name].fuel_value > 0 then + return true + end + -- Check furnace inventory for incompatible items + local inventory = entity.get_inventory(defines.inventory.furnace_source) + if inventory and not inventory.is_empty() then + local existing_item = nil + for i = 1, #inventory do + local stack = inventory[i] + if stack.valid_for_read then + existing_item = stack.name + break + end + end + if existing_item and existing_item ~= item_name then + error("\"furnace already contains " .. existing_item.." so cannot insert " .. item_name .."\"") + end + end + -- Check if it's a valid ingredient for any furnace recipe + for _, recipe in pairs(game.recipe_prototypes) do + if recipe.category == "smelting" then + for _, ingredient in pairs(recipe.ingredients) do + if ingredient.name == item_name then + return true + end + end + end + end + return false + ---- Check if it's a fuel + --if game.item_prototypes[item_name].fuel_value > 0 then + -- return true + --end + ---- Check if it's a valid ingredient for any furnace recipe + --for _, recipe in pairs(game.recipe_prototypes) do + -- if recipe.category == "smelting" then + -- for _, ingredient in pairs(recipe.ingredients) do + -- if ingredient.name == item_name then + -- return true + -- end + -- end + -- end + --end + --return false + elseif entity.burner then + -- Check if it's a fuel + return game.item_prototypes[item_name].fuel_value > 0 + elseif entity.type == "container" or entity.type == "logistic-container" then + return true -- Containers can accept any item + end + -- Add more entity types as needed + return true + end + + -- Find the closest suitable building + for _, building in ipairs(buildings) do + if building.name ~= 'character' and can_insert_item(building, insert_item) then + local distance = ((position.x - building.position.x) ^ 2 + (position.y - building.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest_entity = building + end + end + end + + if closest_entity == nil then + error("\"Could not find a nearby entity that can accept " .. insert_item.."\"") + end + + -- Throw an error if the entity is too far away from the player + if closest_distance > 10 then + error("\"Entity at ("..closest_entity.position.x..", "..closest_entity.position.y..") is too far away from your position of ("..player.position.x..", "..player.position.y.."), move closer.\"") + end + + -- Function to insert items onto a transport belt - one at a time + local function insert_on_belt(belt, item_name) + local line1 = belt.get_transport_line(1) + local line2 = belt.get_transport_line(2) + + -- Try first line + if line1.can_insert_at_back() then + if line1.insert_at_back({name = item_name, count = 1}) then + return 1 + end + end + + -- If first line failed, try second line + if line2.can_insert_at_back() then + if line2.insert_at_back({name = item_name, count = 1}) then + return 1 + end + end + + return 0 -- Could not insert on either line + end + + -- Determine how many items can be inserted + local insertable_count = math.min(count, item_count) + + -- Attempt to insert items + local inserted = 0 + if closest_entity.type == "transport-belt" then + -- For transport belts, we need to use a different method + -- game.print("Inserting ".. insertable_count.. " items onto transport belt...") + inserted = insert_on_belt(closest_entity, insert_item) + elseif closest_entity.type == "assembling-machine" then + local recipe = closest_entity.get_recipe() + if recipe then + local is_product = false + for _, product in pairs(recipe.products) do + if product.name == insert_item then + is_product = true + break + end + end + + if is_product then + -- Insert into output inventory + inserted = closest_entity.get_output_inventory().insert({name=insert_item, count=insertable_count}) + else + -- Insert into input inventory + inserted = closest_entity.get_inventory(defines.inventory.assembling_machine_input).insert({name=insert_item, count=insertable_count}) + end + else + error("No recipe set for the assembling machine.") + end + else + -- For other entities, use the normal insert method + inserted = closest_entity.insert{name=insert_item, count=insertable_count} + end + + -- game.print("Inserted " .. inserted .. " items.") + if inserted > 0 then + -- Only remove successfully inserted items from player + player.remove_item{name=insert_item, count=inserted} + -- game.print("Successfully inserted " .. inserted .. " items.") + return global.utils.serialize_entity(closest_entity) + else + local inventory_info = get_inventory_info(closest_entity) + local error_msg = string.format( + "\"Failed to insert %s into %s (type %s) at position %s. " .. + "Attempted to insert %d items. %s %s\"", + insert_item, + closest_entity.name, + closest_entity.type, + serpent.line(closest_entity.position), + insertable_count, + inventory_info ~= "" and "Inventory is full " .. inventory_info or "Entity might not accept this item or has no available space.", + inventory_info + ) + error(error_msg) + end +end \ No newline at end of file diff --git a/fle/env/tools/agent/inspect_inventory/agent.md b/fle/env/factorio/tools/agent/inspect_inventory/agent.md similarity index 100% rename from fle/env/tools/agent/inspect_inventory/agent.md rename to fle/env/factorio/tools/agent/inspect_inventory/agent.md diff --git a/fle/env/tools/agent/inspect_inventory/client.py b/fle/env/factorio/tools/agent/inspect_inventory/client.py similarity index 97% rename from fle/env/tools/agent/inspect_inventory/client.py rename to fle/env/factorio/tools/agent/inspect_inventory/client.py index 059f38a86..5413e7709 100644 --- a/fle/env/tools/agent/inspect_inventory/client.py +++ b/fle/env/factorio/tools/agent/inspect_inventory/client.py @@ -1,7 +1,7 @@ from typing import List, Union from fle.env import Inventory, Entity, Position -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class InspectInventory(Tool): diff --git a/fle/env/tools/agent/inspect_inventory/server.lua b/fle/env/factorio/tools/agent/inspect_inventory/server.lua similarity index 100% rename from fle/env/tools/agent/inspect_inventory/server.lua rename to fle/env/factorio/tools/agent/inspect_inventory/server.lua diff --git a/fle/env/tools/agent/launch_rocket/agent.md b/fle/env/factorio/tools/agent/launch_rocket/agent.md similarity index 100% rename from fle/env/tools/agent/launch_rocket/agent.md rename to fle/env/factorio/tools/agent/launch_rocket/agent.md diff --git a/fle/env/tools/agent/launch_rocket/client.py b/fle/env/factorio/tools/agent/launch_rocket/client.py similarity index 89% rename from fle/env/tools/agent/launch_rocket/client.py rename to fle/env/factorio/tools/agent/launch_rocket/client.py index 7f8bf67f7..869d99efb 100644 --- a/fle/env/tools/agent/launch_rocket/client.py +++ b/fle/env/factorio/tools/agent/launch_rocket/client.py @@ -2,8 +2,8 @@ from fle.env.entities import Position, RocketSilo from fle.env.game_types import Prototype -from fle.env.tools.agent.get_entity.client import GetEntity -from fle.env.tools import Tool +from fle.env.factorio.tools.agent.get_entity.client import GetEntity +from fle.env.factorio.tools import Tool class LaunchRocket(Tool): diff --git a/fle/env/factorio/tools/agent/launch_rocket/server.lua b/fle/env/factorio/tools/agent/launch_rocket/server.lua new file mode 100644 index 000000000..5da6ead5f --- /dev/null +++ b/fle/env/factorio/tools/agent/launch_rocket/server.lua @@ -0,0 +1,44 @@ +-- Function to find a rocket silo at the given position +local function find_rocket_silo(surface, position) + local silo = surface.find_entities_filtered{ + name = "rocket-silo", + position = position, + limit = 1, + radius=1000 + } + return silo[1] +end + +-- Function to check if the silo has a rocket ready to launch +local function is_rocket_ready(silo) + if not silo then return false end + if not silo.valid then return false end + + -- Check if rocket is ready for launch + return silo.rocket_silo_status == defines.rocket_silo_status.rocket_ready +end + +-- Function to launch rocket from specified position +global.actions.launch_rocket = function(x, y) + -- Get the current game surface + local surface = game.surfaces[1] + local position = {x=x, y=y} + -- Find rocket silo at the given position + local silo = find_rocket_silo(surface, position) + + if not silo then + game.print("No rocket silo found at specified position") + return false + end + + -- Check if silo has a rocket ready + if not is_rocket_ready(silo) then + game.print("Rocket is not ready for launch") + return false + end + + -- Launch the rocket + silo.launch_rocket() + game.print("Rocket launched successfully!") + return true +end \ No newline at end of file diff --git a/fle/env/tools/agent/move_to/agent.md b/fle/env/factorio/tools/agent/move_to/agent.md similarity index 100% rename from fle/env/tools/agent/move_to/agent.md rename to fle/env/factorio/tools/agent/move_to/agent.md diff --git a/fle/env/tools/agent/move_to/client.py b/fle/env/factorio/tools/agent/move_to/client.py similarity index 94% rename from fle/env/tools/agent/move_to/client.py rename to fle/env/factorio/tools/agent/move_to/client.py index 0dee2665d..427ac8f20 100644 --- a/fle/env/tools/agent/move_to/client.py +++ b/fle/env/factorio/tools/agent/move_to/client.py @@ -4,9 +4,9 @@ from fle.env.entities import Position from fle.env.instance import NONE from fle.env.game_types import Prototype -from fle.env.tools.admin.get_path.client import GetPath -from fle.env.tools.admin.request_path.client import RequestPath -from fle.env.tools import Tool +from fle.env.factorio.tools.admin.get_path.client import GetPath +from fle.env.factorio.tools.admin.request_path.client import RequestPath +from fle.env.factorio.tools import Tool from fle.env.lua_manager import LuaScriptManager diff --git a/fle/env/factorio/tools/agent/move_to/server.lua b/fle/env/factorio/tools/agent/move_to/server.lua new file mode 100644 index 000000000..a03dce61f --- /dev/null +++ b/fle/env/factorio/tools/agent/move_to/server.lua @@ -0,0 +1,265 @@ +-- move_to + +-- Register the tick handler when the module is loaded +if not global.fast then + script.on_nth_tick(5, function(event) + if global.walking_queues then + global.actions.update_walking_queues() + end + end) +end + +--local function get_direction(from_pos, to_pos) +-- local dx = to_pos.x - from_pos.x +-- local dy = to_pos.y - from_pos.y +-- if dx == 0 and dy == 0 then +-- return nil +-- elseif math.abs(dx) > math.abs(dy) then +-- return dx > 0 and defines.direction.east or defines.direction.west +-- else +-- return dy > 0 and defines.direction.south or defines.direction.north +-- end +--end + + +global.actions.move_to = function(player_index, path_handle, trailing_entity, is_trailing) + --local player = global.agent_characters[player_index] + local player = global.agent_characters[player_index] + local path = global.paths[path_handle] + local surface = player.surface + + -- Check if path is valid + if not path or type(path) ~= "table" or #path == 0 then + error("Invalid path: " .. serpent.line(path)) + end + + -- If fast mode is disabled, set up walking queue + if not global.fast then + -- Initialize walking queue if it doesn't exist + if not global.walking_queues then + global.walking_queues = {} + end + + -- Create or clear existing queue for this player + if not global.walking_queues[player_index] then + global.walking_queues[player_index] = { + positions = {}, + current_target = nil, + trailing_entity = trailing_entity, + is_trailing = is_trailing + } + else + global.walking_queues[player_index].positions = {} + global.walking_queues[player_index].current_target = nil + global.walking_queues[player_index].trailing_entity = trailing_entity + global.walking_queues[player_index].is_trailing = is_trailing + end + + -- Add all path positions to the queue + for _, point in ipairs(path) do + table.insert(global.walking_queues[player_index].positions, point.position) + end + + -- Start walking to first position + if #global.walking_queues[player_index].positions > 0 then + local target = global.walking_queues[player_index].positions[1] + global.walking_queues[player_index].current_target = target + player.walking_state = { + walking = true, + direction = global.utils.get_direction(player.position, target) + } + end + + return player.position + end + + local function rotate_entity(entity, direction) + local direction_map = {defines.direction.north, defines.direction.east, defines.direction.south, defines.direction.west} + local inserter_direction_map = {defines.direction.south, defines.direction.west, defines.direction.north, defines.direction.east} + + if entity.type == "inserter" then + orientation = inserter_direction_map[direction/2+1] + else + orientation = direction_map[direction/2+1] + end + + while entity.direction ~= orientation do + entity.rotate() + end + end + + local function place(place_position, direction) + if global.utils.can_place_entity(player, trailing_entity, place_position, direction) then + if player.get_item_count(trailing_entity) > 0 then + local created = surface.create_entity{name=trailing_entity, position=place_position, direction=direction, force='player', player=player, build_check_type=defines.build_check_type.manual, fast_replace=true} + if created then + player.remove_item({name=trailing_entity, count=1}) + end + return created + else + error("\"No ".. trailing_entity .." in the inventory\"") + end + elseif surface.can_fast_replace{name=trailing_entity, position=place_position, direction=direction, force='player'} then + local existing_entity = surface.find_entity(trailing_entity, place_position) + if existing_entity and existing_entity.direction ~= direction then + rotate_entity(existing_entity, direction) + end + return existing_entity + end + return nil + end + + local function place_diagonal(from_pos, to_pos, is_leading) + local dx = to_pos.x - from_pos.x + local dy = to_pos.y - from_pos.y + local mid_pos = {x = from_pos.x , y = to_pos.y } + + local dir_x = dx > 0 and defines.direction.east or defines.direction.west + local dir_y = dy > 0 and defines.direction.south or defines.direction.north + + if is_leading then + place(to_pos, (dir_x + 4) % 8) + + local corner_dir + if (dx > 0 and dy > 0) or (dx < 0 and dy < 0) then + corner_dir = dir_x + else + corner_dir = dir_y + end + + if dx == 1 and dy == 1 then + corner_dir = defines.direction.east + end + + place(mid_pos, (corner_dir + 4) % 8) + else + place(from_pos, dir_y) + + local corner_dir + if (dx > 0 and dy > 0) or (dx < 0 and dy < 0) then + corner_dir = dir_y + else + corner_dir = dir_x + end + + if dx == 1 and dy == 1 then + corner_dir = defines.direction.east + end + + place(mid_pos, corner_dir) + end + end + + if is_trailing == 1 or is_trailing == 0 then + if game.entity_prototypes[trailing_entity] == nil then + error('No entity exists that can be laid') + end + end + + local prev_belt = nil + local prev_pos = player.position + for i = 1, #path do + local current_position = player.position + local target_position = path[i].position + + -- Calculate and accumulate movement ticks before teleporting + global.elapsed_ticks = global.elapsed_ticks + global.utils.calculate_movement_ticks(player, prev_pos, target_position) + + + local direction = global.utils.get_direction(prev_pos, target_position) + + if not direction then + goto continue + end + + local new_belt + if is_trailing == 1 then + if math.abs(prev_pos.x - target_position.x) == 1 and math.abs(prev_pos.y - target_position.y) == 1 then + --game.print("Placing diagonal belt at " .. serpent.line(prev_pos) .. " to " .. serpent.line(target_position)) + place_diagonal(prev_pos, target_position, false) + else + --game.print("Placing at direction: " .. direction .. " Current position: " .. serpent.line(prev_pos) .. " Target position: " .. serpent.line(target_position)) + new_belt = place(prev_pos, direction) + if prev_belt then + rotate_entity(prev_belt, global.utils.get_direction(prev_belt.position, prev_pos)) + end + end + player.teleport(target_position) + elseif is_trailing == 0 then + if math.abs(prev_pos.x - target_position.x) == 1 and math.abs(prev_pos.y - target_position.y) == 1 then + place_diagonal(prev_pos, target_position, true) + else + -- game.print("Placing at direction: " .. direction .. " Current position: " .. serpent.line(prev_pos) .. " Target position: " .. serpent.line(target_position)) + directions = {defines.direction.north, defines.direction.east, defines.direction.south, defines.direction.west} + opposite_direction = {defines.direction.south, defines.direction.west, defines.direction.north, defines.direction.east} + new_direction = opposite_direction[direction/2+1] + new_belt = place(target_position, new_direction) + if prev_belt then + rotate_entity(prev_belt, global.utils.get_direction(prev_belt.position, current_position)) + end + end + player.teleport(target_position) + else + player.teleport(target_position) + end + prev_belt = new_belt + prev_pos = target_position + ::continue:: + end + + return player.position +end + +-- Add this new function to handle the walking queue updates +-- This should be called on every tick +global.actions.update_walking_queues = function() + if not global.walking_queues then return end + + for player_index, queue in pairs(global.walking_queues) do + local player = global.agent_characters[player_index] + if not player or not queue.current_target then goto continue end + + local distance = ((player.position.x - queue.current_target.x)^2 + + (player.position.y - queue.current_target.y)^2)^0.5 + + -- If player is close enough to current target + if distance < 1 then + -- Remove the current position from queue + table.remove(queue.positions, 1) + + -- If there are more positions, start walking to next one + if #queue.positions > 0 then + queue.current_target = queue.positions[1] + player.walking_state = { + walking = true, + direction = global.utils.get_direction_with_diagonals(player.position, queue.current_target) + } + else + -- Queue is empty, stop walking + player.walking_state = {walking = false} + queue.current_target = nil + end + else + -- Update walking direction to current target + player.walking_state = { + walking = true, + direction = global.utils.get_direction_with_diagonals(player.position, queue.current_target) + } + end + + ::continue:: + end +end + +global.actions.clear_walking_queue = function(player_index) + if global.walking_queues and global.walking_queues[player_index] then + global.walking_queues[player_index] = nil + end +end + +global.actions.get_walking_queue_length = function(player_index) + if global.walking_queues and global.walking_queues[player_index] then + return #global.walking_queues[player_index].positions + end + return 0 +end \ No newline at end of file diff --git a/fle/env/tools/agent/nearest/agent.md b/fle/env/factorio/tools/agent/nearest/agent.md similarity index 100% rename from fle/env/tools/agent/nearest/agent.md rename to fle/env/factorio/tools/agent/nearest/agent.md diff --git a/fle/env/tools/agent/nearest/client.py b/fle/env/factorio/tools/agent/nearest/client.py similarity index 98% rename from fle/env/tools/agent/nearest/client.py rename to fle/env/factorio/tools/agent/nearest/client.py index 9a56d13fc..98ec2460e 100644 --- a/fle/env/tools/agent/nearest/client.py +++ b/fle/env/factorio/tools/agent/nearest/client.py @@ -2,7 +2,7 @@ from fle.env.entities import Position, ResourcePatch from fle.env.game_types import Prototype, Resource -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class Nearest(Tool): diff --git a/fle/env/factorio/tools/agent/nearest/server.lua b/fle/env/factorio/tools/agent/nearest/server.lua new file mode 100644 index 000000000..430c9a1d7 --- /dev/null +++ b/fle/env/factorio/tools/agent/nearest/server.lua @@ -0,0 +1,71 @@ +global.actions.nearest = function(player_index, resource) + local player = global.agent_characters[player_index] + + local function normalize_resource_name(resource) + if resource == "copper" then + return "copper-ore" + elseif resource == "iron" then + return "iron-ore" + elseif resource == "uranium" then + return "uranium-ore" + else + return resource + end + end + + local normalized_resource = normalize_resource_name(resource) + + local function find_nearest(player, resource) + local surface = player.surface + local position = player.position + local closest_distance = math.huge + local closest = nil + local entities + + if resource == "wood" then + entities = surface.find_entities_filtered{ + area = {{position.x - 500, position.y - 500}, {position.x + 500, position.y + 500}}, + type = "tree" + } + elseif resource == "water" then + local water_positions = surface.find_tiles_filtered{ + area = {{position.x - 500, position.y - 500}, {position.x + 500, position.y + 500}}, + name = "water" + } + for _, water_tile in ipairs(water_positions) do + local distance = ((position.x - water_tile.position.x) ^ 2 + (position.y - water_tile.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest = water_tile.position + end + end + if closest == nil then + error("\"Could not find an entity called "..resource.."\"") + end + --return { x = position.x - closest.x, y = position.y - closest.y } + return {x = closest.x, y = closest.y} + else + entities = surface.find_entities_filtered{ + area = {{position.x - 500, position.y - 500}, {position.x + 500, position.y + 500}}, + name = resource + } + end + + for _, entity in ipairs(entities) do + local distance = ((position.x - entity.position.x) ^ 2 + (position.y - entity.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest = entity.position + end + end + + if closest == nil then + error("\"Could not find an entity called "..resource.."\"") + end + -- game.print("Distance to "..resource.." is "..closest_distance) + return {x= closest.x, y = closest.y} + --return { x = position.x - closest.x, y = position.y - closest.y } + end + + return find_nearest(player, normalized_resource) +end diff --git a/fle/env/tools/agent/nearest_buildable/agent.md b/fle/env/factorio/tools/agent/nearest_buildable/agent.md similarity index 100% rename from fle/env/tools/agent/nearest_buildable/agent.md rename to fle/env/factorio/tools/agent/nearest_buildable/agent.md diff --git a/fle/env/tools/agent/nearest_buildable/client.py b/fle/env/factorio/tools/agent/nearest_buildable/client.py similarity index 98% rename from fle/env/tools/agent/nearest_buildable/client.py rename to fle/env/factorio/tools/agent/nearest_buildable/client.py index 53b3846fb..25198699f 100644 --- a/fle/env/tools/agent/nearest_buildable/client.py +++ b/fle/env/factorio/tools/agent/nearest_buildable/client.py @@ -1,6 +1,6 @@ from fle.env import entities as ent from fle.env.game_types import Prototype -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class NearestBuildable(Tool): diff --git a/fle/env/factorio/tools/agent/nearest_buildable/server.lua b/fle/env/factorio/tools/agent/nearest_buildable/server.lua new file mode 100644 index 000000000..c4c7ed83f --- /dev/null +++ b/fle/env/factorio/tools/agent/nearest_buildable/server.lua @@ -0,0 +1,189 @@ +-- Cache math functions +local floor = math.floor +local ceil = math.ceil +local max = math.max +local abs = math.abs + +global.actions.nearest_buildable = function(player_index, entity_name, bounding_box, center_position) + local player = global.agent_characters[player_index] + local surface = player.surface + local entity_prototype = game.entity_prototypes[entity_name] + local needs_resources = entity_prototype.resource_categories ~= nil + local start_pos = center_position or player.position + local needs_oil = entity_name == "pumpjack" + + -- Cache for chunk resources + local chunk_cache = {} + + local function get_chunk_resources(chunk_x, chunk_y) + local cache_key = chunk_x .. "," .. chunk_y + if not chunk_cache[cache_key] then + chunk_cache[cache_key] = surface.find_entities_filtered{ + area = { + {chunk_x * 32, chunk_y * 32}, + {(chunk_x + 1) * 32, (chunk_y + 1) * 32} + }, + collision_mask = "resource-layer" + } + end + return chunk_cache[cache_key] + end + + local function check_resource_coverage(left_top, right_bottom) + if not needs_resources then return true end + + if needs_oil then + local oil_count = surface.count_entities_filtered{ + area = {left_top, right_bottom}, + name = "crude-oil" + } + return oil_count > 0 + end + -- Quick initial resource count check + local total_resources = surface.count_entities_filtered{ + area = {left_top, right_bottom}, + collision_mask = "resource-layer" + } + + -- Calculate required coverage + local min_x = floor(left_top.x) + local min_y = floor(left_top.y) + local max_x = ceil(right_bottom.x) - 1 + local max_y = ceil(right_bottom.y) - 1 + local required_coverage = (max_x - min_x + 1) * (max_y - min_y + 1) + + -- Early exit if not enough resources + if total_resources < required_coverage then + return false + end + + -- Set up position tracking + local positions = {} + + -- Get relevant chunks + local chunk_min_x = floor(min_x / 32) + local chunk_min_y = floor(min_y / 32) + local chunk_max_x = floor(max_x / 32) + local chunk_max_y = floor(max_y / 32) + + -- Collect resources from relevant chunks + for chunk_x = chunk_min_x, chunk_max_x do + for chunk_y = chunk_min_y, chunk_max_y do + local resources = get_chunk_resources(chunk_x, chunk_y) + for _, resource in pairs(resources) do + local x = floor(resource.position.x) + local y = floor(resource.position.y) + if x >= min_x and x <= max_x and + y >= min_y and y <= max_y then + positions[x] = positions[x] or {} + positions[x][y] = true + end + end + end + end + + -- Verify complete coverage + for x = min_x, max_x do + if not positions[x] then return false end + for y = min_y, max_y do + if not positions[x][y] then return false end + end + end + + return true + end + + local function is_buildable_box(origin, box_dimensions) + -- Calculate actual positions + local left_top = { + x = origin.x + box_dimensions.left_top.x, + y = origin.y + box_dimensions.left_top.y + } + local right_bottom = { + x = origin.x + box_dimensions.right_bottom.x, + y = origin.y + box_dimensions.right_bottom.y + } + + -- Quick collision checks first + if surface.count_tiles_filtered{ + area = {left_top, right_bottom}, + collision_mask = "water-tile" + } > 0 then + return false + end + + if surface.count_entities_filtered{ + area = {left_top, right_bottom}, + collision_mask = "object-layer" + } > 0 then + return false + end + + -- Resource coverage check + if not check_resource_coverage(left_top, right_bottom) then + return false + end + + return true, left_top, right_bottom + end + + local function spiral_search() + local dx, dy = 0, 0 + local segment_length = 1 + local segment_passed = 0 + local direction = 0 -- 0: right, 1: down, 2: left, 3: up + local MAX_RADIUS = 30 + + while max(abs(dx), abs(dy)) <= MAX_RADIUS do + local current_pos = { + x = start_pos.x + dx, + y = start_pos.y + dy + } + + if bounding_box then + local is_buildable, left_top, right_bottom = is_buildable_box(current_pos, bounding_box) + if is_buildable then + --rendering.clear() + rendering.draw_rectangle{ + only_in_alt_mode=true, + surface = surface, + left_top = left_top, + right_bottom = right_bottom, + filled = false, + color = {r=0, g=1, b=0, a=0.5}, + time_to_live = 60000 + } + return {position=current_pos, left_top=left_top, right_bottom=right_bottom} + end + else + -- Simple position check for entities without bounding box + if surface.count_entities_filtered{ + area = {{current_pos.x, current_pos.y}, + {current_pos.x + 1, current_pos.y + 1}}, + collision_mask = needs_resources and "resource-layer" or "object-layer" + } == (needs_resources and 1 or 0) then + return current_pos + end + end + + -- Spiral pattern movement + segment_passed = segment_passed + 1 + if direction == 0 then dx = dx + 1 + elseif direction == 1 then dy = dy + 1 + elseif direction == 2 then dx = dx - 1 + else dy = dy - 1 end + + if segment_passed == segment_length then + segment_passed = 0 + direction = (direction + 1) % 4 + if direction % 2 == 0 then + segment_length = segment_length + 1 + end + end + end + + error("\"Could not find a buildable position for the entity: " .. entity_name.."\"") + end + + return spiral_search() +end diff --git a/fle/env/tools/agent/pickup_entity/agent.md b/fle/env/factorio/tools/agent/pickup_entity/agent.md similarity index 100% rename from fle/env/tools/agent/pickup_entity/agent.md rename to fle/env/factorio/tools/agent/pickup_entity/agent.md diff --git a/fle/env/tools/agent/pickup_entity/client.py b/fle/env/factorio/tools/agent/pickup_entity/client.py similarity index 98% rename from fle/env/tools/agent/pickup_entity/client.py rename to fle/env/factorio/tools/agent/pickup_entity/client.py index dffe076df..b76b234b8 100644 --- a/fle/env/tools/agent/pickup_entity/client.py +++ b/fle/env/factorio/tools/agent/pickup_entity/client.py @@ -2,7 +2,7 @@ from fle.env import entities as ent from fle.env.game_types import Prototype -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class PickupEntity(Tool): diff --git a/fle/env/factorio/tools/agent/pickup_entity/server.lua b/fle/env/factorio/tools/agent/pickup_entity/server.lua new file mode 100644 index 000000000..f5225e88d --- /dev/null +++ b/fle/env/factorio/tools/agent/pickup_entity/server.lua @@ -0,0 +1,154 @@ +global.actions.pickup_entity = function(player_index, x, y, entity) + local player = global.agent_characters[player_index] + local position = {x=x, y=y} + local surface = player.surface + local success = false + rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.25, filled = false, target = position, time_to_live = 12000} + + -- Debug print + -- game.print("Starting pickup attempt for " .. entity .. " at (" .. x .. ", " .. y .. ")") + + -- Function to check if player can receive items + local function can_receive_items(items_to_check) + local main_inventory = player.get_main_inventory() + -- Check each item individually + for _, item in pairs(items_to_check) do + if not main_inventory.can_insert({name = item.name, count = item.count}) then + return false + end + end + return true + end + + -- Function to pick up and add entity to player's inventory + local function pickup_placed_entity(entities) + for _, ent in pairs(entities) do + if ent.valid and ent.name == entity then + -- game.print("Found valid placed entity: " .. ent.name) + + -- Collect all items that need to be inserted + local items_to_insert = {} + + -- Add entity products + --local products = ent.prototype.mineable_properties.products + --if products ~= nil then + -- for _, product in pairs(products) do + -- table.insert(items_to_insert, {name=product.name, count=product.amount}) + -- end + --end + + -- Add chest contents if applicable + if ent.get_inventory(defines.inventory.chest) then + for name, count in pairs(ent.get_inventory(defines.inventory.chest).get_contents()) do + table.insert(items_to_insert, {name=name, count=count}) + end + end + + -- Add transport belt contents if applicable + if ent.type == "transport-belt" then + -- Check line 1 + local line1 = ent.get_transport_line(1) + local contents1 = line1.get_contents() + for name, count in pairs(contents1) do + table.insert(items_to_insert, {name=name, count=count}) + end + + -- Check line 2 + local line2 = ent.get_transport_line(2) + local contents2 = line2.get_contents() + for name, count in pairs(contents2) do + table.insert(items_to_insert, {name=name, count=count}) + end + end + + -- Add the entity itself + table.insert(items_to_insert, {name=ent.name, count=1}) + + -- Check if player can receive all items + if not can_receive_items(items_to_insert) then + error("Inventory is full") + end + + -- Insert all items + for _, item in pairs(items_to_insert) do + player.insert(item) + end + + if ent.can_be_destroyed() then + -- game.print("Picked up placed "..ent.name) + pcall(ent.destroy{raise_destroy=false, do_cliff_correction=false}) + return true + end + end + end + return false + end + + -- Function to pick up items on ground + local function pickup_ground_item(ground_items) + for _, item in pairs(ground_items) do + if item.valid and item.stack and item.stack.name == entity then + -- game.print("Found valid ground item: " .. item.stack.name) + local count = item.stack.count + + -- Check if player can receive the item + if not can_receive_items({{name=entity, count=count}}) then + error("Cannot pick up " .. entity .. " - inventory is full") + end + + local inserted = player.insert{name=entity, count=count} + if inserted > 0 then + -- game.print("Picked up ground item2 " .. count .. " " .. entity) + pcall(item.destroy{raise_destroy=false}) + return true + end + end + return true + end + return false + end + + -- Find both types of entities first + local player_entities = surface.find_entities_filtered{ + name=entity, + position=position, + radius=0.707, + force="player" + } + -- game.print("Found " .. #player_entities .. " placed entities") + + local ground_items = surface.find_entities_filtered{ + name="item-on-ground", + position=position, + radius=0.707 + } + -- game.print("Found " .. #ground_items .. " ground items") + + -- Try to pick up placed entities first, if any exist + if #player_entities > 0 then + success = pickup_placed_entity(player_entities) + if success then + -- game.print("Successfully picked up placed entity") + return {} + end + end + + -- Only try ground items if we haven't succeeded with placed entities + if not success and #ground_items > 0 then + success = pickup_ground_item(ground_items) + if success then + -- game.print("Successfully picked up ground item") + return {} + end + end + + if not success then + if #player_entities == 0 and #ground_items == 0 then + error("Couldn't find "..entity.." at position ("..x..", "..y..") to pick up.") + else + error("Could not pick up "..entity) + end + end + + return {} +end \ No newline at end of file diff --git a/fle/env/tools/agent/place_entity/agent.md b/fle/env/factorio/tools/agent/place_entity/agent.md similarity index 100% rename from fle/env/tools/agent/place_entity/agent.md rename to fle/env/factorio/tools/agent/place_entity/agent.md diff --git a/fle/env/tools/agent/place_entity/client.py b/fle/env/factorio/tools/agent/place_entity/client.py similarity index 95% rename from fle/env/tools/agent/place_entity/client.py rename to fle/env/factorio/tools/agent/place_entity/client.py index 663034a99..1fedfd9a7 100644 --- a/fle/env/tools/agent/place_entity/client.py +++ b/fle/env/factorio/tools/agent/place_entity/client.py @@ -3,9 +3,9 @@ from fle.env.entities import Position, Entity from fle.env import DirectionInternal, Direction from fle.env.game_types import Prototype -from fle.env.tools.agent.get_entity.client import GetEntity -from fle.env.tools.agent.pickup_entity.client import PickupEntity -from fle.env.tools import Tool +from fle.env.factorio.tools.agent.get_entity.client import GetEntity +from fle.env.factorio.tools.agent.pickup_entity.client import PickupEntity +from fle.env.factorio.tools import Tool class PlaceObject(Tool): diff --git a/fle/env/factorio/tools/agent/place_entity/server.lua b/fle/env/factorio/tools/agent/place_entity/server.lua new file mode 100644 index 000000000..a954efca5 --- /dev/null +++ b/fle/env/factorio/tools/agent/place_entity/server.lua @@ -0,0 +1,348 @@ +-- Helper to convert surface direction to entity direction +local function surface_to_entity_direction(surface_dir) + -- In Factorio, offshore pumps face opposite to placement direction + local direction_map = { + [defines.direction.north] = defines.direction.north, -- 0 -> 4 + [defines.direction.east] = defines.direction.east, -- 2 -> 6 + [defines.direction.south] = defines.direction.south, -- 4 -> 0 + [defines.direction.west] = defines.direction.west -- 6 -> 2 + } + return direction_map[surface_dir] +end + +local function find_offshore_pump_position(player, center_pos) + local max_radius = 20 + local search_positions = { + {dx = 0, dy = 1, dir = defines.direction.north}, + {dx = 1, dy = 0, dir = defines.direction.west}, + {dx = 0, dy = -1, dir = defines.direction.south}, + {dx = -1, dy = 0, dir = defines.direction.east} + } + + for radius = 1, max_radius do + for y = -radius, radius do + for x = -radius, radius do + if math.abs(x) == radius or math.abs(y) == radius then + local check_pos = { + x = center_pos.x + x, + y = center_pos.y + y + } + + -- Check if position is already occupied + local entities = player.surface.find_entities_filtered{ + position = check_pos, + collision_mask = "player-layer", + invert = false + } + + if #entities == 0 then + local current_tile = player.surface.get_tile(check_pos.x, check_pos.y) + + if not is_water_tile(current_tile.name) then + for _, search in ipairs(search_positions) do + local water_pos = { + x = check_pos.x + search.dx, + y = check_pos.y + search.dy + } + + -- Check for entities at water position + local water_entities = player.surface.find_entities_filtered{ + position = water_pos, + collision_mask = "water-tile", + invert = true + } + + if #water_entities == 0 then + local adjacent_tile = player.surface.get_tile(water_pos.x, water_pos.y) + + if is_water_tile(adjacent_tile.name) then + local entity_dir = surface_to_entity_direction(search.dir) + local placement = { + name = "offshore-pump", + position = check_pos, + direction = entity_dir, + force = "player" + } + + if player.surface.can_place_entity(placement) then + -- Final collision check for the exact pump dimensions + local final_check = player.surface.find_entities_filtered{ + area = {{check_pos.x - 0.5, check_pos.y - 0.5}, + {check_pos.x + 0.5, check_pos.y + 0.5}}, + collision_mask = "player-layer" + } + + if #final_check == 0 then + return { + position = check_pos, + direction = entity_dir + } + end + end + end + end + end + end + end + end + end + end + end + + return nil +end + +global.actions.place_entity = function(player_index, entity, direction, x, y, exact) + local player = global.agent_characters[player_index] + local position = {x = x, y = y} + + if not direction then + direction = 0 + end + + local entity_direction = global.utils.get_entity_direction(entity, direction) + + -- Common validation functions + local function validate_distance() + local max_distance = player.reach_distance or player.build_distance + local dx = player.position.x - x + local dy = player.position.y - y or 0 + local distance = math.sqrt(dx * dx + dy * dy) + + if distance > max_distance then + error("\"The target position is too far away to place the entity. The player position is " .. + player.position.x .. ", " .. player.position.y .. + " and the target position is " .. x .. ", " .. y .. + ". The distance is " .. string.format("%.2f", distance) .. + " and the max distance is " .. max_distance .. ". Move closer.\"") + end + end + + local function validate_entity() + if game.entity_prototypes[entity] == nil then + local name = entity:gsub(" ", "_"):gsub("-", "_") + error("\""..name .. " isn't something that exists. Did you make a typo?\"") + end + end + + local function validate_inventory() + local count = player.get_item_count(entity) + if count == 0 then + local name = entity:gsub(" ", "_"):gsub("-", "_") + error("\"No " .. name .. " in inventory.\"") + end + end + + -- Slow placement implementation + local function slow_place() + -- Set cursor ghost + player.cursor_ghost = entity + + -- Select the target position + player.update_selected_entity(position) + + -- Schedule the actual placement after delay + script.on_nth_tick(60, function(event) -- 30 ticks = 0.5 seconds + script.on_nth_tick(60, nil) -- Clear the scheduled event + + -- Verify conditions are still valid + validate_distance() + validate_inventory() + + -- Avoid entity at target position + global.utils.avoid_entity(player_index, entity, position) + + -- Perform the actual placement + local placed_entity = player.surface.create_entity{ + name = entity, + force = "player", + position = position, + direction = entity_direction, + } + + if placed_entity then + player.remove_item{name = entity, count = 1} + player.cursor_ghost = nil -- Clear the ghost + return global.utils.serialize_entity(placed_entity) + else + error("\"Failed to place entity after delay\"") + end + end) + + return { pending = true } + end + + -- Fast placement implementation (existing logic) + local function fast_place() + local entity_prototype = game.entity_prototypes[entity] + + if entity == 'offshore-pump' then + exact = false + end + + -- Check for existing entity + if exact then + local existing_entity = player.surface.find_entity(entity, position) + if existing_entity then + error("\"entity already exists at the target position " .. serpent.line(existing_entity.position) .. " - remove this before continuing.\"" ) + end + + -- Get entity prototype's collision box + local collision_box = entity_prototype.collision_box + -- Calculate the area to check for water + local check_area = { + {position.x - collision_box.left_top.x/2, position.y - collision_box.left_top.y/2}, + {position.x + collision_box.right_bottom.x/2, position.y + collision_box.right_bottom.y/2} + } + rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = {x=position.x, y=position.y}, time_to_live = 12000} + + -- Check each tile in the entity's footprint for water + for x = math.floor(check_area[1][1]), math.ceil(check_area[2][1]) do + for y = math.floor(check_area[1][2]), math.ceil(check_area[2][2]) do + local tile = player.surface.get_tile(x, y) + if is_water_tile(tile.name) then + error("\"Cannot place " .. entity .. " at " .. position.x .. ", " .. position.y .. + " as entity footprint overlaps water. Please try a different location\"") + end + end + end + end + global.utils.avoid_entity(player_index, entity, position, direction) + -- Use surface based validation equivalent to LuaPlayer.can_place_entity + local can_build = global.utils.can_place_entity(player, entity, position, entity_direction) + + if not can_build then + if not exact then + local new_position + local new_position = nil + local found_position = false + -- special logic for orienting offshore pumps correctly. + if entity == 'offshore-pump' then + local pos_dir = find_offshore_pump_position(player, position) + entity_direction = global.utils.get_entity_direction(entity, pos_dir['direction']/2) + new_position = pos_dir['position'] + found_position = true + else + -- Existing search logic for nearby valid position + local radius = 1 + local max_radius = 10 + + while not found_position and radius <= max_radius do + for dx = -radius, radius do + for dy = -radius, radius do + if dx == -radius or dx == radius or dy == -radius or dy == radius then + new_position = {x = position.x + dx, y = position.y + dy} + global.utils.avoid_entity(player_index, entity, position, direction) + can_build = global.utils.can_place_entity(player, entity, new_position, entity_direction) + if can_build then + found_position = true + break + end + end + end + if found_position then break end + end + radius = radius + 1 + end + end + + if found_position then + local have_built = player.surface.create_entity{ + name = entity, + force = player.force, + position = new_position, + direction = entity_direction, + } + if have_built then + player.remove_item{name = entity, count = 1} + -- game.print("Placed " .. entity .. " at " .. new_position.x .. ", " .. new_position.y) + return global.actions.get_entity(player_index, entity, new_position.x, new_position.y) + end + else + error("\"Could not find a suitable position to place " .. entity .. " near the target location.\"") + end + else + -- Clear existing entities if exact placement is required + local area = {{position.x - 0.25, position.y - 0.25}, {position.x + 0.25, position.y + 0.25}} + local entities = player.surface.find_entities_filtered{area = area, force = "player"} + if #entities ~= 0 then + if #entities == 1 then + error("\"Could not find a suitable position to place " .. entity .. " at the target location, as there is an existing object in the way\"") + else + error("\"Could not find a suitable position to place " .. entity .. " at the target location, as there are existing objects in the way\"") + end + end + end + + global.utils.avoid_entity(player_index, entity, position, direction) + + can_build = global.utils.can_place_entity(player, entity, position, entity_direction) + + if not can_build then + local entity_prototype = game.entity_prototypes[entity] + local entity_box = entity_prototype.collision_box + local entity_width = 1 + local entity_height = 1 + if direction == defines.direction.north or direction == defines.direction.south then + entity_width = math.abs(entity_box.right_bottom.x - entity_box.left_top.x) + entity_height = math.abs(entity_box.right_bottom.y - entity_box.left_top.y) + else + entity_height = math.abs(entity_box.right_bottom.x - entity_box.left_top.x) + entity_width = math.abs(entity_box.right_bottom.y - entity_box.left_top.y) + end + + rendering.draw_rectangle{ + only_in_alt_mode=true, + surface = player.surface, + left_top = {position.x - entity_width / 2, position.y - entity_height / 2}, + right_bottom = {position.x + entity_width / 2, position.y + entity_height / 2}, + filled = false, + color = {r=1, g=0, b=0, a=0.5}, + time_to_live = 60000 + } + + error("\"Cannot place " .. entity .. " at the target location - something is in the way or part of the terrain is unplaceable (water)\"") + end + end + + local have_built = player.surface.create_entity{ + name = entity, + force = player.force, + position = position, + direction = entity_direction, + } + + if have_built then + player.remove_item{name = entity, count = 1} + -- game.print("Placed " .. entity .. " at " .. position.x .. ", " .. position.y) + + -- Find and return the placed entity + local width = 0.5 + local height = 0.5 + local target_area = { + {position.x - width, position.y - height}, + {position.x + width, position.y + height} + } + local entities = player.surface.find_entities_filtered{area = target_area, name = entity} + + if #entities > 0 then + return global.utils.serialize_entity(entities[1]) + end + error("\"Could not find placed entity\"") + end + end + + -- Main execution flow + validate_distance() + validate_entity() + validate_inventory() + global.utils.avoid_entity(player_index, entity, position) + + -- Choose placement method based on global.fast setting + if global.fast then + return fast_place() + else + local result = slow_place() + return result + end +end \ No newline at end of file diff --git a/fle/env/tools/agent/place_entity_next_to/agent.md b/fle/env/factorio/tools/agent/place_entity_next_to/agent.md similarity index 100% rename from fle/env/tools/agent/place_entity_next_to/agent.md rename to fle/env/factorio/tools/agent/place_entity_next_to/agent.md diff --git a/fle/env/tools/agent/place_entity_next_to/client.py b/fle/env/factorio/tools/agent/place_entity_next_to/client.py similarity index 98% rename from fle/env/tools/agent/place_entity_next_to/client.py rename to fle/env/factorio/tools/agent/place_entity_next_to/client.py index 7690a7564..57192d5b4 100644 --- a/fle/env/tools/agent/place_entity_next_to/client.py +++ b/fle/env/factorio/tools/agent/place_entity_next_to/client.py @@ -1,7 +1,7 @@ from fle.env.entities import Position, Entity from fle.env import DirectionInternal from fle.env.game_types import Prototype -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class PlaceEntityNextTo(Tool): diff --git a/fle/env/factorio/tools/agent/place_entity_next_to/server.lua b/fle/env/factorio/tools/agent/place_entity_next_to/server.lua new file mode 100644 index 000000000..28c2f6328 --- /dev/null +++ b/fle/env/factorio/tools/agent/place_entity_next_to/server.lua @@ -0,0 +1,360 @@ +local function validate_mining_drill_placement(surface, position, entity_name) + -- Check if the entity is a mining drill + local prototype = game.entity_prototypes[entity_name] + if prototype.type ~= "mining-drill" then + return true + end + + -- Get the mining area + local mining_area = prototype.collision_box + local area = { + {position.x + mining_area.left_top.x, position.y + mining_area.left_top.y}, + {position.x + mining_area.right_bottom.x, position.y + mining_area.right_bottom.y} + } + + -- Check for resources in the mining area + local resources = surface.find_entities_filtered({ + area = area, + type = "resource" + }) + + -- For mining drills, we need at least one valid resource + return #resources > 0 +end + + +global.actions.place_entity_next_to = function(player_index, entity, ref_x, ref_y, direction, gap) + local player = global.agent_characters[player_index] + local ref_position = {x = ref_x, y = ref_y} + + local function table_contains(tbl, element) + for _, value in ipairs(tbl) do + if value == element then + return true + end + end + return false + end + + local valid_directions = {0, 1, 2, 3} -- 0: North, 1: East, 2: South, 3: West + + if not table_contains(valid_directions, direction) then + error("Invalid direction " .. direction .. " provided. Please use 0 (north), 1 (east), 2 (south), or 3 (west).") + end + + local ref_entities = player.surface.find_entities_filtered({ + area = {{ref_x - 0.5, ref_y - 0.5}, {ref_x + 0.5, ref_y + 0.5}}, + type = {"character", "resource"}, -- Find players + invert = true + }) + local ref_entity = #ref_entities > 0 and ref_entities[1] or nil + + local function is_transport_belt(entity_name) + return entity_name == "transport-belt" or + entity_name == "fast-transport-belt" or + entity_name == "express-transport-belt" + end + + + local function calculate_position(direction, ref_pos, ref_entity, gap, is_belt, entity_to_place) + local new_pos = {x = ref_pos.x, y = ref_pos.y} + local effective_gap = gap or 0 + + -- Get reference entity's width/height based on its rotation + local ref_width, ref_height + if ref_entity then + if ref_entity.type == "inserter" then + ref_width, ref_height = 1, 1 + else + local ref_orientation = ref_entity.direction + if ref_orientation == 2 or ref_orientation == 6 then -- East or West + ref_width = ref_entity.prototype.tile_height + ref_height = ref_entity.prototype.tile_width + else -- North or South + ref_width = ref_entity.prototype.tile_width + ref_height = ref_entity.prototype.tile_height + end + end + else + ref_width = 1 + ref_height = 1 + end + + -- Check if the entity to place is an inserter + local entity_prototype = game.entity_prototypes[entity_to_place] + -- Original spacing calculation for non-inserters + local entity_width, entity_height + if direction == 1 or direction == 3 then -- East or West + entity_width = entity_prototype.tile_height + entity_height = entity_prototype.tile_width + else -- North or South + entity_width = entity_prototype.tile_width + entity_height = entity_prototype.tile_height + end + + if direction == 0 then -- North + new_pos.y = new_pos.y - (math.ceil(ref_height + entity_height)/2 + effective_gap) + elseif direction == 1 then -- East + new_pos.x = new_pos.x + (math.ceil(ref_width + entity_width)/2 + effective_gap) + elseif direction == 2 then -- South + new_pos.y = new_pos.y + (math.ceil(ref_height + entity_height)/2 + effective_gap) + else -- West + new_pos.x = new_pos.x - (math.ceil(ref_width + entity_width)/2 + effective_gap) + end + + -- Round the position to the nearest 0.5 to align with Factorio's grid + new_pos.x = math.ceil(new_pos.x * 2) / 2 + new_pos.y = math.ceil(new_pos.y * 2) / 2 + + return new_pos + end + + local function calculate_position_old(direction, ref_pos, ref_entity, gap, is_belt, entity_to_place) + local new_pos = {x = ref_pos.x, y = ref_pos.y} + local effective_gap = gap or 0 + + -- Get reference entity's width/height based on its rotation + local ref_width, ref_height + if ref_entity then + local ref_orientation = ref_entity.direction -- 0,2,4,6 = N,E,S,W + if ref_orientation == 2 or ref_orientation == 6 then -- East or West + ref_width = ref_entity.prototype.tile_height + ref_height = ref_entity.prototype.tile_width + else -- North or South + ref_width = ref_entity.prototype.tile_width + ref_height = ref_entity.prototype.tile_height + end + else + ref_width = 1 + ref_height = 1 + end + + -- Get entity to place width/height based on desired direction + local entity_prototype = game.entity_prototypes[entity_to_place] + local entity_width, entity_height + if direction == 1 or direction == 3 then -- East or West + entity_width = entity_prototype.tile_height + entity_height = entity_prototype.tile_width + else -- North or South + entity_width = entity_prototype.tile_width + entity_height = entity_prototype.tile_height + end + + -- Calculate spacing based on the relevant dimensions + if direction == 0 then -- North + new_pos.y = new_pos.y - (math.ceil(ref_height + entity_height)/2 + effective_gap) + elseif direction == 1 then -- East + new_pos.x = new_pos.x + (math.ceil(ref_width + entity_width)/2 + effective_gap) + elseif direction == 2 then -- South + new_pos.y = new_pos.y + (math.ceil(ref_height + entity_height)/2 + effective_gap) + else -- West + new_pos.x = new_pos.x - (math.ceil(ref_width + entity_width)/2 + effective_gap) + end + + -- Round the position to the nearest 0.5 to align with Factorio's grid + new_pos.x = math.ceil(new_pos.x * 2) / 2 + new_pos.y = math.ceil(new_pos.y * 2) / 2 + + return new_pos + end + + local is_belt = is_transport_belt(entity) + + local new_position = calculate_position(direction, ref_position, ref_entity, gap, is_belt, entity) + + local function player_collision(player, target_area) + local character_box = { + left_top = {x = -0.2, y = -0.2}, + right_bottom = {x = 0.2, y = 0.2} + } + local character_area = { + {player.position.x + character_box.left_top.x, player.position.y + character_box.left_top.y}, + {player.position.x + character_box.right_bottom.x, player.position.y + character_box.right_bottom.y} + } + return (character_area[1][1] < target_area[2][1] and character_area[2][1] > target_area[1][1]) and + (character_area[1][2] < target_area[2][2] and character_area[2][2] > target_area[1][2]) + end + + local nearby_entities = player.surface.find_entities_filtered({ + position = new_position, + radius = 0.5, + force = player.force + }) + + if #nearby_entities > 0 then + local colliding_entity_names = {} + for _, nearby_entity in pairs(nearby_entities) do + if nearby_entity.name ~= 'laser-beam' and nearby_entity.name ~= "character" then + table.insert(colliding_entity_names, nearby_entity.name) + end + end + if #colliding_entity_names > 0 then + local colliding_entity_name + if #colliding_entity_names == 1 then + colliding_entity_name = colliding_entity_names[1] + elseif #colliding_entity_names == 2 then + colliding_entity_name = table.concat(colliding_entity_names, " and ") + else + colliding_entity_name = table.concat(colliding_entity_names, ", ", 1, #colliding_entity_names - 1) .. ", and " .. colliding_entity_names[#colliding_entity_names] + end + error("\"A " .. colliding_entity_name .. " already exists at the new position " .. serpent.line(new_position) .. ". Consider increasing the spacing (".. gap.."), changing the direction or changing the reference position (" .. serpent.line(ref_position) .. ")\"") + end + end + + local orientation = global.utils.get_entity_direction(entity, direction) + + if ref_entity then + local prototype = game.entity_prototypes[ref_entity.name] + local collision_box = prototype.collision_box + local width = math.abs(collision_box.right_bottom.x - collision_box.left_top.x) + local height = math.abs(collision_box.right_bottom.y - collision_box.left_top.y) + + local target_area = { + {new_position.x - width / 2, new_position.y - height / 2}, + {new_position.x + width / 2, new_position.y + height / 2} + } + while player_collision(player, target_area) do + player.teleport({player.position.x + width + 1, player.position.y}) + end + end + + -- Check for player collision and move player if necessary + local entity_prototype = game.entity_prototypes[entity] + local entity_box = entity_prototype.collision_box + local entity_width = 1 + local entity_height = 1 + if orientation == defines.direction.north or orientation == defines.direction.south then + entity_width = math.abs(entity_box.right_bottom.x - entity_box.left_top.x) + entity_height = math.abs(entity_box.right_bottom.y - entity_box.left_top.y) + else + entity_height = math.abs(entity_box.right_bottom.x - entity_box.left_top.x) + entity_width = math.abs(entity_box.right_bottom.y - entity_box.left_top.y) + end + + + local target_area = { + {new_position.x - entity_width / 2, new_position.y - entity_height / 2}, + {new_position.x + entity_width / 2, new_position.y + entity_height / 2} + } + + if player_collision(player, target_area) then + local move_distance = math.max(entity_width, entity_height) + 1 + local move_direction = {x = 0, y = 0} + + if direction == 0 or direction == 2 then -- North or South + move_direction.x = 1 -- Move East + else -- East or West + move_direction.y = 1 -- Move South + end + + local new_player_position = { + x = player.position.x + move_direction.x * move_distance, + y = player.position.y + move_direction.y * move_distance + } + player.teleport(new_player_position) + end + + -- First clean up any items-on-ground at the target position + local area = {{new_position.x - entity_width / 2, new_position.y - entity_height / 2}, {new_position.x + entity_width / 2, new_position.y + entity_height / 2}} + + -- Show bounding box + rendering.draw_rectangle({ + only_in_alt_mode=true, + color = {r = 0, g = 1, b = 0}, + filled = false, + left_top = area[1], + right_bottom = area[2], + surface = player.surface, + time_to_live = 60000 + }) + rendering.draw_circle({ + only_in_alt_mode=true, + color = {r = 1, g = 0, b = 0}, + radius = 0.2, + filled = true, + target = new_position, + surface = player.surface, + time_to_live = 60000 + }) + rendering.draw_circle({ + only_in_alt_mode=true, + color = {r = 0, g = 0, b = 1}, + radius = 0.2, + filled = true, + target = ref_position, + surface = player.surface, + time_to_live = 60000 + }) + + + local items = player.surface.find_entities_filtered{ + area = area, + type = "item-on-ground" + } + for _, item in ipairs(items) do + item.destroy() + end + + global.utils.avoid_entity(player_index, entity, new_position, direction) + + local can_build = player.surface.can_place_entity({ + name = entity, + position = new_position, + direction = orientation, + force = player.force + }) + if can_build then + can_build = validate_mining_drill_placement(player.surface, new_position, entity) + if not can_build then + error("Cannot place mining drill - no resources found in mining area") + end + else + local entities = player.surface.find_entities_filtered{area = area, type = {"beam", "resource", "player", "character"}, invert=true} + local blocker_names = {} + for _, e in ipairs(entities) do + table.insert(blocker_names, e.type.."("..serpent.line(e.position)..")") + end + -- game.print(serpent.line(blocker_names)) + + local tree = player.surface.find_entities_filtered{area = area, type = {"tree"}} + for _, e in ipairs(tree) do + table.insert(blocker_names, e.name.."("..serpent.line(e.position)..")") + end + + local tiles = player.surface.find_tiles_filtered{area = area, name={"water", "deepwater", "water-green", "deepwater-green", "water-shallow", "water-mud"}} + if #tiles > 0 then + for _, e in ipairs(tiles) do + -- if e.name is not in blocker_names, we should add item + if not table_contains(blocker_names, e.name.."("..serpent.line(e.position)..")") then + table.insert(blocker_names, e.name.."("..serpent.line(e.position)..")") + end + end + end + -- game.print(serpent.line(blocker_names)) + + error("\'Cannot place entity at the position " .. serpent.line(new_position) .. " with the current direction" .. + ". Attempting to place next to - "..ref_entity.name..". There might be a collision with existing entities or this area cannot be placed on (water). Nearby entities that might be blocking the placement - " .. serpent.line(blocker_names) .. + ". Consider increasing the spacing (".. gap.."), changing the direction or changing the reference position (" .. serpent.line(ref_position) .. ")\'") + + end + + local new_entity = player.surface.create_entity({ + name = entity, + position = new_position, + force = player.force, + direction = orientation, + move_stuck_players = true, + }) + + if not new_entity then + error("Failed to create entity " .. entity .. " at position " .. serpent.line(new_position)) + end + + local item_stack = {name = entity, count = 1} + if player.get_main_inventory().can_insert(item_stack) then + player.get_main_inventory().remove(item_stack) + return global.utils.serialize_entity(new_entity) + else + error("Not enough items in inventory.") + end +end diff --git a/fle/env/tools/agent/print/agent.md b/fle/env/factorio/tools/agent/print/agent.md similarity index 100% rename from fle/env/tools/agent/print/agent.md rename to fle/env/factorio/tools/agent/print/agent.md diff --git a/fle/env/tools/agent/print/client.py b/fle/env/factorio/tools/agent/print/client.py similarity index 97% rename from fle/env/tools/agent/print/client.py rename to fle/env/factorio/tools/agent/print/client.py index 7c63226ff..c0c28a95c 100644 --- a/fle/env/tools/agent/print/client.py +++ b/fle/env/factorio/tools/agent/print/client.py @@ -1,5 +1,5 @@ from fle.env.entities import Entity, Inventory, Position -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class Print(Tool): diff --git a/fle/env/factorio/tools/agent/print/server.lua b/fle/env/factorio/tools/agent/print/server.lua new file mode 100644 index 000000000..124d2842b --- /dev/null +++ b/fle/env/factorio/tools/agent/print/server.lua @@ -0,0 +1,4 @@ +global.actions.print = function(message) + message = dump(message) + return '"'..message..'"' +end \ No newline at end of file diff --git a/fle/env/tools/agent/rotate_entity/agent.md b/fle/env/factorio/tools/agent/rotate_entity/agent.md similarity index 100% rename from fle/env/tools/agent/rotate_entity/agent.md rename to fle/env/factorio/tools/agent/rotate_entity/agent.md diff --git a/fle/env/tools/agent/rotate_entity/client.py b/fle/env/factorio/tools/agent/rotate_entity/client.py similarity index 98% rename from fle/env/tools/agent/rotate_entity/client.py rename to fle/env/factorio/tools/agent/rotate_entity/client.py index 326c98dc2..87b340e95 100644 --- a/fle/env/tools/agent/rotate_entity/client.py +++ b/fle/env/factorio/tools/agent/rotate_entity/client.py @@ -5,7 +5,7 @@ ) # We have 2 Direction objects to avoid circular deps from fle.env import DirectionInternal from fle.env.game_types import prototype_by_name -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class RotateEntity(Tool): diff --git a/fle/env/factorio/tools/agent/rotate_entity/server.lua b/fle/env/factorio/tools/agent/rotate_entity/server.lua new file mode 100644 index 000000000..2d0e7f1c2 --- /dev/null +++ b/fle/env/factorio/tools/agent/rotate_entity/server.lua @@ -0,0 +1,64 @@ +global.actions.rotate_entity = function(player_index, x, y, direction, entity) + local player = global.agent_characters[player_index] + local position = {x=x, y=y} -- Round to nearest tile + local surface = player.surface + + local function table_contains(tbl, element) + for _, value in ipairs(tbl) do + if value == element then + return true + end + end + return false + end + + local closest_distance = math.huge + local closest_entity = nil + local area = {{position.x - 0.5, position.y - 0.5}, {position.x + 0.5, position.y + 0.5}} + local buildings = surface.find_entities_filtered{area = area, force = "player", name=entity} + + -- Find the closest building + for _, building in ipairs(buildings) do + if building.rotatable and building.name ~= 'character' then + local distance = ((position.x - building.position.x) ^ 2 + (position.y - building.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest_entity = building + end + end + end + + if closest_entity == nil then + error("No entity to rotate at the given coordinates.") + end + + local valid_directions = {0, 1, 2, 3} + + if not table_contains(valid_directions, direction) then + error("Invalid direction " .. direction .. " provided. Please use 0 (north), 1 (east), 2 (south), or 3 (west).") + end + + -- game.print("Direction "..direction.. ", "..closest_entity.name..", "..closest_entity.direction) + -- game.print(global.utils.get_entity_direction(closest_entity.name, closest_entity.direction)) + + -- Rotate the entity + closest_entity.direction = global.utils.get_entity_direction(closest_entity.name, direction) + + -- Ensure the entity is properly aligned to the grid + local entity_position = closest_entity.position + local aligned_position = { + x = math.floor(entity_position.x), + y = math.floor(entity_position.y) + } + -- game.print("Entity position: " .. entity_position.x .. ", " .. entity_position.y) + -- game.print("Aligned position: " .. aligned_position.x .. ", " .. aligned_position.y) + + if entity_position.x ~= aligned_position.x or entity_position.y ~= aligned_position.y then + closest_entity.teleport(aligned_position) + end + + -- game.print("Rotated " .. closest_entity.name .. " to " .. closest_entity.direction) + + local serialized = global.utils.serialize_entity(closest_entity) + return serialized +end \ No newline at end of file diff --git a/fle/env/tools/agent/score/client.py b/fle/env/factorio/tools/agent/score/client.py similarity index 95% rename from fle/env/tools/agent/score/client.py rename to fle/env/factorio/tools/agent/score/client.py index f474d53df..fce79ea25 100644 --- a/fle/env/tools/agent/score/client.py +++ b/fle/env/factorio/tools/agent/score/client.py @@ -1,4 +1,4 @@ -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class Reward(Tool): diff --git a/fle/env/tools/agent/score/server.lua b/fle/env/factorio/tools/agent/score/server.lua similarity index 100% rename from fle/env/tools/agent/score/server.lua rename to fle/env/factorio/tools/agent/score/server.lua diff --git a/fle/env/tools/agent/send_message/agent.md b/fle/env/factorio/tools/agent/send_message/agent.md similarity index 100% rename from fle/env/tools/agent/send_message/agent.md rename to fle/env/factorio/tools/agent/send_message/agent.md diff --git a/fle/env/tools/agent/send_message/client.py b/fle/env/factorio/tools/agent/send_message/client.py similarity index 95% rename from fle/env/tools/agent/send_message/client.py rename to fle/env/factorio/tools/agent/send_message/client.py index 566c1eccd..7c02aebae 100644 --- a/fle/env/tools/agent/send_message/client.py +++ b/fle/env/factorio/tools/agent/send_message/client.py @@ -1,7 +1,7 @@ from typing import Any, Dict, Optional import time -from fle.env.tools import Tool -from fle.env.tools.admin.render_message.client import RenderMessage +from fle.env.factorio.tools import Tool +from fle.env.factorio.tools.admin.render_message.client import RenderMessage import logging import uuid from a2a.types import Message, Part, TextPart diff --git a/fle/env/factorio/tools/agent/send_message/server.lua b/fle/env/factorio/tools/agent/send_message/server.lua new file mode 100644 index 000000000..b6f2de2f6 --- /dev/null +++ b/fle/env/factorio/tools/agent/send_message/server.lua @@ -0,0 +1 @@ +-- Left blank intentionally - this functionality is handled by the A2A protocol \ No newline at end of file diff --git a/fle/env/tools/agent/set_entity_recipe/agent.md b/fle/env/factorio/tools/agent/set_entity_recipe/agent.md similarity index 100% rename from fle/env/tools/agent/set_entity_recipe/agent.md rename to fle/env/factorio/tools/agent/set_entity_recipe/agent.md diff --git a/fle/env/tools/agent/set_entity_recipe/client.py b/fle/env/factorio/tools/agent/set_entity_recipe/client.py similarity index 98% rename from fle/env/tools/agent/set_entity_recipe/client.py rename to fle/env/factorio/tools/agent/set_entity_recipe/client.py index d1a1799b0..a991cbed5 100644 --- a/fle/env/tools/agent/set_entity_recipe/client.py +++ b/fle/env/factorio/tools/agent/set_entity_recipe/client.py @@ -2,7 +2,7 @@ from fle.env.entities import Entity from fle.env.game_types import Prototype, RecipeName -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class SetEntityRecipe(Tool): diff --git a/fle/env/factorio/tools/agent/set_entity_recipe/server.lua b/fle/env/factorio/tools/agent/set_entity_recipe/server.lua new file mode 100644 index 000000000..d57a87849 --- /dev/null +++ b/fle/env/factorio/tools/agent/set_entity_recipe/server.lua @@ -0,0 +1,48 @@ +global.actions.set_entity_recipe = function(player_index, recipe_name, x, y) + local player = global.agent_characters[player_index] + local surface = player.surface + --local position = player.position + local closest_distance = math.huge + local closest_building = nil + + -- Iterate through all crafting machines in the area + local area = {{x - 1, y - 1}, {x + 1, y + 1}} + local buildings = surface.find_entities_filtered{area = area, type = {"assembling-machine", "inserter"}} + + -- Find the closest building + for _, building in ipairs(buildings) do + local distance = ((x - building.position.x) ^ 2 + (y - building.position.y) ^ 2) ^ 0.5 + if distance < closest_distance then + closest_distance = distance + closest_building = building + end + end + + -- If a closest building is found, handle based on its type + if closest_building then + local serialized + + -- Handle different entity types + if closest_building.type == "inserter" then + -- For filter inserters, set the filter + if closest_building.name == "filter-inserter" or closest_building.name == "stack-filter-inserter" then + closest_building.set_filter(1, recipe_name) -- Set first filter slot + serialized = global.utils.serialize_entity(closest_building) + end + else + -- Original assembling machine logic + local recipe = player.force.recipes[recipe_name] + if recipe and closest_building.get_recipe() ~= recipe then + closest_building.set_recipe(recipe_name) + end + serialized = global.utils.serialize_entity(closest_building) + end + + local entity_json = game.table_to_json(serialized) + -- game.print(entity_json) + return serialized + else + error("No building found that could have its recipe or filter set.") + end +end + diff --git a/fle/env/tools/agent/set_research/agent.md b/fle/env/factorio/tools/agent/set_research/agent.md similarity index 100% rename from fle/env/tools/agent/set_research/agent.md rename to fle/env/factorio/tools/agent/set_research/agent.md diff --git a/fle/env/tools/agent/set_research/client.py b/fle/env/factorio/tools/agent/set_research/client.py similarity index 97% rename from fle/env/tools/agent/set_research/client.py rename to fle/env/factorio/tools/agent/set_research/client.py index f6d9dc3bf..8ae2d61f9 100644 --- a/fle/env/tools/agent/set_research/client.py +++ b/fle/env/factorio/tools/agent/set_research/client.py @@ -1,7 +1,7 @@ from typing import List, Optional from pydantic import BaseModel from fle.env.game_types import Technology -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class Ingredient(BaseModel): diff --git a/fle/env/factorio/tools/agent/set_research/server.lua b/fle/env/factorio/tools/agent/set_research/server.lua new file mode 100644 index 000000000..cdb262e7d --- /dev/null +++ b/fle/env/factorio/tools/agent/set_research/server.lua @@ -0,0 +1,74 @@ +global.actions.set_research = function(player_index, technology_name) + local player = global.agent_characters[player_index] + local force = player.force + + -- Helper function to check if technology can be researched + local function can_research_technology(force, tech_name) + local tech = force.technologies[tech_name] + + if not tech then + return false, "technology doesn't exist" + end + + if tech.researched then + return false, "technology is already researched" + end + + if not tech.enabled then + return false, "technology is not enabled" + end + + -- Check prerequisites + for _, prerequisite in pairs(tech.prerequisites) do + if not prerequisite.researched then + return false, "missing prerequisite - " .. prerequisite.name + end + end + + -- Check if we have the required research ingredients + for _, ingredient in pairs(tech.research_unit_ingredients) do + if not force.recipes[ingredient.name].enabled then + return false, "missing required science pack recipe - " .. ingredient.name + end + end + + return true, tech + end + + -- Main logic + local can_research, result = can_research_technology(force, technology_name) + + if not can_research then + error(string.format("\"Cannot research %s because %s\"", + technology_name, result)) + end + if force.current_research then + force.set_saved_technology_progress(force.current_research.name, force.research_progress) + end + -- Cancel current research if any + --force.cancel_current_research() + + -- Set new research using add_research + local success = force.add_research(technology_name) + if not success then + error(string.format("\"Failed to start research for %s\"", technology_name)) + end + + -- Collect and return the research ingredients + local ingredients = {} + local tech = result -- Using the technology object from can_research_technology + + -- Get the count of research units needed + local units_required = tech.research_unit_count + + -- Collect all ingredients and their counts + for _, ingredient in pairs(tech.research_unit_ingredients) do + table.insert(ingredients, { + name = "\""..ingredient.name.."\"", + count = ingredient.amount * units_required, + type = ingredient.type + }) + end + + return ingredients +end \ No newline at end of file diff --git a/fle/env/tools/agent/shift_entity/client.py b/fle/env/factorio/tools/agent/shift_entity/client.py similarity index 87% rename from fle/env/tools/agent/shift_entity/client.py rename to fle/env/factorio/tools/agent/shift_entity/client.py index 4bff7b557..5b64013e3 100644 --- a/fle/env/tools/agent/shift_entity/client.py +++ b/fle/env/factorio/tools/agent/shift_entity/client.py @@ -3,11 +3,11 @@ from fle.env.entities import Entity, Direction from fle.env.game_types import prototype_by_name from fle.env import DirectionInternal as DirectionA -from fle.env.tools.agent.can_place_entity.client import CanPlaceEntity -from fle.env.tools.agent.connect_entities.client import ConnectEntities -from fle.env.tools.agent.pickup_entity.client import PickupEntity -from fle.env.tools.agent.place_entity.client import PlaceObject as PlaceEntity -from fle.env.tools import Tool +from fle.env.factorio.tools.agent.can_place_entity.client import CanPlaceEntity +from fle.env.factorio.tools.agent.connect_entities.client import ConnectEntities +from fle.env.factorio.tools.agent.pickup_entity.client import PickupEntity +from fle.env.factorio.tools.agent.place_entity.client import PlaceObject as PlaceEntity +from fle.env.factorio.tools import Tool class ShiftEntity(Tool): diff --git a/fle/env/tools/agent/sleep/agent.md b/fle/env/factorio/tools/agent/sleep/agent.md similarity index 100% rename from fle/env/tools/agent/sleep/agent.md rename to fle/env/factorio/tools/agent/sleep/agent.md diff --git a/fle/env/tools/agent/sleep/client.py b/fle/env/factorio/tools/agent/sleep/client.py similarity index 96% rename from fle/env/tools/agent/sleep/client.py rename to fle/env/factorio/tools/agent/sleep/client.py index 6eb378d0b..8d0c764a2 100644 --- a/fle/env/tools/agent/sleep/client.py +++ b/fle/env/factorio/tools/agent/sleep/client.py @@ -1,6 +1,6 @@ from time import sleep -from fle.env.tools import Tool +from fle.env.factorio.tools import Tool class Sleep(Tool): diff --git a/fle/env/factorio/tools/agent/sleep/server.lua b/fle/env/factorio/tools/agent/sleep/server.lua new file mode 100644 index 000000000..5373f331a --- /dev/null +++ b/fle/env/factorio/tools/agent/sleep/server.lua @@ -0,0 +1,6 @@ +global.actions.sleep = function(ticks_elapsed) + if ticks_elapsed > 0 then + global.elapsed_ticks = global.elapsed_ticks + ticks_elapsed + end + return game.tick +end \ No newline at end of file diff --git a/fle/env/tools/controller.py b/fle/env/factorio/tools/controller.py similarity index 100% rename from fle/env/tools/controller.py rename to fle/env/factorio/tools/controller.py diff --git a/fle/env/tools/init.py b/fle/env/factorio/tools/init.py similarity index 87% rename from fle/env/tools/init.py rename to fle/env/factorio/tools/init.py index b05594fc1..9e705e3c1 100644 --- a/fle/env/tools/init.py +++ b/fle/env/factorio/tools/init.py @@ -1,4 +1,4 @@ -from fle.env.tools.controller import Controller +from fle.env.factorio.tools.controller import Controller from fle.env.instance import FactorioInstance, FactorioServer diff --git a/fle/env/tools/tool.py b/fle/env/factorio/tools/tool.py similarity index 95% rename from fle/env/tools/tool.py rename to fle/env/factorio/tools/tool.py index 586f50a01..e11a54489 100644 --- a/fle/env/tools/tool.py +++ b/fle/env/factorio/tools/tool.py @@ -3,7 +3,7 @@ from fle.env.entities import Position, Entity from fle.env.namespace import FactorioNamespace from fle.env.instance import FactorioServer -from fle.env.tools.controller import Controller +from fle.env.factorio.tools.controller import Controller class Tool(Controller): diff --git a/fle/env/utils/rcon.py b/fle/env/utils/rcon.py index 20cc1a5ee..a9e508c25 100644 --- a/fle/env/utils/rcon.py +++ b/fle/env/utils/rcon.py @@ -12,6 +12,9 @@ import io import contextlib +ROOT_DIR = Path(__file__).resolve().parent.parent.parent +print(ROOT_DIR) + def _load_script(filename): with open(filename, "r", encoding="utf-8") as file: @@ -30,14 +33,12 @@ def _load_scripts(scripts): def _get_mods_dir(): # get local execution path - path = os.path.dirname(Path(os.path.dirname(os.path.realpath(__file__)))) - return path + "/mods" + return str(ROOT_DIR / "env" / "factorio" / "mods") def _get_dir(name="tools"): # get local execution path - path = os.path.dirname(Path(os.path.dirname(os.path.realpath(__file__)))) - return path + f"/{name}" + return str(ROOT_DIR / "env" / "factorio" / name) def _get_tool_names(name="tools") -> List[str]: From 79ae1cdf81d57780038a40071f36c6900ef1ad57 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 30 Jul 2025 12:46:42 +0530 Subject: [PATCH 31/53] minor update + remove script for now --- .../tools/admin/clear_collision_boxes.lua | 42 - .../script/tools/admin/clear_entities.lua | 65 - .../tools/admin/extend_collision_boxes.lua | 201 -- .../tools/admin/get_factory_centroid.lua | 198 -- .../script/tools/admin/get_messages.lua | 1 - .../script/tools/admin/get_path.lua | 38 - .../tools/admin/get_production_stats.lua | 48 - .../script/tools/admin/inspect_entities.lua | 123 -- .../script/tools/admin/load_blueprint.lua | 64 - .../script/tools/admin/load_entity_state.lua | 289 --- .../tools/admin/load_research_state.lua | 48 - .../script/tools/admin/production_stats.lua | 48 - .../tools/admin/regenerate_resources.lua | 37 - .../script/tools/admin/render.lua | 157 -- .../script/tools/admin/render_message.lua | 18 - .../script/tools/admin/request_path.lua | 118 -- .../script/tools/admin/save_blueprint.lua | 61 - .../script/tools/admin/save_entity_state.lua | 383 ---- .../tools/admin/save_research_state.lua | 62 - .../script/tools/agent/can_place_entity.lua | 84 - .../script/tools/agent/connect_entities.lua | 1307 ------------ .../script/tools/agent/craft_item.lua | 213 -- .../script/tools/agent/extract_item.lua | 195 -- .../tools/agent/get_connection_amount.lua | 0 .../script/tools/agent/get_entities.lua | 37 - .../script/tools/agent/get_entity.lua | 48 - .../tools/agent/get_prototype_recipe.lua | 10 - .../tools/agent/get_research_progress.lua | 47 - .../script/tools/agent/get_resource_patch.lua | 105 - .../script/tools/agent/harvest_resource.lua | 622 ------ .../script/tools/agent/insert_item.lua | 244 --- .../script/tools/agent/inspect_inventory.lua | 219 -- .../script/tools/agent/launch_rocket.lua | 44 - .../script/tools/agent/move_to.lua | 265 --- .../script/tools/agent/nearest.lua | 71 - .../script/tools/agent/nearest_buildable.lua | 189 -- .../script/tools/agent/pickup_entity.lua | 154 -- .../script/tools/agent/place_entity.lua | 348 --- .../tools/agent/place_entity_next_to.lua | 360 ---- .../script/tools/agent/print.lua | 4 - .../script/tools/agent/rotate_entity.lua | 64 - .../script/tools/agent/score.lua | 352 ---- .../script/tools/agent/send_message.lua | 1 - .../script/tools/agent/set_entity_recipe.lua | 48 - .../script/tools/agent/set_research.lua | 74 - .../script/tools/agent/sleep.lua | 6 - .../script/utils/alerts.lua | 441 ---- .../script/utils/build_checkerboard.lua | 4 - .../script/utils/checksum.lua | 15 - .../script/utils/clear_entities.lua | 65 - .../script/utils/clear_inventory.lua | 2 - .../script/utils/connection_points.lua | 295 --- .../script/utils/enemies.lua | 7 - .../script/utils/initialise.lua | 1168 ---------- .../script/utils/initialise_inventory.lua | 9 - .../script/utils/priority_queue.lua | 61 - .../script/utils/production_score.lua | 339 --- .../recipe_fluid_connection_mappings.lua | 174 -- .../script/utils/reset.lua | 1 - .../script/utils/reset_position.lua | 2 - .../script/utils/serialize.lua | 1873 ----------------- .../script/utils/util.lua | 690 ------ fle/env/factorio_server.py | 3 +- fle/env/run_envs.py | 27 +- 64 files changed, 14 insertions(+), 12274 deletions(-) delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_collision_boxes.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_entities.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/extend_collision_boxes.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_factory_centroid.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_messages.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_path.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_production_stats.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/inspect_entities.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_blueprint.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_entity_state.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_research_state.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/production_stats.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/regenerate_resources.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render_message.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/request_path.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_blueprint.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_entity_state.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_research_state.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/can_place_entity.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/connect_entities.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/craft_item.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/extract_item.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_connection_amount.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entities.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entity.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_prototype_recipe.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_research_progress.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_resource_patch.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/harvest_resource.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/insert_item.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/inspect_inventory.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/launch_rocket.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/move_to.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest_buildable.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/pickup_entity.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity_next_to.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/print.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/rotate_entity.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/score.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/send_message.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_entity_recipe.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_research.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/sleep.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/alerts.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/build_checkerboard.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/checksum.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_entities.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_inventory.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/connection_points.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/enemies.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise_inventory.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/priority_queue.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/production_score.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/recipe_fluid_connection_mappings.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset_position.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/serialize.lua delete mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script/utils/util.lua diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_collision_boxes.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_collision_boxes.lua deleted file mode 100644 index 6d24e29d7..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_collision_boxes.lua +++ /dev/null @@ -1,42 +0,0 @@ -global.actions.clear_collision_boxes = function(player_index) - local player = global.agent_characters[player_index] - if not player then return end - - -- Clean up temporary entities - local clearance_entities = global.clearance_entities[player_index] - if not clearance_entities then return end - - -- Define search area around player (500 tile radius) - local search_area = { - left_top = { - x = player.position.x - 500, - y = player.position.y - 500 - }, - right_bottom = { - x = player.position.x + 500, - y = player.position.y + 500 - } - } - - -- Find all simple-entity-with-owner entities in the area - local entities = player.surface.find_entities_filtered{ - type = "simple-entity-with-owner", - area = search_area - } - - -- Destroy found entities - local count = 0 - for _, entity in pairs(entities) do - if entity.valid then - entity.destroy() - count = count + 1 - end - end - - -- Destroy all created entities - for _, entity in pairs(clearance_entities) do - if entity.valid then - entity.destroy() - end - end -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_entities.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_entities.lua deleted file mode 100644 index ea8224337..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/clear_entities.lua +++ /dev/null @@ -1,65 +0,0 @@ -global.actions.clear_entities = function(player_index) - local function clear_area_of_entities(player, area, force_filter) - local surface = player.surface - local entities = surface.find_entities_filtered{ - area = area, - force = force_filter, - type = { - "accumulator", "ammo-turret", "arithmetic-combinator", "artillery-turret", - "assembling-machine", "beacon", "boiler", "constant-combinator", - "container", "curved-rail", "decider-combinator", "electric-pole", - "electric-turret", "fluid-turret", "furnace", "gate", "generator", - "heat-interface", "heat-pipe", "inserter", "lab", "lamp", - "land-mine", "linked-belt", "linked-container", "loader", - "loader-1x1", "market", "mining-drill", "offshore-pump", - "pipe", "pipe-to-ground", "power-switch", "programmable-speaker", - "pump", "radar", "rail-chain-signal", "rail-signal", - "reactor", "roboport", "rocket-silo", "solar-panel", - "splitter", "storage-tank", "straight-rail", "train-stop", - "transport-belt", "underground-belt", "wall" - } - } - - for _, entity in ipairs(entities) do - if entity and entity.valid and entity ~= player then - entity.destroy() - end - end - - -- Clear dropped items separately - local dropped_items = surface.find_entities_filtered{ - area = area, - name = "item-on-ground" - } - for _, item in ipairs(dropped_items) do - if item and item.valid then - item.destroy() - end - end - end - - local function reset_character_inventory(player) - for inventory_id, inventory in pairs(defines.inventory) do - local character_inventory = player.get_inventory(inventory) - if character_inventory then - character_inventory.clear() - end - end - end - - -- Main execution - local player = global.agent_characters[player_index] - local area = { - {player.position.x - 1000, player.position.y - 1000}, - {player.position.x + 1000, player.position.y + 1000} - } - - -- Clear player force entities - clear_area_of_entities(player, area, player.force) - -- Clear neutral force entities - clear_area_of_entities(player, area, "neutral") - - reset_character_inventory(player) - player.force.reset() - return 1 -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/extend_collision_boxes.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/extend_collision_boxes.lua deleted file mode 100644 index 02c859524..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/extend_collision_boxes.lua +++ /dev/null @@ -1,201 +0,0 @@ --- Helper function for pumpjack fluid positions ---local function get_pumpjack_connection_positions(pumpjack) --- local positions = {} --- local x, y = pumpjack.position.x, pumpjack.position.y --- --- -- Pumpjack output position changes based on direction --- if pumpjack.direction == defines.direction.north then --- table.insert(positions, {x = x, y = y - 2}) --- elseif pumpjack.direction == defines.direction.south then --- table.insert(positions, {x = x, y = y + 2}) --- elseif pumpjack.direction == defines.direction.east then --- table.insert(positions, {x = x + 2, y = y}) --- elseif pumpjack.direction == defines.direction.west then --- table.insert(positions, {x = x - 2, y = y}) --- end --- --- return positions ---end - ----- Helper function for boiler fluid positions ---local function get_boiler_connection_positions(boiler) --- local positions = {} --- local x, y = boiler.position.x, boiler.position.y --- --- if boiler.direction == defines.direction.north then --- -- Water input points --- table.insert(positions, {x = x - 2, y = y + 0.5}) --- table.insert(positions, {x = x + 2, y = y + 0.5}) --- -- Steam output point --- table.insert(positions, {x = x, y = y - 1.5}) --- elseif boiler.direction == defines.direction.south then --- -- Water input points --- table.insert(positions, {x = x - 2, y = y - 0.5}) --- table.insert(positions, {x = x + 2, y = y - 0.5}) --- -- Steam output point --- table.insert(positions, {x = x, y = y + 1.5}) --- elseif boiler.direction == defines.direction.east then --- -- Water input points --- table.insert(positions, {x = x - 0.5, y = y - 2}) --- table.insert(positions, {x = x - 0.5, y = y + 2}) --- -- Steam output point --- table.insert(positions, {x = x + 1.5, y = y}) --- elseif boiler.direction == defines.direction.west then --- -- Water input points --- table.insert(positions, {x = x + 0.5, y = y - 2}) --- table.insert(positions, {x = x + 0.5, y = y + 2}) --- -- Steam output point --- table.insert(positions, {x = x - 1.5, y = y}) --- end --- --- return positions ---end - - - -local function add_clearance_entities(surface, force, region, start_pos, end_pos) - local created_entities = {} - local all_positions = {} - - local epsilon = 0.707 -- Small value for floating point comparison - - -- Helper function to check if a position is start or end - local function is_excluded_position(pos) - return (math.abs(pos.x - start_pos.x) < epsilon and math.abs(pos.y - start_pos.y) < epsilon) or - (math.abs(pos.x - end_pos.x) < epsilon and math.abs(pos.y - end_pos.y) < epsilon) - end - - -- Find all relevant entities in the region - local entities = { - pipes = surface.find_entities_filtered{name = "pipe", force = force, area = region}, - boilers = surface.find_entities_filtered{name = "boiler", force = force, area = region}, - drills = surface.find_entities_filtered{type = "mining-drill", force = force, area = region}, - pumpjacks = surface.find_entities_filtered{name = "pumpjack", force = force, area = region}, - refineries = surface.find_entities_filtered{name = "oil-refinery", force = force, area = region}, - chemical_plants = surface.find_entities_filtered{name = "chemical-plant", force = force, area = region}, - storage_tanks = surface.find_entities_filtered{name = "storage-tank", force = force, area = region} - } - - - -- Draw debug circles for start and end positions - rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 1, g = 0, b = 0}, surface = surface, radius = 0.5, filled = false, target = start_pos, time_to_live = 60000} - rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 1, b = 0}, surface = surface, radius = 0.5, filled = false, target = end_pos, time_to_live = 60000} - - -- Collect positions from pipes - for _, pipe in pairs(entities.pipes) do - local pipe_positions = { - {x = pipe.position.x + 1, y = pipe.position.y}, - {x = pipe.position.x - 1, y = pipe.position.y}, - {x = pipe.position.x, y = pipe.position.y + 1}, - {x = pipe.position.x, y = pipe.position.y - 1} - } - for _, pos in pairs(pipe_positions) do - if not is_excluded_position(pos) then - table.insert(all_positions, pos) - end - end - end - - -- Collect positions from boilers - for _, boiler in pairs(entities.boilers) do - for _, pos in pairs(global.utils.get_boiler_connection_points(boiler)) do - if not is_excluded_position(pos) then - table.insert(all_positions, pos) - end - end - end - - -- Collect positions from pumpjacks - for _, pumpjack in pairs(entities.pumpjacks) do - for _, pos in pairs(global.utils.get_pumpjack_connection_points(pumpjack)) do - if not is_excluded_position(pos) then - table.insert(all_positions, pos) - end - end - end - - -- Collect positions from refineries - for _, refinery in pairs(entities.refineries) do - for _, pos in pairs(global.utils.get_refinery_connection_points(refinery)) do - if not is_excluded_position(pos) then - table.insert(all_positions, pos) - end - end - end - - -- Collect positions from chemical plants - for _, plant in pairs(entities.chemical_plants) do - for _, pos in pairs(global.utils.get_chemical_plant_connection_points(plant)) do - if not is_excluded_position(pos) then - table.insert(all_positions, pos) - end - end - end - - -- Collect positions from storage tanks - for _, tank in pairs(entities.storage_tanks) do - for _, pos in pairs(global.utils.get_storage_tank_connection_points(tank)) do - if not is_excluded_position(pos) then - table.insert(all_positions, pos) - end - end - end - -- game.print("There are "..#entities.drills) - for _, drill in pairs(entities.drills) do - -- game.print(serpent.block(drill)) - end - -- Collect positions from mining drills - for _, drill in pairs(entities.drills) do - -- game.print("Drop position ".. serpent.line(drill.drop_position)) - local drop_pos = drill.drop_position--{x=math.round(drill.drop_position.x*2)/2, y=math.round(drill.drop_position.y*2)/2} - if not is_excluded_position(drop_pos) then - table.insert(all_positions, drop_pos) - end - end - - -- Create entities at filtered positions - for _, pos in pairs(all_positions) do - -- Draw debug circles for connection points - rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 1, b = 1}, surface = surface, radius = 0.33, filled = false, target = pos, time_to_live = 60000} - - local entity = surface.create_entity{ - name = "simple-entity-with-owner", - position = pos, - force = force, - graphics_variation = 255, - render_player_index = 65535, - raise_built = false - } - if entity then - entity.destructible = false - entity.graphics_variation = 255 - entity.color = {r = 0, g = 0, b = 0, a = 0} - table.insert(created_entities, entity) - end - end - - return created_entities -end - -global.actions.extend_collision_boxes = function(player_index, start_x, start_y, goal_x, goal_y) - local player = global.agent_characters[player_index] - local start_pos = {x=start_x, y=start_y} - local end_pos = {x=goal_x, y=goal_y} - -- Define region for entity checking (add some margin around start/goal) - local region = { - left_top = { - x = math.min(start_x, goal_x) - 20, - y = math.min(start_y, goal_y) - 20 - }, - right_bottom = { - x = math.max(start_x, goal_x) + 20, - y = math.max(start_y, goal_y) + 20 - } - } - - -- Add buffer entities around all pipes, boilers, and drill drop positions - local created = add_clearance_entities(player.surface, player.force, region, start_pos, end_pos) - global.clearance_entities[player_index] = created - - return true -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_factory_centroid.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_factory_centroid.lua deleted file mode 100644 index edb18b325..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_factory_centroid.lua +++ /dev/null @@ -1,198 +0,0 @@ --- Initialize global camera tracking variables in your script's initialization --- Put this in your script's on_init or when you initialize your global table -function initialize_camera_tracking(initial_position) - initial_position = initial_position or {x = 0, y = 0} - - global.camera = global.camera or {} - global.camera.position = global.camera.position or {x = initial_position.x, y = initial_position.y} - global.camera.velocity = global.camera.velocity or {x = 0, y = 0} - global.camera.target = global.camera.target or {x = initial_position.x, y = initial_position.y} - global.camera.zoom = global.camera.zoom or 1 - global.camera.target_zoom = global.camera.target_zoom or 1 - - -- Smoothing coefficients (adjust these values to change camera behavior) - global.camera.position_smoothing = 0.05 -- How quickly to move toward target (0-1) - global.camera.zoom_smoothing = 0.1 -- How quickly to zoom toward target (0-1) - global.camera.velocity_damping = 0.6 -- How quickly velocity decays (0-1) - - -- Maximum velocity to prevent overshooting - global.camera.max_velocity = 3.0 -end - --- Helper function to calculate factory bounds (unchanged) -function calculate_factory_bounds(force) - local min_x = math.huge - local max_x = -math.huge - local min_y = math.huge - local max_y = -math.huge - - -- Entity types to exclude from bounds calculation - local excluded_types = { - ["player"] = true, - ["character"] = true, - ["character-corpse"] = true, - ["item-entity"] = true, - ["particle"] = true, - ["projectile"] = true, - ["resource"] = true, - ["tree"] = true, - ["simple-entity"] = true - } - - -- Iterate through all surfaces - for _, surface in pairs(game.surfaces) do - local entities = surface.find_entities_filtered{ - force = force - } - - for _, entity in pairs(entities) do - if entity.valid and not excluded_types[entity.type] then - -- Get entity bounding box - local box = entity.bounding_box - - -- Update min/max coordinates - min_x = math.min(min_x, box.left_top.x) - max_x = math.max(max_x, box.right_bottom.x) - min_y = math.min(min_y, box.left_top.y) - max_y = math.max(max_y, box.right_bottom.y) - end - end - end - - -- Check if we found any entities - if min_x == math.huge then - return nil - end - - return { - left_top = {x = min_x, y = min_y}, - right_bottom = {x = max_x, y = max_y}, - width = max_x - min_x, - height = max_y - min_y - } -end - --- Updated function to get factory centroid and update camera smoothly -global.actions.get_factory_centroid = function(player) - -- Default to player force if none specified - local force = "player" - - -- Get all surfaces in the game - local surfaces = game.surfaces - - -- Variables to track totals - local total_x = 0 - local total_y = 0 - local entity_count = 0 - - -- Entity types to exclude from centroid calculation - local excluded_types = { - ["player"] = true, - ["character"] = true, - ["character-corpse"] = true, - ["item-entity"] = true, -- dropped items - ["particle"] = true, - ["projectile"] = true, - ["resource"] = true, -- ore patches - ["tree"] = true, - ["simple-entity"] = true -- rocks and other decorative elements - } - - -- Iterate through all surfaces - for _, surface in pairs(surfaces) do - -- Get all entities on the surface belonging to the specified force - local entities = surface.find_entities_filtered{ - force = force - } - - -- Add up positions of valid entities - for _, entity in pairs(entities) do - if entity.valid and not excluded_types[entity.type] then - total_x = total_x + entity.position.x - total_y = total_y + entity.position.y - entity_count = entity_count + 1 - end - end - end - local centroid = {x=0, y=0} - -- Check if we found any entities - if entity_count > 0 then - -- Calculate average position (centroid) - centroid = { - x = total_x / entity_count, - y = total_y / entity_count - } - end - - -- Calculate bounds of the factory - local bounds = calculate_factory_bounds(force) - - -- Initialize camera tracking if it hasn't been initialized - if not global.camera then - initialize_camera_tracking(centroid) - -- Set initial camera position to centroid - --global.camera.position = {x = centroid.x, y = centroid.y} - --global.camera.target = {x = centroid.x, y = centroid.y} - end - - -- Update camera target - global.camera.target.x = centroid.x - global.camera.target.y = centroid.y - - -- Calculate appropriate zoom based on factory size - if bounds then - -- Calculate the diagonal size of the factory - local diagonal = math.sqrt(bounds.width^2 + bounds.height^2) - -- Determine a good zoom level based on factory size - -- Lower values = zoomed out more - global.camera.target_zoom = math.max(0.15, math.min(0.75, 35 / diagonal)) - end - - -- Update camera position with smooth interpolation - update_camera_position() - - -- Calculate additional statistics - local stats = { - centroid = centroid, - raw_centroid = centroid, - entity_count = entity_count, - bounds = bounds, - camera = { - position = global.camera.position, - zoom = global.camera.zoom - } - } - - return stats -end - --- Function to update camera position with physics-based smoothing -function update_camera_position() - -- Calculate delta between current position and target - local dx = global.camera.target.x - global.camera.position.x - local dy = global.camera.target.y - global.camera.position.y - - -- Apply acceleration based on distance to target - global.camera.velocity.x = global.camera.velocity.x + dx * global.camera.position_smoothing - global.camera.velocity.y = global.camera.velocity.y + dy * global.camera.position_smoothing - - -- Apply velocity damping - global.camera.velocity.x = global.camera.velocity.x * global.camera.velocity_damping - global.camera.velocity.y = global.camera.velocity.y * global.camera.velocity_damping - - -- Clamp velocity to maximum - local velocity_magnitude = math.sqrt(global.camera.velocity.x^2 + global.camera.velocity.y^2) - if velocity_magnitude > global.camera.max_velocity then - local factor = global.camera.max_velocity / velocity_magnitude - global.camera.velocity.x = global.camera.velocity.x * factor - global.camera.velocity.y = global.camera.velocity.y * factor - end - - -- Update position based on velocity - global.camera.position.x = global.camera.position.x + global.camera.velocity.x - global.camera.position.y = global.camera.position.y + global.camera.velocity.y - - -- Smoothly interpolate zoom - local dz = global.camera.target_zoom - global.camera.zoom - global.camera.zoom = global.camera.zoom + dz * global.camera.zoom_smoothing -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_messages.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_messages.lua deleted file mode 100644 index b6f2de2f6..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_messages.lua +++ /dev/null @@ -1 +0,0 @@ --- Left blank intentionally - this functionality is handled by the A2A protocol \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_path.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_path.lua deleted file mode 100644 index b0428fdf8..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_path.lua +++ /dev/null @@ -1,38 +0,0 @@ --- Function to get the path as a JSON object -global.actions.get_path = function(request_id) - local request_data = global.path_requests[request_id] - if not request_data then - return game.table_to_json({status = "\"invalid_request\""}) - end - - if request_data == "pending" then - return game.table_to_json({status = "\"pending\""}) - end - - local path = global.paths[request_id] - if not path then - return game.table_to_json({status = "\"not_found\""}) - end - - if path == "busy" then - return game.table_to_json({status = "\"busy\""}) - elseif path == "not_found" then - return game.table_to_json({status = "\"not_found\""}) - else - local waypoints = {} - for _, waypoint in ipairs(path) do - table.insert(waypoints, { - x = waypoint.position.x, - y = waypoint.position.y - }) - end - -- create a beam bounding box at the start and end of the path - local start = path[1].position - local finish = path[#path].position - --create_beam_bounding_box(player, surface, 1, {x = start.x - 0.5, y = start.y - 0.5}, {x = finish.x + 0.5, y = finish.y + 0.5}) - return game.table_to_json({ - status = "\"success\"", - waypoints = waypoints - }) - end -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_production_stats.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_production_stats.lua deleted file mode 100644 index ae38dbb2c..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/get_production_stats.lua +++ /dev/null @@ -1,48 +0,0 @@ -global.actions.production_stats = function(player) - local production_diff = {} - local consumption_diff = {} - local harvested_items = global.harvested_items - local crafted_items = global.crafted_items - -- Get total production counts for force - local force = game.forces.player - - local item_input_counts = force.item_production_statistics.input_counts - local item_production_counts = force.item_production_statistics.output_counts - local fluid_input_counts = force.fluid_production_statistics.input_counts - local fluid_production_counts = force.fluid_production_statistics.output_counts - - for name, count in pairs(item_input_counts) do - consumption_diff[name] = count - end - - for name, count in pairs(item_production_counts) do - production_diff[name] = count - end - - for name, count in pairs(fluid_input_counts) do - consumption_diff[name] = count - end - - for name, count in pairs(fluid_production_counts) do - production_diff[name] = count - end - return { - output = consumption_diff, - input = production_diff, - harvested = harvested_items, - crafted = crafted_items - } -end - -global.actions.reset_production_stats = function(player) - local force = game.forces.player - -- Reset item statistics - force.item_production_statistics.clear() - - -- Reset fluid statistics - force.fluid_production_statistics.clear() - - global.harvested_items = {} - global.crafted_items = {} -end - diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/inspect_entities.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/inspect_entities.lua deleted file mode 100644 index 72d7639d2..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/inspect_entities.lua +++ /dev/null @@ -1,123 +0,0 @@ - -global.actions.inspect_entities = function(player_index, radius, position_x, position_y) - local player = global.agent_characters[player_index] - local position - - if position_x and position_y then - position = {x = tonumber(position_x), y = tonumber(position_y)} - else - position = player.position - end - - radius = tonumber(radius) or 5 - function find_connected_entities(entity) - local entities_to_search = { - {entity = entity, dx = 0, dy = -1, dir = defines.direction.north}, - {entity = entity, dx = 0, dy = 1, dir = defines.direction.south}, - {entity = entity, dx = 1, dy = 0, dir = defines.direction.east}, - {entity = entity, dx = -1, dy = 0, dir = defines.direction.west} - } - - local connected_entities = {} - - for _, search in ipairs(entities_to_search) do - local search_pos = {x = entity.position.x + search.dx, y = entity.position.y + search.dy} - local found_entity = entity.surface.find_entity(entity.name, search_pos) - - if found_entity then - if entity.type == "transport-belt" and found_entity.direction == search.dir then - table.insert(connected_entities, found_entity) - elseif entity.type == "pipe" then - table.insert(connected_entities, found_entity) - end - end - end - - return connected_entities - end - - - function recursive_search(entity, visited, path_ends) - if visited[entity.unit_number] then - return - end - - visited[entity.unit_number] = true - local connected_entities = find_connected_entities(entity) - - if connected_entities and #connected_entities > 0 then - local unconnected_neighbours = 0 - for _, connected_entity in ipairs(connected_entities) do - if not visited[connected_entity.unit_number] then - unconnected_neighbours = unconnected_neighbours + 1 - recursive_search(connected_entity, visited, path_ends) - end - end - - if unconnected_neighbours == 1 then - path_ends[entity.unit_number] = entity - end - else - path_ends[entity.unit_number] = entity - end - end - - function find_path_ends(entity) - local visited = {} - local path_ends = {} - recursive_search(entity, visited, path_ends) - return path_ends - end - - local entity_data = inspect(player, radius, position) - - local result = {} - - for i, data in ipairs(entity_data) do - - local warnings = {} - if not data.warnings then - data.warnings = {} - end - if data.warnings.fuel_warning then - table.insert(warnings, data.warnings.fuel_warning:gsub(" ", "_")) - end - if data.warnings.output_warning then - table.insert(warnings, data.warnings.output_warning:gsub(" ", "_")) - end - if data.warnings.input_warning then - table.insert(warnings, data.warnings.input_warning:gsub(" ", "_")) - end - - local position = {x=data.position.x, y=data.position.y} - - --game.print(data.name .. ": " .. data.direction .. " - ".. global.utils.get_entity_direction(data.name:gsub("_", "-"), data.direction)) - local entity_info = { - name = data.name:gsub("-", "_"), - position = position, - direction = global.utils.get_entity_direction(data.name:gsub("_", "-"), data.direction), - health = data.health, - force = data.force, - energy = data.energy, - contents = data.contents, - crafting_progress = data.crafting_progress, - productivity_bonus = data.productivity_bonus, - orientation = data.orientation, - ingredients = data.ingredients, - path_ends = data.path_ends, - warnings = data.warnings, - status = data.status - } - -- if crafted_items exists on data - if data.crafted_items then - entity_info.crafted_items = data.crafted_items - end - - --local serialized = global.utils.serialize_entity(data) - --table.insert(result, serialized) - - table.insert(result, entity_info) - end - - return dump(result) -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_blueprint.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_blueprint.lua deleted file mode 100644 index 8209f62f2..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_blueprint.lua +++ /dev/null @@ -1,64 +0,0 @@ -global.actions.load_blueprint = function(player_index, bp, offset_x, offset_y) - --- https://forums.factorio.com/viewtopic.php?t=111437 - --- by CharacterOverflow @ Wed Feb 21, 2024 2:34 pm - - local s = game.get_surface('nauvis'); - local f = global.agent_characters[player_index].force - f.research_all_technologies(); - - -- Creates a blueprint entitiy in-game - it's weird, but it works. This blueprint entity has the blueprint loaded we want to run. - local bp_entity = s.create_entity { name = 'item-on-ground', position = { offset_x, offset_y }, stack = 'blueprint' } - local stack_id = bp_entity.stack.import_stack(bp) - - -- Now we call build_blueprint - this does not do *anything* like what you'd expect though. The force_build options is - -- only there simply to hold 'shift' while the blueprint is placed, meaning the blueprint itself still does not get - -- spawned, only the ghosts do. So, we then have much more actual work to do. - -- #TODO - Try to talk to factorio devs and have them either make an additional function (like auto_build_blueprint) or parameter. Would eliminate most of the lua code below, and likely be a speed boost - local bp_ghost = bp_entity.stack.build_blueprint { surface = s, force = f, position = { offset_x, offset_y }, force_build = true } - bp_entity.destroy() - - -- Go through and spawn the actual entities - except for ones that REQUIRE other entities, such as locomotives - local afterSpawns = {} - for _, entity in pairs(bp_ghost) do - - -- Should change this to go by ghost_type, if that's even a valid field. - if (entity.ghost_name == 'locomotive' or entity.ghost_name == 'cargo-wagon' or entity.ghost_name == 'fluid-wagon') then - table.insert(afterSpawns, entity) - else - if (entity ~= nil and entity.name == 'entity-ghost' and entity.ghost_type ~= nil and entity.item_requests ~= nil) then - local items = util.table.deepcopy(entity.item_requests) - -- game.print(serpent.block(entity.items)) - local p, ri = entity.revive(); - if (ri ~= nil) then - for k, v in pairs(items) do - ri.get_module_inventory().insert({ name = k, count = v }) - end - end - else - -- it's a normal thing like a belt or arm - we can just 'revive' the ghost, which will place the entity with all of the correct settings from the blueprint - entity.revive(); - end - end - - end - - -- This is used to place all locomotives and other train objects AFTER rails have been placed - for _, entity in pairs(afterSpawns) do - local r, to = entity.revive(); - end - - -- Set all trains to AUTOMATIC mode (manual = false) - for _, locomotive in pairs(game.surfaces["nauvis"].get_trains()) do - locomotive.manual_mode = false - end - - -- Add logistic bots to each roboport, based on input. One of the few variables, as some designs may be self-sufficient on bots - if (BOTS ~= nil and BOTS > 0) then - for _, roboport in pairs(game.surfaces["nauvis"].find_entities_filtered({ type = "roboport" })) do - roboport.insert({ name = "logistic-robot", count = BOTS }) - end - end - - return stack_id - -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_entity_state.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_entity_state.lua deleted file mode 100644 index 8d78b6ec2..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_entity_state.lua +++ /dev/null @@ -1,289 +0,0 @@ --- Helper function to unquote strings -local function unquote_string(str) - if not str then return nil end - return string.gsub(str, '"', '') -end - --- Main deserialization function -global.actions.load_entity_state = function(player, stored_json_data) - local player_entity = global.agent_characters[player] - local surface = player_entity.surface - local created_entities = {} - local stored_data = game.json_to_table(stored_json_data) - local character_states = {} - -- First pass: Create all non-character entities and store character states - for _, state in pairs(stored_data) do - local name = unquote_string(state.name) - - if name == "character" then - table.insert(character_states, state) - elseif name == "item-on-ground" then - local item_name = unquote_string(state.type) - local item_count = tonumber(state.count) - - if game.item_prototypes[item_name] then - local entity = surface.create_entity({ - name = name, - position = { - x = tonumber(state.position.x), - y = tonumber(state.position.y) - }, - stack = { - name = item_name, - count = item_count - }, - force = game.forces.player - }) - else - -- game.print("Warning: Unknown item type " .. item_name) - end - elseif state.type == "simple-entity-with-owner" then - -- Do nothing, we don't want to load in placeholder entities if they were somehow persisted! - else - local entity = surface.create_entity({ - name = name, - position = { - x = tonumber(state.position.x), - y = tonumber(state.position.y) - }, - direction = tonumber(state.direction), - force = game.forces.player, - raise_built = true - }) - - if entity then - created_entities[state.entity_number] = { - entity = entity, - state = state - } - end - end - end - - -- Handle characters separately - for _, character_state in ipairs(character_states) do - -- Store old character position if it exists - local old_position = nil - local agent_index = character_state.agent_index - local old_character = nil - - -- If we have a valid agent_index, get the old character from global.agent_characters - if agent_index and agent_index > 0 and global.agent_characters[agent_index] then - old_character = global.agent_characters[agent_index] - if old_character then - old_position = old_character.position - old_character.destroy() - end - end - - -- Create new character at the stored position or old position - local position = { - x = tonumber(character_state.position.x), - y = tonumber(character_state.position.y) - } - if not surface.can_place_entity{name="character", position=position} then - position = old_position or {x=0, y=0} - end - - local new_character = surface.create_entity({ - name = "character", - position = position, - direction = tonumber(character_state.direction), - force = game.forces.player, - }) - - if new_character then - -- Update global.agent_characters if we have a valid agent_index - if agent_index and agent_index > 0 then - global.agent_characters[agent_index] = new_character - end - - -- Restore character color if it exists - if character_state.color then - new_character.color = { - r = tonumber(character_state.color.r), - g = tonumber(character_state.color.g), - b = tonumber(character_state.color.b), - a = tonumber(character_state.color.a) - } - end - - -- Restore character inventory - if character_state.inventory then - local main_inventory = new_character.get_inventory(defines.inventory.character_main) - if main_inventory then - for item_name, count in pairs(character_state.inventory) do - -- Remove quotes if they exist - item_name = unquote_string(item_name) - if item_name and item_name ~= "" then - if game.item_prototypes[item_name] then - main_inventory.insert({ - name = item_name, - count = tonumber(count) - }) - else - -- game.print("Warning: Unknown item " .. item_name) - end - end - end - else - -- game.print("Warning: Could not get character main inventory") - end - end - - -- Add character to created_entities for inventory restoration - created_entities[character_state.entity_number] = { - entity = new_character, - state = character_state - } - end - end - - -- Second pass: Restore states - for unit_number, data in pairs(created_entities) do - local entity = data.entity - local state = data.state - local entity_type = entity.type - - -- game.print("Processing entity: " .. entity.name .. " (type: " .. entity_type .. ")") - - -- Restore inventories based on entity type - for inv_name, contents in pairs(state.inventories or {}) do - local inventory = nil - - -- Only try to access inventories that match the entity type - if inv_name == "chest" and entity_type == "container" then - inventory = entity.get_inventory(defines.inventory.chest) - elseif inv_name == "furnace_source" and entity_type == "furnace" then - inventory = entity.get_inventory(defines.inventory.furnace_source) - elseif inv_name == "furnace_result" and entity_type == "furnace" then - inventory = entity.get_inventory(defines.inventory.furnace_result) - elseif inv_name == "fuel" and entity.burner then - inventory = entity.get_inventory(defines.inventory.fuel) - elseif inv_name == "burnt_result" and entity.burner then - inventory = entity.get_inventory(defines.inventory.burnt_result) - elseif inv_name == "assembling_machine_input" and entity_type == "assembling-machine" then - inventory = entity.get_inventory(defines.inventory.assembling_machine_input) - elseif inv_name == "assembling_machine_output" and entity_type == "assembling-machine" then - inventory = entity.get_inventory(defines.inventory.assembling_machine_output) - elseif inv_name == "turret_ammo" and entity_type == "ammo-turret" then - inventory = entity.get_inventory(defines.inventory.turret_ammo) - elseif inv_name == "lab_input" and entity_type == "lab" then - inventory = entity.get_inventory(defines.inventory.lab_input) - end - - if inventory then - -- game.print("Found valid inventory for " .. inv_name) - for quoted_item_name, count in pairs(contents) do - local item_name = unquote_string(quoted_item_name) - if item_name and item_name ~= "" then - if game.item_prototypes[item_name] then - -- game.print("Inserting " .. count .. " " .. item_name) - inventory.insert({ - name = item_name, - count = tonumber(count) - }) - else - -- game.print("Warning: Unknown item " .. item_name) - end - else - -- game.print("Warning: Empty item name in " .. inv_name) - end - end - else - -- game.print("No valid inventory found for " .. inv_name) - end - end - - -- Restore burner - if state.burner and entity.burner then - if state.burner.currently_burning then - local burning_name = unquote_string(state.burner.currently_burning) - if game.item_prototypes[burning_name] then -- Verify burning item exists - entity.burner.currently_burning = game.item_prototypes[burning_name] - entity.burner.remaining_burning_fuel = tonumber(state.burner.remaining_burning_fuel) - entity.burner.heat = tonumber(state.burner.heat) - else - -- game.print("Warning: Unknown burning item " .. burning_name) - end - end - end - - -- Restore recipe - only for entities that can have recipes - if state.recipe then - -- Only try to set recipe on appropriate entity types - if (entity.type == "assembling-machine" or - entity.type == "furnace" or - entity.type == "rocket-silo") and - entity.get_recipe then -- Double check entity supports recipes - - local recipe_name = unquote_string(state.recipe.name) - if game.recipe_prototypes[recipe_name] then - -- game.print("Setting recipe " .. recipe_name .. " on " .. entity.name) - pcall(function() - entity.set_recipe(recipe_name) - end) - else - -- game.print("Warning: Unknown recipe " .. recipe_name) - end - else - -- game.print("Warning: Skipping recipe for incompatible entity type: " .. entity.type) - end - end - - -- Restore transport belt contents - if entity_type == "transport-belt" and state.transport_lines then - -- game.print("Has transport_lines field: " .. tostring(state.transport_lines ~= nil)) - -- Handle front line (line 1) - local line1 = entity.get_transport_line(1) - - if state.transport_lines["1"] then - -- game.print("Transport line 1") - for item_name, count in pairs(state.transport_lines["1"]) do - local name = unquote_string(item_name) - local item_count = tonumber(count) - if game.item_prototypes[name] then - for i = 1, item_count do - -- Space items evenly along the belt - local position = (i - 1) / item_count - line1.insert_at(position, { - name = name, - count = 1 - }) - end - end - end - end - - -- Handle back line (line 2) - local line2 = entity.get_transport_line(2) - if state.transport_lines["2"] then - -- game.print("Transport line 2") - for item_name, count in pairs(state.transport_lines["2"]) do - local name = unquote_string(item_name) - local item_count = tonumber(count) - if game.item_prototypes[name] then - for i = 1, item_count do - -- Space items evenly along the belt - local position = (i - 1) / item_count - line2.insert_at(position, { - name = name, - count = 1 - }) - end - end - end - end - end - - -- Restore energy and active state - if state.energy then - entity.energy = tonumber(state.energy) - end - if state.active ~= nil then - entity.active = state.active - end - end - - return true -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_research_state.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_research_state.lua deleted file mode 100644 index 966ea298a..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/load_research_state.lua +++ /dev/null @@ -1,48 +0,0 @@ --- Load research state -global.actions.load_research_state = function(player_index, research_state) - local player = global.agent_characters[player_index] - local force = player.force - - -- Reset all research first - for _, tech in pairs(force.technologies) do - tech.researched = false - force.set_saved_technology_progress(tech.name, nil) - --tech.enabled = false - end - - -- Apply saved research states - for name, tech_state in pairs(research_state.technologies) do - local tech = force.technologies[name] - if tech then - tech.researched = tech_state.researched - tech.enabled = tech_state.enabled - -- Level might need special handling for infinite research - if tech.level ~= tech_state.level then - tech.level = tech_state.level - end - end - end - - for name, progress in pairs(research_state.progress) do - force.set_saved_technology_progress(name, progress) - end - - -- Restore current research and progress - if research_state.current_research then - --if force.current_research then - -- force.cancel_current_research() - --end - force.add_research(research_state.current_research) - force.research_progress = research_state.research_progress - end - - -- Restore research queue if supported - if force.research_queue_enabled and research_state.research_queue then - force.research_queue = {} - for _, tech_name in ipairs(research_state.research_queue) do - force.add_research(tech_name) - end - end - - return true -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/production_stats.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/production_stats.lua deleted file mode 100644 index ae38dbb2c..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/production_stats.lua +++ /dev/null @@ -1,48 +0,0 @@ -global.actions.production_stats = function(player) - local production_diff = {} - local consumption_diff = {} - local harvested_items = global.harvested_items - local crafted_items = global.crafted_items - -- Get total production counts for force - local force = game.forces.player - - local item_input_counts = force.item_production_statistics.input_counts - local item_production_counts = force.item_production_statistics.output_counts - local fluid_input_counts = force.fluid_production_statistics.input_counts - local fluid_production_counts = force.fluid_production_statistics.output_counts - - for name, count in pairs(item_input_counts) do - consumption_diff[name] = count - end - - for name, count in pairs(item_production_counts) do - production_diff[name] = count - end - - for name, count in pairs(fluid_input_counts) do - consumption_diff[name] = count - end - - for name, count in pairs(fluid_production_counts) do - production_diff[name] = count - end - return { - output = consumption_diff, - input = production_diff, - harvested = harvested_items, - crafted = crafted_items - } -end - -global.actions.reset_production_stats = function(player) - local force = game.forces.player - -- Reset item statistics - force.item_production_statistics.clear() - - -- Reset fluid statistics - force.fluid_production_statistics.clear() - - global.harvested_items = {} - global.crafted_items = {} -end - diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/regenerate_resources.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/regenerate_resources.lua deleted file mode 100644 index 0c49c7021..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/regenerate_resources.lua +++ /dev/null @@ -1,37 +0,0 @@ -global.actions.regenerate_resources = function(player_index) - local player = global.agent_characters[player_index] - local surface = player.surface - for _, ore in pairs(surface.find_entities_filtered({type="resource"})) do - -- skip oil - if ore.name ~= "crude-oil" then - ore.amount = 10000 - else - ore.amount = 300000 - end - end - player.force.reset() -end - -global.actions.regenerate_resources2 = function(player_index) - local player = global.agent_characters[player_index] - - local surface = player.surface - for _, e in pairs(surface.find_entities_filtered{type="resource"}) do - if e.prototype.infinite_resource then - e.amount = e.initial_amount - else - e.destroy() - end - end - local non_infinites = {} - for resource, prototype in pairs(game.get_filtered_entity_prototypes{{filter="type", type="resource"}}) do - if not prototype.infinite_resource then - table.insert(non_infinites, resource) - end - end - surface.regenerate_entity(non_infinites) - for _, e in pairs(surface.find_entities_filtered{type="mining-drill"}) do - e.update_connections() - end - return 1 -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render.lua deleted file mode 100644 index 0f7481627..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render.lua +++ /dev/null @@ -1,157 +0,0 @@ -global.actions.render = function(player_index, method, arg1, arg2, arg3, arg4) - local player = global.agent_characters[player_index] - local position, bounding_box, radius - - if method == "bounding_box" then - -- If using bounding box method - bounding_box = { - left_top = {x = tonumber(arg1), y = tonumber(arg2)}, - right_bottom = {x = tonumber(arg3), y = tonumber(arg4)} - } - elseif method == "radius" then - -- If using radius method - local pos_x = tonumber(arg1) - local pos_y = tonumber(arg2) - radius = tonumber(arg3) or 40 - - position = {x = pos_x, y = pos_y} - bounding_box = { - left_top = {x = position.x - radius, y = position.y - radius}, - right_bottom = {x = position.x + radius, y = position.y + radius} - } - else - return "\"Error: Invalid method\"" - end - - -- Get all entities - local area = { - {bounding_box.left_top.x, bounding_box.left_top.y}, - {bounding_box.right_bottom.x, bounding_box.right_bottom.y} - } - - -- Get water tiles - local water_tiles = {} - for x = math.floor(area[1][1]), math.ceil(area[2][1]) do - for y = math.floor(area[1][2]), math.ceil(area[2][2]) do - local tile = player.surface.get_tile(x, y) - if tile and tile.valid and tile.name and (tile.name:find("water") or tile.name == "deepwater" or tile.name == "water") then - table.insert(water_tiles, { - x = tile.position.x, - y = tile.position.y, - name = "\""..tile.name.."\"" - }) - end - end - end - - -- Get resource entities - local resource_types = {"iron-ore", "copper-ore", "coal", "stone", "uranium-ore", "crude-oil"} - local resources = {} - - for _, resource_type in ipairs(resource_types) do - local resource_entities = player.surface.find_entities_filtered{ - area = area, - name = resource_type - } - - for _, entity in ipairs(resource_entities) do - local resource_data = { - name = "\""..entity.name.."\"", - position = { - x = entity.position.x, - y = entity.position.y - }, - amount = entity.amount - } - - table.insert(resources, resource_data) - end - end - - -- Get trees - local trees = {} - local tree_entities = player.surface.find_entities_filtered{ - area = area, - type = "tree" - } - - for _, tree in ipairs(tree_entities) do - local tree_data = { - name = "\""..tree.name.."\"", - position = { - x = tree.position.x, - y = tree.position.y - } - } - - -- Add tree size if available - if tree.prototype and tree.prototype.tree_color_count then - tree_data.size = tree.prototype.tree_color_count - end - - table.insert(trees, tree_data) - end - - -- Get rocks - local rocks = {} - local rock_entities = player.surface.find_entities_filtered{ - area = area, - type = "simple-entity" - } - - for _, rock in ipairs(rock_entities) do - -- Check if it's actually a rock (naming convention for rocks typically includes "rock" or "stone") - if rock.name and (rock.name:find("rock") or rock.name:find("stone")) then - local rock_data = { - name = "\""..rock.name.."\"", - position = { - x = rock.position.x, - y = rock.position.y - } - } - - table.insert(rocks, rock_data) - end - end - - -- Get electricity network information - local electricity_networks = {} - local electric_poles = player.surface.find_entities_filtered{ - area = area, - type = {"electric-pole", "power-switch"} - } - - -- Process electric poles to get network information - for _, pole in ipairs(electric_poles) do - if pole.valid and pole.electric_network_id then - local network_id = pole.electric_network_id - local supply_area = pole.prototype.supply_area_distance or 0 - - -- Get the area this pole covers - local pole_data = { - position = { - x = pole.position.x, - y = pole.position.y - }, - network_id = network_id, - supply_area = supply_area, - name = "\""..pole.name.."\"" - } - - table.insert(electricity_networks, pole_data) - end - end - - -- Combine all data for response - local render_data = { - water_tiles = water_tiles, - resources = resources, - trees = trees, - rocks = rocks, - electricity_networks = electricity_networks, - bounding_box = bounding_box, - position = position - } - - return dump(render_data) -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render_message.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render_message.lua deleted file mode 100644 index 2a1df3622..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/render_message.lua +++ /dev/null @@ -1,18 +0,0 @@ -global.actions.render_message = function(player_index, message) - -- Get color based on player index - local color = {r = 1, g = 1, b = 1} -- Default white - if player_index == 1 then - color = {r = 0.6, g = 1, b = 0.6} -- Light green - message = "Agent 1: " .. message - elseif player_index == 2 then - color = {r = 0.6, g = 0.6, b = 1} -- Light blue - message = "Agent 2: " .. message - elseif player_index == 3 then - color = {r = 1, g = 0.6, b = 0.6} -- Light red - message = "Agent 3: " .. message - end - - -- Print message with color - game.print(message, color) - return true -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/request_path.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/request_path.lua deleted file mode 100644 index 072fc3805..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/request_path.lua +++ /dev/null @@ -1,118 +0,0 @@ --- Store created entities globally -if not global.clearance_entities then - global.clearance_entities = {} -end - -global.actions.request_path = function(player_index, start_x, start_y, goal_x, goal_y, radius, allow_paths_through_own_entities, entity_size) - local player = global.agent_characters[player_index] - if not player then return nil end - local size = entity_size/2 - 0.01 - - local surface = player.surface - local force = player.force - - -- Define region for pipe checking (add some margin around start/goal) - local region = { - left_top = { - x = math.min(start_x, goal_x) - 10, - y = math.min(start_y, goal_y) - 10 - }, - right_bottom = { - x = math.max(start_x, goal_x) + 10, - y = math.max(start_y, goal_y) + 10 - } - } - rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0.5, g = 0, b = 0.5}, surface = player.surface, radius = 0.303, filled = false, target = {x=start_x, y=start_y}, time_to_live = 12000} - rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 0.5, b = 0.5}, surface = player.surface, radius = 0.303, filled = false, target = {x=goal_x, y=goal_y }, time_to_live = 12000} - - -- Add temporary collision entities - local clearance_entities = {} - global.utils.avoid_entity(player_index, "iron-chest", {y = goal_y, x = goal_x}) - - local goal_position = player.surface.find_non_colliding_position( - "iron-chest", - {y = goal_y, x = goal_x}, - 10, - 0.5, - true - ) - - local start_position = {y = start_y, x = start_x} - - local path_request = { - bounding_box = {{-size, -size}, {size, size}}, - collision_mask = { - "player-layer", - "train-layer", - "consider-tile-transitions", - "water-tile", - "object-layer", - "transport-belt-layer", - "water-tile" - }, - start = start_position, - goal = goal_position, - force = force, - radius = radius or 0, - entity_to_ignore = player, - can_open_gates = true, - path_resolution_modifier = resolution, - pathfind_flags = { - cache = false, - no_break = true, - prefer_straight_paths = true, - allow_paths_through_own_entities = allow_paths_through_own_entities - } - } - - local request_id = surface.request_path(path_request) - - global.clearance_entities[request_id] = clearance_entities - - if not global.path_requests then - global.path_requests = {} - end - if not global.paths then - global.paths = {} - end - - global.path_requests[request_id] = player_index - - return request_id -end - --- Modify the pathfinding finished handler to clean up entities ---script.on_event(defines.events.on_script_path_request_finished, function(event) --- -- Clean up clearance entities --- if global.clearance_entities[event.id] then --- for _, entity in pairs(global.clearance_entities[event.id]) do --- if entity.valid then --- entity.destroy() --- end --- end --- global.clearance_entities[event.id] = nil --- end ---end) - -script.on_event(defines.events.on_script_path_request_finished, function(event) - local request_data = global.path_requests[event.id] - if not request_data then - return - end - - local player = global.agent_characters[request_data] - if not player then - return - end - - if event.path then - global.paths[event.id] = event.path - log("Path found for request ID: " .. event.id) - elseif event.try_again_later then - global.paths[event.id] = "busy" - log("Pathfinder busy for request ID: " .. event.id) - else - global.paths[event.id] = "not_found" - log("Path not found for request ID: " .. event.id) - end -end) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_blueprint.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_blueprint.lua deleted file mode 100644 index 09be818ec..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_blueprint.lua +++ /dev/null @@ -1,61 +0,0 @@ -global.actions.save_blueprint = function(player_index) - local player = global.agent_characters[player_index] - local force = player.force - if not force then - return nil, "Force not found" - end - - local surface = game.surfaces[1] -- Get primary surface - local entities = surface.find_entities_filtered{force = force} - - if #entities == 0 then - return nil, "No entities found for force" - end - - -- Calculate bounding box containing all force entities - local min_x, min_y = math.huge, math.huge - local max_x, max_y = -math.huge, -math.huge - - for _, entity in pairs(entities) do - local pos = entity.position - min_x = math.min(min_x, pos.x) - min_y = math.min(min_y, pos.y) - max_x = math.max(max_x, pos.x) - max_y = math.max(max_y, pos.y) - end - - -- Calculate center point of the blueprint - local center_x = (min_x + max_x) / 2 - local center_y = (min_y + max_y) / 2 - - -- Add some padding to the box - local area = { - left_top = {x = min_x - 1, y = min_y - 1}, - right_bottom = {x = max_x + 1, y = max_y + 1} - } - - -- Create blueprint item stack - local bp = player.cursor_stack - bp.set_stack({name = "blueprint"}) - bp.label = "Saved Entities" - bp.allow_manual_label_change = true - - -- Create blueprint from area - bp.create_blueprint{ - surface = surface, - force = force, - area = area, - include_entities = true, - include_modules = true, - include_station_names = true, - include_trains = true, - include_fuel = true - } - - local stack_string = bp.export_stack() - - -- Clear cursor and delete blueprint - bp.clear() - - return dump({blueprint='\"'..stack_string..'\"', center_x=center_x, center_y=center_y}) -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_entity_state.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_entity_state.lua deleted file mode 100644 index a3c7b9c95..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_entity_state.lua +++ /dev/null @@ -1,383 +0,0 @@ --- Helper to ensure all numbers are serializable -local function serialize_number(num) - if num == math.huge then - return "inf" - elseif num == -math.huge then - return "-inf" - else - return tostring(num) - end -end - --- Helper to serialize position to fixed format -local function serialize_position(pos) - return { - x = serialize_number(pos.x), - y = serialize_number(pos.y) - } -end - -local function serialize_recipe_info(recipe) - if not recipe then return nil end - - local ingredients = {} - for _, ingredient in pairs(recipe.ingredients) do - table.insert(ingredients, { - name = '"' .. ingredient.name .. '"', - amount = serialize_number(ingredient.amount), - type = '"' .. ingredient.type .. '"' - }) - end - - local products = {} - for _, product in pairs(recipe.products) do - table.insert(products, { - name = '"' .. product.name .. '"', - amount = serialize_number(product.amount), - type = '"' .. product.type .. '"', - probability = product.probability and serialize_number(product.probability) or "1" - }) - end - - return { - name = '"' .. recipe.name .. '"', - category = '"' .. recipe.category .. '"', - enabled = recipe.enabled, - energy = serialize_number(recipe.energy), - ingredients = ingredients, - products = products - } -end - --- Main serialization function -global.actions.save_entity_state = function(player_index, distance, player_entities, resource_entities, items_on_ground) - local surface = global.agent_characters[player_index].surface - if player_entities then - entities = surface.find_entities_filtered({area={{-distance, -distance}, {distance, distance}}, force=global.agent_characters[player_index].force}) - else - if resource_entities then - entities = surface.find_entities({{-distance, -distance}, {distance, distance}}) - else - entities = surface.find_entities_filtered({area={{-distance, -distance}, {distance, distance}}, force=global.agent_characters[player_index].force}) - end - end - - -- Always get items on ground if requested - if items_on_ground then - local ground_items = surface.find_entities_filtered({ - area = {{-distance, -distance}, {distance, distance}}, - name = "item-on-ground" - }) - -- Merge the arrays - for _, item in pairs(ground_items) do - table.insert(entities, item) - end - end - - local entity_states = {} - local entity_array = {} - for _, entity in pairs(entities) do - - -- Serialize inventories by type - local inventory_defines = { - chest = defines.inventory.chest, - furnace_source = defines.inventory.furnace_source, - furnace_result = defines.inventory.furnace_result, - fuel = defines.inventory.fuel, - burnt_result = defines.inventory.burnt_result, - assembling_machine_input = defines.inventory.assembling_machine_input, - assembling_machine_output = defines.inventory.assembling_machine_output, - turret_ammo = defines.inventory.turret_ammo, - lab_input = defines.inventory.lab_input, - lab_modules = defines.inventory.lab_modules, - assembling_machine_modules = defines.inventory.assembling_machine_modules - } - - if entity.name == "item-on-ground" then - -- Capture the item details - local item = entity.stack - if item and item.valid then - local state = { - name = '\"item-on-ground\"', - type = '"' .. item.name .. '"', - count = serialize_number(item.count), - position = serialize_position(entity.position), - } - table.insert(entity_array, state) - end - elseif entity.name ~= "character" then - local state = { - name = '"' .. entity.name .. '"', - position = serialize_position(entity.position), - direction = entity.direction, - entity_number = entity.unit_number or -1, - type = '"' .. entity.type .. '"', - health = serialize_number(entity.health), - energy = serialize_number(entity.energy or 0), - active = entity.active, - status = '"' .. (global.entity_status_names[entity.status] or "normal") .. '"', - warnings = {}, - inventories = {} - } - - -- Add any warnings - for _, warning in pairs(get_issues(entity) or {}) do - table.insert(state.warnings, '"' .. warning .. '"') - end - - -- Handle dimensions - local prototype = game.entity_prototypes[entity.name] - if prototype then - local collision_box = prototype.collision_box - state.dimensions = { - width = serialize_number(math.abs(collision_box.right_bottom.x - collision_box.left_top.x)), - height = serialize_number(math.abs(collision_box.right_bottom.y - collision_box.left_top.y)) - } - state.tile_dimensions = { - tile_width = serialize_number(prototype.tile_width), - tile_height = serialize_number(prototype.tile_height) - } - end - - for name, define in pairs(inventory_defines) do - local inventory = entity.get_inventory(define) - if inventory then - state.inventories[name] = {} - -- Get contents with proper item names - local contents = inventory.get_contents() - for item_name, count in pairs(contents) do - if item_name and item_name ~= "" then -- Ensure valid item name - state.inventories[name][tostring(item_name)] = serialize_number(count) - end - end - end - end - - -- Handle fluids - if entity.fluidbox then - state.fluid_box = {} - for i = 1, #entity.fluidbox do - local fluid = entity.fluidbox[i] - if fluid then - table.insert(state.fluid_box, { - name = '"' .. fluid.name .. '"', - amount = serialize_number(fluid.amount), - temperature = serialize_number(fluid.temperature) - }) - end - end - end - - -- Handle burner state - if entity.burner then - state.burner = { - currently_burning = entity.burner.currently_burning and - '"' .. entity.burner.currently_burning.name .. '"' or nil, - remaining_burning_fuel = serialize_number(entity.burner.remaining_burning_fuel or 0), - heat = serialize_number(entity.burner.heat or 0) - } - - -- Add burner inventory with proper item names - local burner_inventory = entity.burner.inventory - if burner_inventory then - state.burner.inventory = {} - local contents = burner_inventory.get_contents() - --game.print("get_contents() results:") - for item_name, count in pairs(contents) do - if item_name and item_name ~= "" then -- Ensure valid item name - --game.print("Item: '" .. tostring(item_name) .. "' Count: " .. tostring(count)) - --state.burner.inventory['\"' .. tostring(item_name) .. '\"'] = serialize_number(count) - state.burner.inventory[tostring(item_name)] = serialize_number(count) - end - end - end - end - - -- Handle recipe - only for crafting machines and furnaces - if (entity.type == "assembling-machine" or - entity.type == "furnace" or - entity.type == "rocket-silo") and - entity.get_recipe then - local recipe = entity.get_recipe() - if recipe then - state.recipe = serialize_recipe_info(recipe) - end - end - - -- Handle specific entity types - --if entity.type == "transport-belt" then - -- state.input_position = serialize_position(entity.position) - -- state.output_position = serialize_position(entity.position) - -- -- Add belt contents - -- state.inventory = {} - -- for name, count in pairs(entity.get_transport_line(1).get_contents()) do - -- state.inventory[tostring(name)] = serialize_number(count) - -- end - if entity.type == "transport-belt" then - --state.input_position = serialize_position(entity.input_position) - --state.output_position = serialize_position(entity.output_position) - state.position = serialize_position(entity.position) - - -- Store contents of each transport line separately - state.transport_lines = {} - - -- Front line (line 1) - state.transport_lines[1] = {} - local contents1 = entity.get_transport_line(1).get_contents() - for name, count in pairs(contents1) do - state.transport_lines[1][tostring(name)] = serialize_number(count) - end - - -- Back line (line 2) - state.transport_lines[2] = {} - local contents2 = entity.get_transport_line(2).get_contents() - for name, count in pairs(contents2) do - state.transport_lines[2][tostring(name)] = serialize_number(count) - end - - -- Total counts (for backwards compatibility) - state.inventory = {} - for name, count in pairs(contents1) do - state.inventory[tostring(name)] = serialize_number(count) - end - for name, count in pairs(contents2) do - local existing = state.inventory[tostring(name)] - if existing then - state.inventory[tostring(name)] = serialize_number(tonumber(existing) + count) - else - state.inventory[tostring(name)] = serialize_number(count) - end - end - - elseif entity.type == "inserter" then - state.pickup_position = serialize_position(entity.pickup_position) - state.drop_position = serialize_position(entity.drop_position) - - elseif entity.type == "splitter" then - -- Calculate splitter positions based on orientation - local x, y = entity.position.x, entity.position.y - state.input_positions = {} - state.output_positions = {} - local lateral_offset = 0.5 - - if entity.direction == defines.direction.north then - state.input_positions = { - serialize_position({x = x - lateral_offset, y = y + 1}), - serialize_position({x = x + lateral_offset, y = y + 1}) - } - state.output_positions = { - serialize_position({x = x - lateral_offset, y = y - 1}), - serialize_position({x = x + lateral_offset, y = y - 1}) - } - elseif entity.direction == defines.direction.south then - state.input_positions = { - serialize_position({x = x + lateral_offset, y = y - 1}), - serialize_position({x = x - lateral_offset, y = y - 1}) - } - state.output_positions = { - serialize_position({x = x + lateral_offset, y = y + 1}), - serialize_position({x = x - lateral_offset, y = y + 1}) - } - elseif entity.direction == defines.direction.east then - state.input_positions = { - serialize_position({x = x - 1, y = y - lateral_offset}), - serialize_position({x = x - 1, y = y + lateral_offset}) - } - state.output_positions = { - serialize_position({x = x + 1, y = y - lateral_offset}), - serialize_position({x = x + 1, y = y + lateral_offset}) - } - elseif entity.direction == defines.direction.west then - state.input_positions = { - serialize_position({x = x + 1, y = y + lateral_offset}), - serialize_position({x = x + 1, y = y - lateral_offset}) - } - state.output_positions = { - serialize_position({x = x - 1, y = y + lateral_offset}), - serialize_position({x = x - 1, y = y - lateral_offset}) - } - end - - -- Serialize splitter inventories - state.inventory = {} - for i = 1, 2 do - state.inventory[i] = {} - for name, count in pairs(entity.get_transport_line(i).get_contents()) do - state.inventory[i][tostring(name)] = serialize_number(count) - end - end - - elseif entity.type == "mining-drill" then - state.drop_position = serialize_position(entity.drop_position) - - elseif entity.type == "boiler" then - -- Add connection points - local x, y = entity.position.x, entity.position.y - if entity.direction == defines.direction.north then - state.connection_points = { - serialize_position({x = x - 2, y = y + 0.5}), - serialize_position({x = x + 2, y = y + 0.5}) - } - state.steam_output_point = serialize_position({x = x, y = y - 2}) - elseif entity.direction == defines.direction.south then - state.connection_points = { - serialize_position({x = x - 2, y = y - 0.5}), - serialize_position({x = x + 2, y = y - 0.5}) - } - state.steam_output_point = serialize_position({x = x, y = y + 2}) - elseif entity.direction == defines.direction.east then - state.connection_points = { - serialize_position({x = x - 0.5, y = y - 2}), - serialize_position({x = x - 0.5, y = y + 2}) - } - state.steam_output_point = serialize_position({x = x + 2, y = y}) - elseif entity.direction == defines.direction.west then - state.connection_points = { - serialize_position({x = x + 0.5, y = y - 2}), - serialize_position({x = x + 0.5, y = y + 2}) - } - state.steam_output_point = serialize_position({x = x - 2, y = y}) - end - end - - table.insert(entity_array, state) - else - -- Find the index of this character in global.agent_characters - agent_index = -1 - for idx, agent in pairs(global.agent_characters) do - if agent == entity then - agent_index = idx - break - end - end - local state = { - name = '"' .. entity.name .. '"', - position = serialize_position(entity.position), - direction = entity.direction, - entity_number = entity.unit_number or -1, - inventories = {}, - agent_index = agent_index, - color = { - r = serialize_number(entity.color.r), - g = serialize_number(entity.color.g), - b = serialize_number(entity.color.b), - a = serialize_number(entity.color.a) - } - } - -- Get the character's inventory using defines - local inventory = entity.get_inventory(defines.inventory.character_main) - - if inventory then - state.inventory = {} - local contents = inventory.get_contents() - for item_name, count in pairs(contents) do - if item_name and item_name ~= "" then - state.inventory[tostring(item_name)] = serialize_number(count) - end - end - end - table.insert(entity_array, state) - end - end - return entity_array -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_research_state.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_research_state.lua deleted file mode 100644 index 728708532..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/admin/save_research_state.lua +++ /dev/null @@ -1,62 +0,0 @@ -global.actions.save_research_state = function(player_index) - local player = global.agent_characters[player_index] - local force = player.force - - -- Helper to serialize technology state - local function serialize_technology(tech) - local prerequisites = {} - for name, _ in pairs(tech.prerequisites) do - table.insert(prerequisites, "\""..name.."\"") - end - - local ingredients = {} - for _, ingredient in pairs(tech.research_unit_ingredients) do - table.insert(ingredients, { - name = "\""..ingredient.name.."\"", - amount = ingredient.amount - }) - end - - return { - name = "\""..tech.name.."\"", - researched = tech.researched, - enabled = tech.enabled, - -- visible = tech.visible, - level = tech.level, - research_unit_count = tech.research_unit_count, - research_unit_energy = tech.research_unit_energy, - prerequisites = prerequisites, - ingredients = ingredients, - --saved_progress = saved_progress -- This will be nil if no progress is saved - } - end - - local research_state = { - technologies = {}, - current_research = nil, - research_progress = 0, - research_queue = {}, - progress = {} - } - - -- Save all technology states - for name, tech in pairs(force.technologies) do - research_state.technologies[name] = serialize_technology(tech) - end - - -- Save current research and progress - if force.current_research then - research_state.current_research = "\""..force.current_research.name.."\"" - research_state.research_progress = force.research_progress - - research_state.progress[force.current_research.name] = force.research_progress or 0 - end - - -- Save research queue if it exists - if force.research_queue then - for _, tech in pairs(force.research_queue) do - table.insert(research_state.research_queue, "\""..tech.name.."\"") - end - end - return research_state -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/can_place_entity.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/can_place_entity.lua deleted file mode 100644 index 439351f47..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/can_place_entity.lua +++ /dev/null @@ -1,84 +0,0 @@ -global.actions.can_place_entity = function(player_index, entity, direction, x, y) - local player = global.agent_characters[player_index] - local position = {x = x, y = y} - - ---- Check player's reach distance - local dx = player.position.x - x - local dy = player.position.y - y - local distance = math.sqrt(dx * dx + dy * dy) - - if distance > player.reach_distance then - error("The distance to the target position is too far away to place the entity (" ..distance.."). Move closer.") - end - - -- if not global.actions.can_reach_entity(player, x, y) then - -- error("The target position is too far away to place the entity. The player position is " .. player.position.x .. ", " .. player.position.y .. " and the target position is " .. x .. ", " .. y .. ". Move closer.") - -- end - - -- Check entity prototype exists - if game.entity_prototypes[entity] == nil then - local name = entity:gsub(" ", "_"):gsub("-", "_") - error(name .. " isn't a valid entity prototype. Did you make a typo?") - end - - -- Check inventory for the entity - if player.get_item_count(entity) == 0 then - local name = entity:gsub(" ", "_"):gsub("-", "_") - error("No " .. name .. " in inventory.") - end - - -- Get entity direction and collision box - local direction_enum = get_entity_direction(entity, direction) - local prototype = game.entity_prototypes[entity] - local collision_box = prototype.collision_box - local width = math.abs(collision_box.right_bottom.x - collision_box.left_top.x) - local height = math.abs(collision_box.right_bottom.y - collision_box.left_top.y) - - -- Define the area where the entity will be placed - local target_area = { - {x = position.x - width / 2, y = position.y - height / 2}, - {x = position.x + width / 2, y = position.y + height / 2} - } - - -- Check for collision with other entities - local entities = player.surface.find_entities_filtered{area = target_area, force = player.force} - - -- iterate over entities and remove any with the player_character name - for i = #entities, 1, -1 do - if entities[i].name == "character" or entities[i].name == 'item-on-ground' then - table.remove(entities, i) - end - end - if #entities > 1 then - error("Cannot place the entity at the specified location due to collision with other entities.") - end - - -- Additional checks for specific entities like offshore-pump - if entity == "offshore-pump" then - local tile = player.surface.get_tile(x, y) - if not tile.prototype.name:match("water") then - error("Cannot place the entity at the specified position due to lack of water.") - end - end - - ---- Check if the entity can be placed - --- if placement fails, it tries 11 different positions (i=0 to 10) by moving the player diagonally - local can_build = global.utils.avoid_entity(player_index, entity, position, direction) - if not can_build then - error("Cannot place the entity at the specified position: x="..position.x..", y="..position.y) - end - return true -end - -function get_entity_direction(entity, direction) - if direction == nil then - return defines.direction.north - end - local prototype = game.entity_prototypes[entity] - local cardinals = {defines.direction.north, defines.direction.east, defines.direction.south, defines.direction.west} - if prototype and prototype.type == "inserter" then - return cardinals[(direction % 4) + 1] - else - return cardinals[direction % 4] - end -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/connect_entities.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/connect_entities.lua deleted file mode 100644 index cac4a5468..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/connect_entities.lua +++ /dev/null @@ -1,1307 +0,0 @@ --- connect_entities - -local MAX_SERIALIZATION_ITERATIONS = 1000 -- Maximum iterations for serializing belt groups -local MAX_PLACEMENT_ATTEMPTS = 50 -- Maximum attempts to find placeable positions -local MAX_PATH_LENGTH = 1000 -- Maximum number of path points to process -local MAX_STRAIGHT_SECTIONS = 100 -local MAX_UNDERGROUND_SEGMENTS = 50 - --- Wire reach values for different electric pole types -local wire_reach = { - ['small-electric-pole'] = 4, - ['medium-electric-pole'] = 9, - ['big-electric-pole'] = 30, - ['substation'] = 18 -} - -local default_connect_types = { - ['pipe-to-ground'] = 'pipe', - ['underground-belt'] = 'transport-belt', - ['express-underground-belt'] = 'express-transport-belt', - ['fast-underground-belt'] = 'fast-underground-belt', -} - -local underground_ranges = { - ['pipe-to-ground'] = 8, - ['underground-belt'] = 4, - ['fast-underground-belt'] = 4, - ['express-underground-belt'] = 4 -} - -local function is_within_pole_range(position, pole) - local dx = position.x - pole.position.x - local dy = position.y - pole.position.y - local wire_reach = wire_reach[pole.name] or 4 - return (dx * dx + dy * dy) <= wire_reach * wire_reach -end - -local function get_electric_network_at_position(position) - -- Get all electric poles near the position - local nearby_poles = game.surfaces[1].find_entities_filtered{ - position = position, - radius = 9, -- Maximum pole reach is 9 for medium poles - type = "electric-pole" - } - - -- Check if any pole's wire reaches this position - for _, pole in pairs(nearby_poles) do - if is_within_pole_range(position, pole) then - return pole.electric_network_id - end - end - return nil -end - -local function are_positions_in_same_network(pos1, pos2) - local network1 = get_electric_network_at_position(pos1) - local network2 = get_electric_network_at_position(pos2) - return network1 and network2 and network1 == network2 -end - -local function is_position_saturated(position, reach) - -- Get nearby poles - local nearby_poles = game.surfaces[1].find_entities_filtered{ - position = position, - radius = 9, - type = "electric-pole" - } - - -- Check each corner of a 1x1 tile centered on the position - local corners = { - {x = position.x - reach/2, y = position.y - reach/2}, - {x = position.x - reach/2, y = position.y + reach/2}, - {x = position.x + reach/2, y = position.y + reach/2}, - {x = position.x + reach/2, y = position.y - reach/2} - } - - -- For each corner, check if it's within range of any existing pole - for _, corner in pairs(corners) do - local corner_covered = false - for _, pole in pairs(nearby_poles) do - if is_within_pole_range(corner, pole) then - corner_covered = true - break - end - end - if not corner_covered then - return false -- Found an uncovered corner - end - end - - return true -- All corners are covered -end - - -function get_step_size(connection_type) - return wire_reach[connection_type] or 1 -end - -function math.round(x) - return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) -end - -local function has_valid_fluidbox(entity) - return entity.fluidbox and #entity.fluidbox > 0 and entity.fluidbox[1] and entity.fluidbox[1].get_fluid_system_id -end - -local function are_fluidboxes_connected(entity1, entity2) - if has_valid_fluidbox(entity1) and has_valid_fluidbox(entity2) then - return entity1.fluidbox[1].get_fluid_system_id() == entity2.fluidbox[1].get_fluid_system_id() - end - return false -end - --- Calculate maximum possible underground sections based on inventory -local function calculate_max_underground_sections(player, underground_type) - local available_count = 0 - for _, inv in pairs({defines.inventory.character_main}) do - if player.get_inventory(inv) then - available_count = available_count + player.get_inventory(inv).get_item_count(underground_type) - end - end - -- Each underground section requires 2 entities (entrance and exit) - return math.floor(available_count / 2) -end - --- Helper function to check if an entity type is an underground variant -local function is_underground_type(entity_type) - return underground_ranges[entity_type] ~= nil -end - --- Modified split_section_into_underground_segments to respect inventory limits -local function split_section_into_underground_segments(section, path, range, max_segments) - local segments = {} - - -- Add margin of 1 at start and end of section - local effective_start = section.start_index + 1 - local effective_end = section.end_index - 1 - - -- Check if section is still long enough after adding margins - if effective_end - effective_start < 2 then - return segments -- Return empty segments if section is too short - end - - local current_start = effective_start - local segment_count = 0 - local iteration_count = 0 - local MAX_ITERATIONS = math.min(section.length * 2, MAX_UNDERGROUND_SEGMENTS) -- Prevent excessive iterations - - - while current_start + 1 < effective_end and segment_count < max_segments do - iteration_count = iteration_count + 1 - if iteration_count > MAX_ITERATIONS then - -- game.print("Warning: Maximum iterations reached while splitting underground segments") - break - end - -- Calculate end index for this segment - local end_index = math.min(current_start + range, effective_end) - - -- Only create segment if there's at least 1 tile gap between entrance and exit - if end_index > current_start then - table.insert(segments, { - entrance_index = current_start, - exit_index = end_index, - direction = section.direction - }) - - segment_count = segment_count + 1 - - -- Move to position after the exit for next iteration - current_start = end_index + 1 - else - -- If we can't create a valid segment, move forward - current_start = current_start + 1 - end - end - - - return segments -end - --- Find straight sections in path suitable for underground entities -local function find_straight_sections(path, min_length) - local sections = {} - local current_section = { - start_index = 1, - direction = nil, - length = 0 - } - - for i = 1, #path - 1 do --Always start slightly into the path, and away from the end to prevent connection issues. - local current_pos = path[i].position - local next_pos = path[i + 1].position - local dx = next_pos.x - current_pos.x - local dy = next_pos.y - current_pos.y - local current_direction = {dx = dx, dy = dy} - - -- Normalize direction - if math.abs(dx) > math.abs(dy) then - current_direction = {dx = dx/math.abs(dx), dy = 0} - else - current_direction = {dx = 0, dy = dy/math.abs(dy)} - end - - if not current_section.direction then - current_section.direction = current_direction - end - - -- Check if we're still going in the same direction - if current_section.direction.dx == current_direction.dx and - current_section.direction.dy == current_direction.dy then - current_section.length = current_section.length + 1 - else - -- Direction changed, check if previous section was long enough - if current_section.length >= min_length then - table.insert(sections, { - start_index = current_section.start_index, - end_index = i, - direction = current_section.direction, - length = current_section.length - }) - end - -- Start new section - current_section = { - start_index = i, - direction = current_direction, - length = 1 - } - end - end - - -- Check final section - if current_section.length >= min_length then - table.insert(sections, { - start_index = current_section.start_index, - end_index = #path, - direction = current_section.direction, - length = current_section.length - }) - end - - return sections -end - --- Helper function to serialize a belt group -local function serialize_belt_group(entity) - if not entity or not entity.valid or entity.type ~= "transport-belt" then - return nil - end - - local serialized = {} - local seen = {} - local iteration_count = 0 - - local function get_connected_belt_entities(belt, is_output) - local connected_entities = {} - local seen_owners = {} - - -- Get connected lines - local connected_lines = {} - if is_output then - if #belt.belt_neighbours['outputs'] then - for _, line in pairs(belt.belt_neighbours['outputs']) do - table.insert(connected_lines, line) - end - end - else - if #belt.belt_neighbours['inputs'] then - for _, line in pairs(belt.belt_neighbours['inputs']) do - table.insert(connected_lines, line) - end - end - end - - -- game.print("connected lines "..#connected_lines) - -- Convert lines to unique belt entities - for _, line in pairs(connected_lines) do - if line and line.valid and not seen_owners[line.unit_number] then - seen_owners[line.unit_number] = true - table.insert(connected_entities, line) - end - end - -- game.print("connected entities "..#connected_entities) - return connected_entities - end - - local function serialize_connected_belts(belt, is_output) - iteration_count = iteration_count + 1 - if iteration_count > MAX_SERIALIZATION_ITERATIONS then - -- game.print("Warning: Belt serialization reached iteration limit") - return - end - - if not belt or not belt.valid or seen[belt.unit_number] then - return - end - - seen[belt.unit_number] = true - local belt_data = global.utils.serialize_entity(belt) - table.insert(serialized, belt_data) - - -- Get connected belt entities - local next_belts = get_connected_belt_entities(belt, is_output) - for _, connected_belt in pairs(next_belts) do - if connected_belt.valid and not seen[connected_belt.unit_number] then - serialize_connected_belts(connected_belt, is_output) - end - end - end - - -- Start serialization from the given entity - serialize_connected_belts(entity, false) -- Follow input direction - serialize_connected_belts(entity, true) -- Follow output direction - - return serialized -end - - -local function are_poles_connected(entity1, entity2) - if not (entity1 and entity2) then return false end - if not (entity1.electric_network_id and entity2.electric_network_id) then return false end - return entity1.electric_network_id == entity2.electric_network_id -end - -local function is_placeable(position) - local invalid_tiles = { - ["water"] = true, - ["deepwater"] = true, - ["water-green"] = true, - ["deepwater-green"] = true, - ["water-shallow"] = true, - ["water-mud"] = true, - } - - local entities = game.surfaces[1].find_entities_filtered{ - position = position, - collision_mask = "player-layer" - } - if #entities == 1 then - if entities[1].name == "character" then - return not invalid_tiles[game.surfaces[1].get_tile(position.x, position.y).name] - end - end - return #entities == 0 and not invalid_tiles[game.surfaces[1].get_tile(position.x, position.y).name] -end - -local function find_placeable_neighbor(pos, previous_pos) - local directions = { - {dx = 0, dy = -1}, -- up - {dx = 1, dy = 0}, -- right - {dx = 0, dy = 1}, -- down - {dx = -1, dy = 0} -- left - } - - if previous_pos then - local desired_dx = pos.x - previous_pos.x - local desired_dy = pos.y - previous_pos.y - table.sort(directions, function(a, b) - local a_score = math.abs(a.dx - desired_dx) + math.abs(a.dy - desired_dy) - local b_score = math.abs(b.dx - desired_dx) + math.abs(b.dy - desired_dy) - return a_score < b_score - end) - end - - for _, dir in ipairs(directions) do - local test_pos = {x = pos.x + dir.dx, y = pos.y + dir.dy} - if is_placeable(test_pos) then - return test_pos - end - end - return nil -end - - -local function interpolate_manhattan(pos1, pos2) - local interpolated = {} - local dx = pos2.x - pos1.x - local dy = pos2.y - pos1.y - local manhattan_distance = math.abs(dx) + math.abs(dy) - -- game.print("Distance3 "..manhattan_distance) - if manhattan_distance > 2 then - local steps = math.max(math.abs(dx), math.abs(dy)) - local x_step = math.floor((dx / steps)*2)/2 - local y_step = math.floor((dy / steps)*2)/2 - - for i = 1, steps - 1 do - local new_pos_x = {x = pos1.x + math.round(x_step * i), y = pos1.y} - if is_placeable(new_pos_x) then - table.insert(interpolated, {position = new_pos_x}) - else - local neighbor = find_placeable_neighbor(new_pos_x, pos1) - if neighbor then - table.insert(interpolated, {position = neighbor}) - end - end - - local new_pos_y = {x = pos1.x + math.round(x_step * i), y = pos1.y + math.round(y_step * i)} - if is_placeable(new_pos_y) then - table.insert(interpolated, {position = new_pos_y}) - else - local neighbor = find_placeable_neighbor(new_pos_y, pos1) - if neighbor then - table.insert(interpolated, {position = neighbor}) - end - end - end - --elseif math.abs(dx) == 1 and math.abs(dy) == 1 then - -- local mid_pos = {x = pos2.x, y = pos1.y} - -- if is_placeable(mid_pos) then - -- table.insert(interpolated, {position = mid_pos}) - -- else - -- mid_pos = {x = pos1.x, y = pos2.y} - -- if is_placeable(mid_pos) then - -- table.insert(interpolated, {position = mid_pos}) - -- else - -- mid_pos = find_placeable_neighbor(mid_pos, pos1) - -- if mid_pos then - -- table.insert(interpolated, {position = mid_pos}) - -- end - -- end - -- end - --end - elseif math.abs(dx) == 1 and math.abs(dy) == 1 then --and math.abs(dx) <= 1.5 and math.abs(dy) <= 1.5 then - -- Try first horizontal then vertical movement - local mid_pos = {x = pos2.x, y = pos1.y} - if is_placeable(mid_pos) then - table.insert(interpolated, {position = mid_pos}) - else - -- Try vertical then horizontal movement - mid_pos = {x = pos1.x, y = pos2.y} - if is_placeable(mid_pos) then - table.insert(interpolated, {position = mid_pos}) - else - -- If neither orthogonal position works, try to find a neighbor - local neighbor = find_placeable_neighbor(mid_pos, pos1) - if neighbor then - table.insert(interpolated, {position = neighbor}) - end - end - end - - end - - return interpolated -end - - - -local function place_at_position(player, connection_type, current_position, dir, serialized_entities, dry_run, counter_state, is_underground_exit) - - local is_electric_pole = wire_reach[connection_type] ~= nil - local placement_position = current_position - local existing_entity = nil - - for _, entity in pairs(serialized_entities) do - if entity.position.x == placement_position.x and entity.position.y == placement_position.y then - return - end - end - - if is_electric_pole then - if is_position_saturated(current_position, wire_reach[connection_type]) then - - return -- No need to place another pole - end - - placement_position = game.surfaces[1].find_non_colliding_position(connection_type, current_position, 2, 0.1) - if not placement_position then - error("Cannot find suitable position to place " .. connection_type) - end - else - local entities = game.surfaces[1].find_entities_filtered{ - position = current_position, - type = {"beam", "resource", "player"}, - invert=true - - } - for _, entity in pairs(entities) do - if entity.name == connection_type then - existing_entity = entity - break - end - end - end - - if existing_entity then - -- game.print("Existing entity "..existing_entity.name) - -- Get the existing network ID before any modifications - --local existing_network_id = has_valid_fluidbox(existing_entity) and existing_entity.fluidbox[1].get_fluid_system_id() - - -- Update direction if needed - if existing_entity.name ~= connection_type then - if existing_entity.direction ~= dir then - existing_entity.direction = dir - end - end - - -- For pipes, merge networks - if connection_type == 'pipe' then - for _, serialized in ipairs(serialized_entities) do - local entity = game.surfaces[1].find_entity(connection_type, serialized.position) - if entity and has_valid_fluidbox(entity) then - existing_entity.connect_neighbour(entity) - end - end - end - - -- Update or add to serialized list - for i, serialized in ipairs(serialized_entities) do - if serialized.position.x == current_position.x and serialized.position.y == current_position.y then - serialized_entities[i] = global.utils.serialize_entity(existing_entity) - return existing_entity - end - end - table.insert(serialized_entities, global.utils.serialize_entity(existing_entity)) - return existing_entity - - else - -- For underground entities, we need to set the correct type (input/output) - local entity_variant = { - name = connection_type, - position = placement_position, - direction = dir, - force = player.force, - type = is_underground_exit and "output" or "input", - move_stuck_players=true - } - -- We can just teleport away here to avoid collision as we dont adhere by distance rules in connect_entities - player.teleport({placement_position.x+2, placement_position.y+2}) - local can_place - if connection_type == 'pipe' or connection_type == 'pipe-to-ground' or connection_type == 'underground-pipe' then - -- Use permissive surface check for pipe placement to allow tight spaces - can_place = game.surfaces[1].can_place_entity({ - name = connection_type, - position = placement_position, - direction = dir, - force = player.force - }) - else - can_place = global.utils.can_place_entity(player, connection_type, placement_position, dir) - end - - --local can_place = global.utils.avoid_entity(1, connection_type, placement_position, dir) - --if not can_build then - -- error("Cannot place the entity at the specified position: x="..position.x..", y="..position.y) - --end - --local player_position = player.position - -- player.teleport({placement_position.x, placement_position.y}) - --local can_place = global.actions.can_place_entity(1, connection_type, dir, placement_position.x, placement_position.y)--game.surfaces[1].can_place_entity(entity_variant) - --player.teleport(player_position) - - --local can_place = global.utils.avoid_entity(1, connection_type, placement_position, dir) - rendering.draw_circle{only_in_alt_mode=true, width = 0.25, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = placement_position, time_to_live = 12000} - - if dry_run and can_place == false then - -- Define the area where the entity will be placed - local target_area = { - {x = placement_position.x - 1 / 2, y = placement_position.y - 1 / 2}, - {x = placement_position.x + 1 / 2, y = placement_position.y + 1 / 2} - } - - -- Check for collision with other entities - local entities = player.surface.find_entities_filtered{area = target_area, force = player.force} - for _, entity in pairs(entities) do - -- game.print("1 "..entity.name) - end - - error("Cannot connect due to placement blockage 1.") - end - - - -- Place entity - if can_place and not dry_run then - --global.utils.avoid_entity(player.index, connection_type, placement_position, dir) - - local placed_entity = game.surfaces[1].create_entity(entity_variant) - if placed_entity then - player.remove_item({name = connection_type, count = 1}) - counter_state.place_counter = counter_state.place_counter + 1 - table.insert(serialized_entities, global.utils.serialize_entity(placed_entity)) - return placed_entity - end - - elseif can_place and dry_run then - counter_state.place_counter = counter_state.place_counter + 1 - return nil - - else - -- game.print("Avoiding entity at " .. placement_position.x.. ", " .. placement_position.y) - -- global.utils.avoid_entity(player.index, connection_type, placement_position, dir) - - -- error("Cannot place entity") - --local entities = player.surface.find_entities_filtered{position=placement_position, radius=0.5, type = {"beam", "resource", "player"}, invert=true} - --local can_place = #entities == 0 - --if can_place then - -- local tile = player.surface.get_tile(placement_position.x, placement_position.y) - -- if is_water_tile(tile.name) then - -- can_place = false - -- end - --end - -- - --if can_place then - -- local placed_entity = player.surface.create_entity({ - -- name = connection_type, - -- position = placement_position, - -- direction = dir, - -- force = player.force - -- }) - -- - -- if placed_entity then - -- player.remove_item({name = connection_type, count = 1}) - -- counter_state.place_counter = counter_state.place_counter + 1 - -- table.insert(serialized_entities, global.utils.serialize_entity(placed_entity)) - -- return placed_entity - -- end - -- - --end - end - end - - -- Check inventory - local has_item = false - for _, inv in pairs({defines.inventory.character_main}) do - if player.get_inventory(inv) then - local count = player.get_inventory(inv).get_item_count(connection_type) - if count > 0 then - has_item = true - break - end - end - end - if not has_item then - error("You do not have the required item in their inventory.") - end - - -- We can just teleport away here to avoid collision as we dont adhere by distance rules in connect_entities - player.teleport({placement_position.x+2, placement_position.y+2}) - local can_place - if connection_type == 'pipe' or connection_type == 'pipe-to-ground' or connection_type == 'underground-pipe' then - -- Use permissive surface check for pipe placement to allow tight spaces - can_place = game.surfaces[1].can_place_entity({ - name = connection_type, - position = placement_position, - direction = dir, - force = player.force - }) - else - can_place = global.utils.can_place_entity(player, connection_type, placement_position, dir) - end - --local player_position = player.position - --player.teleport({placement_position.x, placement_position.y}) - --local can_place = global.actions.can_place_entity(1, connection_type, dir, placement_position.x, placement_position.y)--game.surfaces[1].can_place_entity(entity_variant) - --player.teleport(player_position) - - --local can_place = global.utils.avoid_entity(1, connection_type, placement_position, dir) - rendering.draw_circle{only_in_alt_mode=true, width = 0.25, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = placement_position, time_to_live = 12000} - - if dry_run and can_place == false then - local target_area = { - {x = placement_position.x - 1 / 2, y = placement_position.y - 1 / 2}, - {x = placement_position.x + 1 / 2, y = placement_position.y + 1 / 2} - } - - -- Check for collision with other entities - local entities = player.surface.find_entities_filtered{area = target_area, force = player.force} - for _, entity in pairs(entities) do - -- game.print("1: "..entity.name) - end - - error("Cannot connect due to placement blockage.") - end - - - -- Place entity - if can_place and not dry_run then - --global.utils.avoid_entity(player.index, connection_type, placement_position, dir) - - local placed_entity = game.surfaces[1].create_entity({ - name = connection_type, - position = placement_position, - direction = dir, - force = player.force, - move_stuck_players=true - }) - - if placed_entity then - player.remove_item({name = connection_type, count = 1}) - counter_state.place_counter = counter_state.place_counter + 1 - table.insert(serialized_entities, global.utils.serialize_entity(placed_entity)) - return placed_entity - end - - elseif can_place and dry_run then - counter_state.place_counter = counter_state.place_counter + 1 - return nil - - else - -- game.print("Avoiding entity at " .. placement_position.x.. ", " .. placement_position.y) - -- global.utils.avoid_entity(player.index, connection_type, placement_position, dir) - - -- error("Cannot place entity") - --local entities = player.surface.find_entities_filtered{position=placement_position, radius=0.5, type = {"beam", "resource", "player"}, invert=true} - --local can_place = #entities == 0 - --if can_place then - -- local tile = player.surface.get_tile(placement_position.x, placement_position.y) - -- if is_water_tile(tile.name) then - -- can_place = false - -- end - --end - -- - --if can_place then - -- local placed_entity = player.surface.create_entity({ - -- name = connection_type, - -- position = placement_position, - -- direction = dir, - -- force = player.force - -- }) - -- - -- if placed_entity then - -- player.remove_item({name = connection_type, count = 1}) - -- counter_state.place_counter = counter_state.place_counter + 1 - -- table.insert(serialized_entities, global.utils.serialize_entity(placed_entity)) - -- return placed_entity - -- end - -- - --end - end -end - -local function table_contains(tbl, element) - for _, value in ipairs(tbl) do - if value == element then - return true - end - end - return false -end - -local function connect_entities(player_index, source_x, source_y, target_x, target_y, path_handle, connection_types, dry_run) - local counter_state = {place_counter = 0} - local player = global.agent_characters[player_index] - local last_placed_entity = nil - - local start_position = {x = math.floor(source_x*2)/2, y = math.floor(source_y*2)/2} - local end_position = {x = target_x, y = target_y} - - - local raw_path = global.paths[path_handle] - -- game.print("Path length "..#raw_path) - -- game.print(serpent.line(start_position).." - "..serpent.line(end_position)) - - if not raw_path or type(raw_path) ~= "table" or #raw_path == 0 then - error("Invalid path: " .. serpent.line(path)) - end - - -- game.print("Normalising", {print_skip=defines.print_skip.never}) - local path = global.utils.normalise_path(raw_path, start_position, end_position) - - -- Get default and underground connection types - local default_connection_type = default_connect_types[connection_types[1]] or connection_types[1] - local underground_type = nil - for _, type in ipairs(connection_types) do - if is_underground_type(type) then - underground_type = type - break - end - end - - --rendering.clear() - - rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 1, g = 0, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = start_position, time_to_live = 60000} - rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = end_position, time_to_live = 60000} - - for i = 1, #path - 1 do - rendering.draw_line{only_in_alt_mode=true, surface = player.surface, from = path[i].position, to = path[i + 1].position, color = {1, 0, 1}, width = 2, dash_length=0.25, gap_length = 0.25} - end - for i = 1, #raw_path - 1 do - rendering.draw_line{only_in_alt_mode=true, surface = player.surface, from = raw_path[i].position, to = raw_path[i + 1].position, color = {1, 1, 0}, width = 0, dash_length=0.2, gap_length = 0.2} - end - - - local last_position = start_position - local step_size = wire_reach[default_connection_type] or 1 - local is_electric_pole = wire_reach[default_connection_type] ~= nil - - - for i = 1, #path-1, step_size do - global.elapsed_ticks = global.elapsed_ticks + global.utils.calculate_movement_ticks(player, last_position, path[i].position) - last_position = path[i].position - end - - local serialized_entities = {} - - -- Get source and target entities - local source_entity = global.utils.get_closest_entity(player, {x = source_x, y = source_y}) - local target_entity = global.utils.get_closest_entity(player, {x = target_x, y = target_y}) - - - if #connection_types == 1 and connection_types[1] == 'pipe-to-ground' then - - -- Calculate the direction from start to end. - local dir = global.utils.get_direction(start_position, end_position) - local entrance_dir = global.utils.get_entity_direction(underground_type, dir / 2) - - -- Place the underground entrance at the start position. - place_at_position(player, underground_type, start_position, entrance_dir, - serialized_entities, dry_run, counter_state, false) - - local exit_dir - if underground_type == "pipe-to-ground" then - -- For pipe-to-ground, rotate the direction 180° for the exit. - exit_dir = global.utils.get_entity_direction(underground_type, (dir / 2 + 2) % 4) - else - exit_dir = global.utils.get_entity_direction(underground_type, dir / 2) - end - - -- Place the underground exit at the end position. - place_at_position(player, underground_type, end_position, exit_dir, - serialized_entities, dry_run, counter_state, true) - - return { - entities = serialized_entities, - connected = true, - number_of_entities = counter_state.place_counter - } - end - - - if underground_type then - -- Calculate maximum possible underground sections based on inventory - local max_underground_sections = calculate_max_underground_sections(player, underground_type) - - -- Find straight sections suitable for underground entities - local range = underground_ranges[underground_type] - local straight_sections = find_straight_sections(path, 4) - - -- Track remaining underground sections we can create - local remaining_sections = max_underground_sections - - - - - local current_index = 1 - for _, section in ipairs(straight_sections) do - - if remaining_sections <= 0 then - break - end - - local MAX_INDEX_PLACEMENT = 200 - -- Place regular entities up to the section - while current_index < section.start_index do - local current_pos = path[current_index].position - local next_pos = path[current_index + 1].position - local dir = global.utils.get_direction(current_pos, next_pos) - place_at_position(player, default_connection_type, current_pos, - global.utils.get_entity_direction(default_connection_type, dir/2), - serialized_entities, dry_run, counter_state, false) - current_index = current_index + 1 - if current_index > MAX_INDEX_PLACEMENT then - break - end - end - - -- Place initial surface entity for direction change - local margin_pos = path[section.start_index].position - local margin_next_pos = path[section.start_index + 1].position - local margin_dir = global.utils.get_direction(margin_pos, margin_next_pos) - place_at_position(player, default_connection_type, margin_pos, - global.utils.get_entity_direction(default_connection_type, margin_dir/2), - serialized_entities, dry_run, counter_state, false) - - -- Split the section into multiple underground segments, limited by remaining_sections - local segments = split_section_into_underground_segments(section, path, range, remaining_sections) - - -- Update remaining_sections count - remaining_sections = remaining_sections - #segments - - for i, segment in ipairs(segments) do - -- Place underground entrance - local entrance_pos = path[segment.entrance_index].position - local exit_pos = path[segment.exit_index].position - local dir = global.utils.get_direction(entrance_pos, exit_pos) - - local entity_dir = global.utils.get_entity_direction(underground_type, dir/2) - - place_at_position(player, underground_type, entrance_pos, - entity_dir, - serialized_entities, dry_run, counter_state, false) - - -- Adjust direction for pipe-to-ground exit and place exit pipe - if underground_type == 'pipe-to-ground' then - entity_dir = global.utils.get_entity_direction(underground_type, (dir/2 + 2)%4) - end - - place_at_position(player, underground_type, exit_pos, - entity_dir, - serialized_entities, dry_run, counter_state, true) - - -- Update current_index to after the exit - current_index = segment.exit_index + 1 - end - -- Place final surface entity for direction change if we had segments - if #segments > 0 then - local final_margin_pos = path[section.end_index].position - local final_prev_pos = path[section.end_index - 1].position - local final_dir = global.utils.get_direction(final_prev_pos, final_margin_pos) - place_at_position(player, default_connection_type, final_margin_pos, - global.utils.get_entity_direction(default_connection_type, final_dir/2), - serialized_entities, dry_run, counter_state, false) - end - - -- If we've used all available underground sections, use regular entities for the rest - if remaining_sections <= 0 then - break - end - - end - - -- Place remaining regular entities to finish the connection - while current_index < #path do - local current_pos = path[current_index].position - local next_pos = path[current_index + 1].position - local dir = global.utils.get_direction(current_pos, next_pos) - place_at_position(player, default_connection_type, current_pos, - global.utils.get_entity_direction(default_connection_type, dir/2), - serialized_entities, dry_run, counter_state, false) - current_index = current_index + 1 - end - - -- After underground segment placement - if current_index == #path and not path[#path].has_entity then - local final_pos = path[#path].position - local prev_pos = path[#path-1].position - local dir = global.utils.get_direction(prev_pos, final_pos) - place_at_position(player, default_connection_type, final_pos, - global.utils.get_entity_direction(default_connection_type, dir/2), - serialized_entities, dry_run, counter_state, false) - end - else - -- Handle source belt orientation if it exists - if not is_electric_pole and not table_contains(connection_types, 'pipe') then - local source_pos = {x = source_x, y = source_y} - local entities = game.surfaces[1].find_entities_filtered{ - position = source_pos, - name = connection_types, - force = "player" - } - - if #entities > 0 and #path > 1 then - -- Calculate initial direction based on first two points in path - local initial_dir = global.utils.get_direction(path[1].position, path[2].position) - --local entity_dir = global.utils.get_entity_direction('pipe', initial_dir/2) - - -- Update source belt direction if needed - local source_belt = entities[1] - if source_belt and source_belt.valid and source_belt.direction ~= initial_dir then - source_belt.direction = initial_dir - table.insert(serialized_entities, global.utils.serialize_entity(source_belt)) - end - end - end - - if is_electric_pole then - -- Place poles until we achieve connectivity - local last_pole = source_entity - - for i = 1, #path, step_size do - local current_pos = path[i].position - local dir = global.utils.get_direction(current_pos, path[math.min(i + step_size, #path)].position) - local entity_dir = global.utils.get_entity_direction(default_connection_type, dir/2) - - -- Place the pole - local placed_entity = place_at_position(player, default_connection_type, current_pos, entity_dir, serialized_entities, dry_run, counter_state) - - if not dry_run then - -- Get the newly placed pole - local current_pole = placed_entity or global.utils.get_closest_entity(player, current_pos) - - -- Check if we've achieved connectivity to the target - if are_poles_connected(current_pole, target_entity) then - break -- Stop placing poles once we have connectivity - end - - last_pole = current_pole - end - end - - -- If we haven't achieved connectivity yet, place one final pole at the target - if not dry_run and last_pole and target_entity and not are_poles_connected(last_pole, target_entity) then - local final_dir = global.utils.get_direction(path[#path].position, end_position) - -- game.print("Placing final pole at "..serpent.line(end_position)) - place_at_position(player, default_connection_type, end_position, - global.utils.get_entity_direction(default_connection_type, final_dir/2), - serialized_entities, dry_run, counter_state) - end - else - if table_contains(connection_types, 'pipe') then - place_at_position(player, 'pipe', start_position, 0, serialized_entities, dry_run, counter_state) - end - - for i = 1, #path-1, step_size do - local dir = global.utils.get_direction(path[i].position, path[math.min(i + step_size, #path)].position) - local placed = place_at_position(player, default_connection_type, path[i].position, - global.utils.get_entity_direction(default_connection_type, dir/2), - serialized_entities, dry_run, counter_state) - if placed then - last_placed_entity = placed - end - end - - -- Handle final placement - if table_contains(connection_types, 'pipe') then - local preemptive_target = { - x = (target_x + path[#path].position.x)/2, - y = (target_y + path[#path].position.y)/2 - } - - -- Place intermediate and final pipes - place_at_position(player, 'pipe', path[#path].position, - global.utils.get_direction(path[#path].position, preemptive_target), - serialized_entities, dry_run, counter_state) - - place_at_position(player, 'pipe', preemptive_target, - global.utils.get_direction(path[#path].position, preemptive_target), - serialized_entities, dry_run, counter_state) - - place_at_position(player, 'pipe', end_position, - global.utils.get_direction(preemptive_target, end_position), - serialized_entities, dry_run, counter_state) - else - local last_path_index = #path - local second_to_last_index = math.max(1, last_path_index - 1) - local final_dir = global.utils.get_direction( - path[second_to_last_index].position, - path[last_path_index].position - ) - - local final_entity = place_at_position(player, default_connection_type, end_position, - global.utils.get_entity_direction(default_connection_type, final_dir/2), - serialized_entities, dry_run, counter_state) - - if final_entity then - last_placed_entity = final_entity - end - - -- After all belts are placed, serialize the entire belt group if we're not in dry run - if not dry_run and last_placed_entity and last_placed_entity.valid and default_connection_type:find("belt") then - -- Clear the existing serialized entities (which only contain individual placements) - - serialized_entities = {} - -- Serialize the entire connected belt group - local group_data = serialize_belt_group(last_placed_entity) - if group_data then - for _, serialized in ipairs(group_data) do - table.insert(serialized_entities, serialized) - end - end - end - end - end - end - - -- Check final connectivity - local is_connected = false - if source_entity and target_entity then - if is_electric_pole then - is_connected = are_poles_connected(source_entity, target_entity) - else - is_connected = are_fluidboxes_connected(source_entity, target_entity) - end - end - - -- game.print("Connection status: " .. tostring(is_connected)) - - - return { - entities = serialized_entities, - connected = is_connected, - number_of_entities = counter_state.place_counter - } -end - -global.utils.normalise_path = function(original_path, start_position, end_position) - local path = {} - local seen = {} -- To track seen positions - if original_path == nil or #original_path < 1 or original_path == "not_found" then - error("Failed to find a path.") - end - if math.ceil(start_position.x) == start_position.x then - start_position.x = start_position.x + 0.5 - --start_position.y = start_position.y + 0.5 - end - if math.ceil(start_position.y) == start_position.y then - start_position.y = start_position.y + 0.5 - end - - if math.ceil(end_position.x) == end_position.x or math.ceil(end_position.y) == end_position.y then - end_position.x = end_position.x + 0.5 - end_position.y = end_position.y + 0.5 - end - - --game.print(serpent.block(original_path)) - --for i = 1, #original_path do - -- game.print(serpent.line(original_path[i])) - --end - - if math.ceil(original_path[1].position.x) == original_path[1].position.x or math.ceil(original_path[1].position.y) == original_path[1].position.y then - original_path[1].position.x = original_path[1].position.x + 0.5 - original_path[1].position.y = original_path[1].position.y + 0.5 - end - - -- Helper function to add unique positions - local function add_unique(pos, prev_pos) - local key = pos.x .. "," .. pos.y - if not seen[key] then - if is_placeable(pos) then - table.insert(path, {position = pos}) - seen[key] = true - return pos - else - local alt_pos = find_placeable_neighbor(pos, prev_pos) - if alt_pos then - local alt_key = alt_pos.x .. "," .. alt_pos.y - if not seen[alt_key] then - table.insert(path, {position = alt_pos}) - seen[alt_key] = true - return alt_pos - end - end - end - end - return nil - end - - -- Add start position first - local previous_pos = add_unique(start_position, nil) or start_position - - -- Process each segment of the path - local current_pos = previous_pos - for i = 1, #original_path do - local target_pos = original_path[i].position - local interpolated = interpolate_manhattan(current_pos, target_pos) - - -- Add interpolated positions - for _, point in ipairs(interpolated) do - local new_pos = add_unique(point.position, current_pos) - if new_pos then - current_pos = new_pos - end - end - - -- Add the target position - local new_pos = add_unique(target_pos, current_pos) - if new_pos then - current_pos = new_pos - end - end - - -- Finally interpolate to end position if it's different from the last position - if current_pos.x ~= end_position.x or current_pos.y ~= end_position.y then - local interpolated = interpolate_manhattan(current_pos, end_position) - for _, point in ipairs(interpolated) do - local new_pos = add_unique(point.position, current_pos) - if new_pos then - current_pos = new_pos - end - end - add_unique(end_position, current_pos) - end - - return path -end - --- Helper function to check if two positions are adjacent -local function are_positions_adjacent(pos1, pos2) - local dx = math.abs(pos1.x - pos2.x) - local dy = math.abs(pos1.y - pos2.y) - return (dx <= 1 and dy <= 1) and not (dx == 0 and dy == 0) -end - --- Helper function to validate belt direction compatibility -local function are_belt_directions_compatible(dir1, dir2, pos1, pos2) - -- Convert positions to vectors - local dx = pos2.x - pos1.x - local dy = pos2.y - pos1.y - - -- Check if the belts are pointing in compatible directions - -- This is a simplified check - you may need to adjust based on your specific needs - if dir1 == dir2 then - -- Same direction - check if it matches position delta - local expected_dir = global.utils.get_direction(pos1, pos2) - return dir1 == expected_dir - else - -- Different directions - check if they form a valid turn - -- Add more sophisticated turn validation if needed - return are_positions_adjacent(pos1, pos2) - end -end - - --- Function to validate belt connectivity -local function validate_belt_connectivity(path) - if not path or #path < 2 then - return false, "Invalid path length" - end - - -- Check each segment of the path - for i = 1, #path - 1 do - local current_pos = path[i].position - local next_pos = path[i + 1].position - - -- Check if positions are adjacent or properly spaced - if not are_positions_adjacent(current_pos, next_pos) then - return false, "Could not create a belt at position " .. - current_pos.x .. "," .. current_pos.y - end - - -- Calculate directions for current and next position - local current_dir = global.utils.get_direction(current_pos, next_pos) - local next_dir = i < #path - 1 and - global.utils.get_direction(next_pos, path[i + 2].position) or - current_dir - - -- Check direction compatibility - if not are_belt_directions_compatible(current_dir, next_dir, current_pos, next_pos) then - return false, "Invalid belt turn detected at position " .. - current_pos.x .. "," .. current_pos.y - end - - -- Check if position is placeable - if not is_placeable(current_pos) then - return false, "Cannot place belt at position " .. - current_pos.x .. "," .. current_pos.y - end - end - - -- Check final position - if not is_placeable(path[#path].position) then - return false, "Cannot place belt at final position" - end - - return true, nil -end - --- Modify the connect_entities function to include validation -local function connect_entities_with_validation(player_index, source_x, source_y, target_x, target_y, path_handle, connection_types, dry_run) - local path = global.paths[path_handle] - if path == nil then - error("No path found") - end - -- Only perform validation for belt-type entities - for _,connection_type in pairs(connection_types) do - if wire_reach[connection_type] then - if are_positions_in_same_network( - {x = source_x, y = source_y}, - {x = target_x, y = target_y} - ) then - --error("Source and target positions are already connected to the same power network") - --return {} - break - end - --break - elseif connection_type:find("belt") then - -- Normalize path first - local normalized_path = global.utils.normalise_path(path, - {x = source_x, y = source_y}, - {x = target_x, y = target_y}) - - -- Validate connectivity - local is_valid, error_message = validate_belt_connectivity(normalized_path) - if not is_valid then - error(error_message) - end - break - end - end - - -- If validation passes or it's not a belt, proceed with normal connection - return connect_entities(player_index, source_x, source_y, target_x, target_y, - path_handle, connection_types, dry_run) -end - - --- Using the new shortest_path function. -global.actions.connect_entities = function(player_index, source_x, source_y, target_x, target_y, path_handle, connection_type_string, dry_run, number_of_connection_entities) - - local connection_types = {} - for item in string.gmatch(connection_type_string, "([^,]+)") do - -- game.print(item) - table.insert(connection_types, item) - end - --First do a dry run - local result = connect_entities_with_validation(player_index, source_x, source_y, target_x, target_y, path_handle, connection_types, true) - -- then do an actual run if dry run is false - if not dry_run then - -- Check if the player has enough entities in their inventory - local required_count = result.number_of_entities - -- game.print("Required count: " .. required_count) - -- game.print("Available count: " .. number_of_connection_entities) - if number_of_connection_entities < required_count then - error("\"You do not have enough " .. connection_type_string .. " in you inventory to complete this connection. Required number - " .. required_count .. ", Available in inventory - " .. number_of_connection_entities.."\"") - end - result = connect_entities_with_validation(player_index, source_x, source_y, target_x, target_y, path_handle, connection_types, false) - end - - return result -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/craft_item.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/craft_item.lua deleted file mode 100644 index 225b0b41b..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/craft_item.lua +++ /dev/null @@ -1,213 +0,0 @@ -global.actions.craft_item = function(player_index, entity, count) - local player = global.agent_characters[player_index] - - local function calculate_crafting_ticks(recipe, crafts_count) - -- energy_required is in seconds, multiply by 60 to get ticks - local ticks_per_craft = (recipe.energy or 0.5) * 60 - return math.ceil(ticks_per_craft * crafts_count) - end - - local function check_inventory_space(player, item_name, count) - -- Get the prototype of the item - local item_prototype = game.item_prototypes[item_name] - if not item_prototype then - return false, "Invalid item prototype" - end - - -- Calculate how many slots this would need - local stack_size = item_prototype.stack_size - local slots_needed = math.ceil(count / stack_size) - - -- Check if player has enough free inventory slots - local inventory = player.get_main_inventory() - if not inventory then - return false, "Cannot access player inventory" - end - - -- Count free slots and partially filled slots of the same item - local available_slots = 0 - for i = 1, #inventory do - local stack = inventory[i] - if not stack.valid_for_read then - -- Empty slot - available_slots = available_slots + 1 - elseif stack.name == item_name and stack.count < stack_size then - -- Partially filled slot of the same item - available_slots = available_slots + 1 - end - end - - if available_slots < slots_needed then - return false, "Inventory is full - requires " .. slots_needed .. " slots but only " .. available_slots .. " available" - end - - return true - end - - - local function get_missing_ingredients(player, recipe, count) - local missing_ingredients = {} - local crafts_needed = math.ceil(count / recipe.products[1].amount) - for _, ingredient in pairs(recipe.ingredients) do - local count_that_player_has = player.get_item_count(ingredient.name) - local needed = ingredient.amount * crafts_needed - if count_that_player_has < needed then - local difference = needed - count_that_player_has - missing_ingredients[ingredient.name] = difference - end - end - return missing_ingredients - end - - local function get_required_technology(recipe_name, force) - for _, tech in pairs(force.technologies) do - if tech.effects then - for _, effect in pairs(tech.effects) do - if effect.type == "unlock-recipe" and effect.recipe == recipe_name then - return tech.name - end - end - end - end - return nil - end - - local function can_craft_recipe(player, recipe_name) - local recipe = player.force.recipes[recipe_name] - if not recipe then - return false, "recipe for " .. recipe_name .. " doesn't exist, it is a raw resource that must be gathered first" - end - if not recipe.enabled then - local required_tech = get_required_technology(recipe_name, player.force) - local tech_message = required_tech and string.format(" (requires %s technology)", required_tech) or "" - return false, "recipe for " .. recipe_name .. " is not unlocked yet" .. tech_message .. ". You need to research the technology first" - end - if recipe.category ~= "crafting" then - return false, "Item " .. recipe_name .. " cannot be crafted. Recipe requires a crafting machine or smelting in a furnace" - end - return true, recipe - end - - local function update_production_stats(force, recipe, crafts_count) - local stats = force.item_production_statistics - local craft_stats = {crafted_count = crafts_count, inputs = {}, outputs = {}} - for _, ingredient in pairs(recipe.ingredients) do - craft_stats.inputs[ingredient.name] = ingredient.amount * crafts_count - stats.on_flow(ingredient.name, -ingredient.amount * crafts_count) - end - for _, product in pairs(recipe.products) do - if product.type == "item" then - stats.on_flow(product.name, product.amount * crafts_count) - craft_stats.outputs[product.name] = product.amount * crafts_count - end - end - table.insert(global.crafted_items, craft_stats) - end - - -- Single recursive crafting function that handles both fast and slow modes - local function attempt_craft(player, entity_name, count, attempted_recipes) - attempted_recipes = attempted_recipes or {} - - -- Prevent infinite recursion - if attempted_recipes[entity_name] then - return 0, "recursive crafting loop detected" - end - attempted_recipes[entity_name] = true - - local can_craft, recipe_or_error = can_craft_recipe(player, entity_name) - if not can_craft then - return 0, recipe_or_error - end - - local recipe = recipe_or_error - local crafts_needed = math.ceil(count / recipe.products[1].amount) - local actual_craft_count = crafts_needed * recipe.products[1].amount - - -- Check for missing ingredients - local missing_ingredients = get_missing_ingredients(player, recipe, actual_craft_count) - if next(missing_ingredients) then - -- Try to craft each missing ingredient - for ingredient_name, needed_amount in pairs(missing_ingredients) do - local crafted_amount, error_msg = attempt_craft(player, ingredient_name, needed_amount, attempted_recipes) - if crafted_amount == 0 then - return 0, "couldn't craft a required sub-ingredient for ".. entity_name .. " - " .. ingredient_name .. " - " .. error_msg .. ". Required " ..ingredient_name.. " amount for " .. entity_name .. " - " .. needed_amount .. ")" - end - end - end - - -- Calculate total ticks needed for this craft - local crafting_ticks = calculate_crafting_ticks(recipe, crafts_needed) - - - -- After potentially crafting intermediates, check if we can now craft the original item - if global.fast then - -- Only add ticks in fast mode since in slow mode they are added naturally - global.elapsed_ticks = global.elapsed_ticks + crafting_ticks - - -- Add inventory space check here - local can_insert, error_msg = check_inventory_space(player, entity_name, actual_craft_count) - if not can_insert then - return 0, error_msg - end - - -- Fast crafting implementation - local missing = get_missing_ingredients(player, recipe, actual_craft_count) - if next(missing) then - local missing_str = "" - for name, amount in pairs(missing) do - missing_str = missing_str .. name .. " x" .. amount .. ", " - end - return 0, "still missing ingredients - " .. missing_str:sub(1, -3) - end - - for _, ingredient in pairs(recipe.ingredients) do - player.remove_item({name = ingredient.name, count = ingredient.amount * crafts_needed}) - end - - local crafted = player.insert({name = entity_name, count = actual_craft_count}) - if crafted < actual_craft_count then - player.surface.spill_item_stack(player.position, {name = entity_name, count = actual_craft_count - crafted}) - end - - update_production_stats(player.force, recipe, crafted) - return crafted, nil - else - -- Slow crafting implementation - local crafted = player.begin_crafting{count=count, recipe=entity_name} - if crafted == 0 then - return 0, "unable to begin crafting - check prerequisites and inventory space" - end - update_production_stats(player.force, recipe, crafted) - return crafted, nil - end - end - - -- Main crafting logic - local total_crafted = 0 - local final_error = nil - - while total_crafted < count do - local remaining = count - total_crafted - local crafted_amount, error_msg = attempt_craft(player, entity, remaining, {}) - - if crafted_amount > 0 then - total_crafted = total_crafted + crafted_amount - if not global.fast then - break - end - else - final_error = error_msg - break - end - end - - if total_crafted >= count or (not global.fast and total_crafted > 0) then - return count - elseif total_crafted > 0 then - error(string.format("\"Successfully crafted %dx but failed to craft %dx %s because %s\"", - total_crafted, count - total_crafted, entity, final_error)) - else - error(string.format("\"Failed to craft %dx %s because %s\"", - count, entity, final_error)) - end -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/extract_item.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/extract_item.lua deleted file mode 100644 index 1d00a2fda..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/extract_item.lua +++ /dev/null @@ -1,195 +0,0 @@ --- Helper function to check all possible inventories of an entity -local function get_entity_item_count(entity, item_name) - local inventory_types = { - defines.inventory.chest, - defines.inventory.furnace_source, - defines.inventory.furnace_result, - defines.inventory.assembling_machine_input, - defines.inventory.assembling_machine_output, - defines.inventory.fuel, - defines.inventory.burnt_result, - defines.inventory.reactor_source, - defines.inventory.reactor_result, - defines.inventory.lab_input, - defines.inventory.lab_source, - defines.inventory.mining_drill_input, - defines.inventory.item_main, -- For cargo wagons - defines.inventory.robot_cargo, - defines.inventory.robot_repair, - defines.inventory.car_trunk, - defines.inventory.car_fuel, - defines.inventory.roboport_material, - defines.inventory.roboport_robot, - defines.inventory.storage_tank, - defines.inventory.artillery_turret_ammo, - defines.inventory.turret_ammo, - defines.inventory.beacon_modules, - defines.inventory.character_main, - defines.inventory.character_guns, - defines.inventory.character_ammo, - defines.inventory.character_armor, - defines.inventory.character_vehicle, - defines.inventory.character_trash - } - - local total_count = 0 - for _, inv_type in ipairs(inventory_types) do - local inventory = entity.get_inventory(inv_type) - if inventory then - total_count = total_count + entity.get_item_count(item_name) - end - end - return total_count -end - --- Helper function to remove items from any valid inventory -local function remove_items_from_entity(entity, stack) - local inventory_types = { - defines.inventory.chest, - defines.inventory.furnace_source, - defines.inventory.furnace_result, - defines.inventory.assembling_machine_input, - defines.inventory.assembling_machine_output, - defines.inventory.fuel, - defines.inventory.burnt_result, - defines.inventory.reactor_source, - defines.inventory.reactor_result, - defines.inventory.lab_input, - defines.inventory.lab_source, - defines.inventory.mining_drill_input, - defines.inventory.item_main, - defines.inventory.robot_cargo, - defines.inventory.robot_repair, - defines.inventory.car_trunk, - defines.inventory.car_fuel, - defines.inventory.roboport_material, - defines.inventory.roboport_robot, - defines.inventory.storage_tank, - defines.inventory.artillery_turret_ammo, - defines.inventory.turret_ammo, - defines.inventory.beacon_modules, - defines.inventory.character_main, - defines.inventory.character_guns, - defines.inventory.character_ammo, - defines.inventory.character_armor, - defines.inventory.character_vehicle, - defines.inventory.character_trash - } - - local items_remaining = stack.count - local total_removed = 0 - - for _, inv_type in ipairs(inventory_types) do - if items_remaining <= 0 then - break - end - - local inventory = entity.get_inventory(inv_type) - if inventory then - local current_stack = {name = stack.name, count = items_remaining} - local removed = entity.remove_item(current_stack) - total_removed = total_removed + removed - items_remaining = items_remaining - removed - end - end - - return total_removed -end - -global.actions.extract_item = function(player_index, extract_item, count, x, y, source_name) - local player = global.agent_characters[player_index] - local position = {x=x, y=y} - local surface = player.surface - - -- First validate the request - if count <= 0 then - error("\"Invalid count: must be greater than 0\"") - end - - -- Find all entities in range - local search_radius = 10 - local area = {{position.x - search_radius, position.y - search_radius}, - {position.x + search_radius, position.y + search_radius}} - local buildings = nil - - if source_name ~= nil then - buildings = surface.find_entities_filtered{ - area = area, - name = source_name - } - else - buildings = surface.find_entities_filtered{ - area = area - } - end - - -- Find the closest building with the item we want - local closest_distance = math.huge - local closest_entity = nil - local found_any_items = false - - for _, building in ipairs(buildings) do - if building.name ~= 'character' then - local item_count = get_entity_item_count(building, extract_item) - if item_count > 0 then - found_any_items = true - local distance = ((position.x - building.position.x) ^ 2 + - (position.y - building.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest_entity = building - end - end - end - end - - -- Error handling in priority order - - if #buildings == 0 then - error("\"Could not find any entities in range\"") - end - - if not closest_entity then - if source_name then - error("\"Could not find a valid "..source_name.." entity containing " .. extract_item.."\"") - else - error("\"Could not find a valid entity containing " .. extract_item .. "\"") - end - end - - if closest_distance > search_radius then - error("\"Entity at ("..closest_entity.position.x..", "..closest_entity.position.y..") is too far away from your position of ("..player.position.x..","..player.position.y.."), move closer.\"") - end - - if not found_any_items then - error("\"No " .. extract_item .. " found in any nearby entities\"") - end - - -- Calculate how many items we can actually extract - local available_count = get_entity_item_count(closest_entity, extract_item) - local extract_count = math.min(count, available_count) - - -- Create the stack for extraction - local stack = {name=extract_item, count=extract_count} - - -- Attempt the extraction - local number_extracted = remove_items_from_entity(closest_entity, stack) - - if number_extracted > 0 then - -- Insert items into player inventory - local inserted = player.insert(stack) - - -- If we couldn't insert all items, put them back in the container - if inserted < number_extracted then - stack.count = number_extracted - inserted - closest_entity.insert(stack) - number_extracted = inserted - end - - -- game.print("Extracted " .. number_extracted .. " " .. extract_item) - return number_extracted - else - -- This should rarely happen given our prior checks - error("\"Failed to extract " .. extract_item .. "\"") - end -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_connection_amount.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_connection_amount.lua deleted file mode 100644 index e69de29bb..000000000 diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entities.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entities.lua deleted file mode 100644 index 0b1440d8e..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entities.lua +++ /dev/null @@ -1,37 +0,0 @@ -global.actions.get_entities = function(player_index, radius, entity_names_json, position_x, position_y) - local player = global.agent_characters[player_index] - local position - if position_x and position_y then - position = {x = tonumber(position_x), y = tonumber(position_y)} - else - position = player.position - end - - radius = tonumber(radius) or 5 - local entity_names = game.json_to_table(entity_names_json) or {} - local area = { - {position.x - radius, position.y - radius}, - {position.x + radius, position.y + radius} - } - - local filter = {} - if entity_names and #entity_names > 0 then - filter = {name = entity_names} - end - - local entities - if #entity_names > 0 then - entities = player.surface.find_entities_filtered{area = area, force = player.force, filter=filter} - else - entities = player.surface.find_entities_filtered{area = area, force = player.force} - end - - local result = {} - for _, entity in ipairs(entities) do - if entity.name ~= 'character' then - local serialized = global.utils.serialize_entity(entity) - table.insert(result, serialized) - end - end - return dump(result) -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entity.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entity.lua deleted file mode 100644 index deb63415e..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_entity.lua +++ /dev/null @@ -1,48 +0,0 @@ -global.actions.get_entity = function(player_index, entity, x, y) - local player = global.agent_characters[player_index] - local position = {x=x, y=y} - - if game.entity_prototypes[entity] == nil then - local name = entity:gsub(" ", "_"):gsub("-", "_") - error(name .. " isnt something that exists. Did you make a typo? ") - end - - local prototype = game.entity_prototypes[entity] - --local collision_box = prototype.collision_box - local width = 1--math.abs(collision_box.right_bottom.x - collision_box.left_top.x) - local height = 1--math.abs(collision_box.right_bottom.y - collision_box.left_top.y) - - -- game.print("Width: " .. width) - -- game.print("Height: " .. height) - - local target_area = { - {position.x - width , position.y - height }, - {position.x + width , position.y + height } - } - --local entities = player.surface.find_entities_filtered{area = target_area} --, name = entity} - local entities = player.surface.find_entities_filtered{area = target_area, name = entity} - -- game.print("Number of entities found: " .. #entities) - - local closest_distance = math.huge - local closest_entity = nil - - for _, building in ipairs(entities) do - if building.name ~= 'character' then - local distance = ((position.x - building.position.x) ^ 2 + - (position.y - building.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest_entity = building - end - end - end - - if closest_entity ~= nil then - local entity = closest_entity -- get the first entity of the specified type in the area - local serialized = global.utils.serialize_entity(entity) - --local entity_json = game.table_to_json(serialized)-- game.table_to_json(entity - return serialized - else - error("\"No entity of type " .. entity .. " found at the specified position.\"") - end -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_prototype_recipe.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_prototype_recipe.lua deleted file mode 100644 index e3e7c41f9..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_prototype_recipe.lua +++ /dev/null @@ -1,10 +0,0 @@ -global.actions.get_prototype_recipe = function(player_index, recipe_name) - local player = global.agent_characters[player_index] - local recipe = player.force.recipes[recipe_name] - if not recipe then - return "recipe doesnt exist" - end - local serialized = global.utils.serialize_recipe(recipe) - return serialized -end - diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_research_progress.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_research_progress.lua deleted file mode 100644 index ba15fff0f..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_research_progress.lua +++ /dev/null @@ -1,47 +0,0 @@ -global.actions.get_research_progress = function(player_index, technology_name) - local player = global.agent_characters[player_index] - local force = player.force - - -- If no technology specified, use current research - if not technology_name then - if not force.current_research then - error("\"No research currently in progress\"") - end - technology_name = force.current_research.name - end - - -- Get the technology - local tech = force.technologies[technology_name] - if not tech then - error(string.format("\"Technology %s doesn't exist\"", technology_name)) - end - - -- Get progress info and calculate remaining work - local is_current = force.current_research and force.current_research.name == technology_name - local progress = 0 - local remaining_units = 0 - - if tech.researched then - -- Already researched technologies need no resources - return {} - elseif is_current then - progress = force.research_progress - -- Calculate remaining research units based on progress - remaining_units = math.ceil(tech.research_unit_count * (1 - progress)) - else - -- Not started technologies need all resources - remaining_units = tech.research_unit_count - end - - -- Collect remaining ingredients info - local ingredients = {} - for _, ingredient in pairs(tech.research_unit_ingredients) do - table.insert(ingredients, { - name = "\""..ingredient.name.."\"", - count = ingredient.amount * remaining_units, - type = ingredient.type - }) - end - - return ingredients -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_resource_patch.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_resource_patch.lua deleted file mode 100644 index 7b59db8e8..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/get_resource_patch.lua +++ /dev/null @@ -1,105 +0,0 @@ -global.actions.get_resource_patch = function(player_index, resource, x, y, radius) - local player = global.agent_characters[player_index] - local position = {x = x, y = y} - local surface = player.surface - - local function render_box(box) - local left_bottom = {x=box.left_top.x, y=box.right_bottom.y} - local right_top = {y=box.left_top.y, x=box.right_bottom.x} - - rendering.clear() - rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 1, g = 0, b = 0}, surface = surface, radius = 0.5, filled = false, target = box.left_top, time_to_live = 60000} - rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 0, g = 1, b = 0}, surface = surface, radius = 0.5, filled = false, target = box.right_bottom, time_to_live = 60000} - rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 1, g = 0, b = 1}, surface = surface, radius = 0.5, filled = false, target = left_bottom, time_to_live = 60000} - rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 0, g = 1, b = 1}, surface = surface, radius = 0.5, filled = false, target = right_top, time_to_live = 60000} - - end - -- Function to expand bounding box - local function expand_bounding_box(box, pos) - box.left_top.x = math.min(box.left_top.x, pos.x) - box.left_top.y = math.min(box.left_top.y, pos.y) - box.right_bottom.x = math.max(box.right_bottom.x, pos.x) - box.right_bottom.y = math.max(box.right_bottom.y, pos.y) - - - end - - -- Initialize bounding box - local bounding_box = {left_top = {x = x, y = y}, right_bottom = {x = x, y = y}} - - if resource == "water" then - local water_tiles = surface.find_tiles_filtered{position = position, name = "water", radius = radius} - if #water_tiles == 0 then - error("No water at the specified location.") - end - - local total_water_tiles = 0 - for _, tile in pairs(water_tiles) do - expand_bounding_box(bounding_box, tile.position) - total_water_tiles = total_water_tiles + 1 - end - bounding_box.left_top.x = bounding_box.left_top.x - 0.5 - bounding_box.left_top.y = bounding_box.left_top.y - 0.5 - bounding_box.right_bottom.y = bounding_box.right_bottom.y + 1.5 - bounding_box.right_bottom.x = bounding_box.right_bottom.x + 1.5 - render_box(bounding_box) - return {bounding_box = bounding_box, size = total_water_tiles} - elseif resource == "wood" then - local trees = surface.find_entities_filtered{ - position = position, - type = "tree", - radius = radius - } - if #trees == 0 then - error("No trees at the specified location.") - end - local total_wood = 0 - for _, tree in pairs(trees) do - expand_bounding_box(bounding_box, tree.position) - -- Estimate wood amount based on tree prototype - local tree_product = tree.prototype.mineable_properties.products[1] - if tree_product and tree_product.name == "wood" then - total_wood = total_wood + tree_product.amount - else - -- If wood amount is not specified, assume 1 wood per tree - total_wood = total_wood + 1 - end - end - render_box(bounding_box) - return {bounding_box = bounding_box, size = total_wood} - else - local resource_entities = surface.find_entities_filtered{position = position, name = resource, radius = radius} - if #resource_entities == 0 then - error("\"No resource of type " .. resource .. " at the specified location.\"") - end - - -- Recursive function to explore all connected resource entities - local function explore_resource_patch(entity, visited) - local key = entity.position.x .. "," .. entity.position.y - if visited[key] then - return 0 - end - visited[key] = true - expand_bounding_box(bounding_box, entity.position) - local resource_count = entity.amount - - local neighbors = surface.find_entities_filtered{ - area = {{entity.position.x - 1, entity.position.y - 1}, {entity.position.x + 1, entity.position.y + 1}}, - type = "resource", - name = resource - } - for _, neighbor in pairs(neighbors) do - if neighbor ~= entity then - resource_count = resource_count + explore_resource_patch(neighbor, visited) - end - end - return resource_count - end - - local visited = {} - local total_resource = explore_resource_patch(resource_entities[1], visited) - - render_box(bounding_box) - return {bounding_box = bounding_box, size = total_resource} - end -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/harvest_resource.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/harvest_resource.lua deleted file mode 100644 index 9cea09536..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/harvest_resource.lua +++ /dev/null @@ -1,622 +0,0 @@ ---- global.actions.harvest_resource(player_index, x, y, count, radius) -local function calculate_mining_ticks(entity) - local mining_time = entity.prototype.mineable_properties.mining_time or 1 - -- Convert mining time (in seconds) to ticks (60 ticks per second) - return math.ceil(mining_time * 60) -end - -local function update_production_stats(force, entity_name, amount) - local stats = force.item_production_statistics - stats.on_flow(entity_name, amount) - if global.harvested_items[entity_name] then - global.harvested_items[entity_name] = global.harvested_items[entity_name] + amount - else - global.harvested_items[entity_name] = amount - end - end - -local function get_entity_yield(entity) - local yield = 0 - if entity.valid and entity.minable then - local products = entity.prototype.mineable_properties.products - for _, product in pairs(products) do - yield = yield + (product.amount or 1) - end - end - return yield -end - --- Function to calculate distance between two points -local function distance(pos1, pos2) - return math.sqrt((pos1.x - pos2.x)^2 + (pos1.y - pos2.y)^2) -end - --- Function to sort entities by distance from a given position -local function sort_entities_by_distance(entities, from_position) - table.sort(entities, function(a, b) - return distance(a.position, from_position) < distance(b.position, from_position) - end) - return entities -end - --- Helper function to start mining an entity and track yields -local function start_mining_entity(player, entity) - if entity.valid and entity.minable then - --game.print("Starting mining entity " .. entity.name .. " at " .. serpent.line(entity.position)) - - -- First select the entity - - --player.selected = entity - - -- Then set mining state with position - if not player.mining_state.mining then - player.update_selected_entity(entity.position) - player.mining_state = { - mining = true, - position = entity.position - } - end - - - -- Calculate expected yield - local expected_yield = 0 - local products = entity.prototype.mineable_properties.products - for _, product in pairs(products) do - expected_yield = expected_yield + (product.amount or 1) - end - - return { - entity = entity, - start_tick = game.tick, - expected_yield = expected_yield - } - end - return nil -end - - -local function add_entities_to_queue(queue, entities, count) - local expected_yield = 0 - local added = 0 - - sort_entities_by_distance(entities, queue.mining_position) - - for _, entity in ipairs(entities) do - if entity.valid and entity.minable then - local yield = get_entity_yield(entity) - if expected_yield + yield <= count then - table.insert(queue.entities, entity) - expected_yield = expected_yield + yield - added = added + 1 - elseif expected_yield < count then - -- Add this entity even though it will exceed count - table.insert(queue.entities, entity) - expected_yield = expected_yield + yield - break - else - break - end - end - end - - return expected_yield -end - -script.on_nth_tick(15, function(event) - -- If no queues at all, just return - if not global.harvest_queues then return end - - for player_index, queue in pairs(global.harvest_queues) do - local player = global.agent_characters[player_index] - -- Skip if player not valid - if not player or not player.valid then goto continue end - - -- Already reached or exceeded our target? - if queue.total_yield >= queue.target_yield then - -- Remove this player's queue - global.harvest_queues[player_index] = nil - goto continue - end - - -- Check if player is still in resource reach distance - local dist_x = player.position.x - queue.mining_position.x - local dist_y = player.position.y - queue.mining_position.y - local sq_dist = (dist_x * dist_x) + (dist_y * dist_y) - local sq_reach = (player.resource_reach_distance * player.resource_reach_distance) - if sq_dist > sq_reach then - -- Too far away; do nothing for now - goto continue - end - - -- If there's no current mining, pick up the next entity - if not queue.current_mining then - local next_entity = table.remove(queue.entities, 1) - if not next_entity then - -- No more entities left - global.harvest_queues[player_index] = nil - goto continue - end - - -- Start mining - queue.current_mining = { - entity = next_entity, - start_tick = game.tick - } - else - -- We have a current entity being mined - local entity = queue.current_mining.entity - if not entity or not entity.valid or not entity.minable then - -- Entity no longer valid, skip - queue.current_mining = nil - goto continue - end - - local ticks_mining = game.tick - queue.current_mining.start_tick - if ticks_mining >= 30 then - -- Time to finish mining - local inv_before = player.get_main_inventory().get_contents() - local mined_ok = player.mine_entity(entity) -- Instantly mines & adds items - if mined_ok then - local inv_after = player.get_main_inventory().get_contents() - - -- Figure out how many items we actually gained - local items_added = 0 - for name, after_count in pairs(inv_after) do - local before_count = inv_before[name] or 0 - items_added = items_added + (after_count - before_count) - end - - if items_added > 0 then - -- Add to our queue's total_yield - local new_total = queue.total_yield + items_added - - if new_total > queue.target_yield then - -- We overshot. Remove the extras from the player's inventory. - local overshoot = new_total - queue.target_yield - -- We'll try to remove it from whatever items were gained. - -- If multiple resource types might drop, you'd handle them individually. - - local overshoot_left = overshoot - for name, after_count in pairs(inv_after) do - local before_count = inv_before[name] or 0 - local gained_this_item = (after_count - before_count) - if gained_this_item > 0 then - local to_remove = math.min(overshoot_left, gained_this_item) - local actually_removed = player.remove_item({name = name, count = to_remove}) - overshoot_left = overshoot_left - actually_removed - if overshoot_left <= 0 then - break - end - end - end - new_total = queue.target_yield - end - - queue.total_yield = new_total - end - end - - -- Clear current mining - queue.current_mining = nil - end - end - ::continue:: - end -end) - - -local function check_inventory_space(player, entity, count) - -- Get player's main inventory - local inventory = player.get_main_inventory() - if not inventory then return false end - - -- Create a temporary inventory to test insertion - local test_inventory = game.create_inventory(1) - local can_insert = true - - -- Calculate total items that would be added - local total_items = 0 - local products = entity.prototype.mineable_properties.products - for _, product in pairs(products) do - local amount = (product.amount or 1) * count - -- Try to insert into test inventory - local test_stack = {name = product.name, count = amount} - if not inventory.can_insert(test_stack) then - can_insert = false - break - end - total_items = total_items + amount - end - - -- Clean up test inventory - test_inventory.destroy() - - if not can_insert then - error("Inventory is full") - end - - return true -end - - -local function find_entity_type_at_position(surface, position) - local exact_entities = surface.find_entities_filtered{ - position = position, - type = {"tree", "resource", "simple-entity"},--, "optimized-decorative"}, - radius = 1 -- Tiny radius for exact position check - } - - if #exact_entities > 0 then - -- game.print("Found type ".. exact_entities[1].name) - return exact_entities[1].type, exact_entities[1].name - end - return nil, nil -end - -local function harvest_specific_resources(player, surface, position, count, target_type, target_name) - -- game.print("Harvesting specific resources") - -- Use existing harvest/harvest_trees functions but filtered to specific type - local radius = player.resource_reach_distance - local entities = surface.find_entities_filtered{ - position = position, - radius = radius, - type = target_type, - name = target_name, - limit = count - } - - if #entities == 0 then - error("No matching resources found within range") - end - - -- Use your existing harvest functions based on type - if target_type == "tree" then - return harvest_trees(entities, count, position, player) - else - return harvest(entities, count, position, player) - end -end - - -local function find_entities_at_position(surface, position, entity_types, exact) - local radius = exact and 0.1 or nil -- Use tiny radius for exact position check - return surface.find_entities_filtered{ - position = position, - type = entity_types, - radius = radius - } -end - -local function begin_mining(queue, player) - if #queue.entities > 0 then - local first_entity = table.remove(queue.entities, 1) - queue.current_mining = start_mining_entity(player, first_entity) - if not queue.current_mining then - error("Failed to start mining entity") - end - end -end - -local function initialize_harvest_queue(player_index, position, target_yield) - if not global.harvest_queues then - global.harvest_queues = {} - end - - global.harvest_queues[player_index] = { - entities = {}, - mining_position = position, - total_mined = 0, - total_yield = 0, - current_mining = nil, - target_yield = target_yield - } - - return global.harvest_queues[player_index] -end - - -local function harvest_resource_slow(player, player_index, surface, position, count) - local exact_entities = find_entities_at_position(surface, position, {"tree", "resource"}, true) - - if #exact_entities > 0 then - local queue = initialize_harvest_queue(player_index, position, count) - local expected_yield = add_entities_to_queue(queue, exact_entities, count) - begin_mining(queue, player) - return expected_yield - end - - local radius_entities = find_entities_at_position(surface, position, {"tree", "resource"}, false) - if #radius_entities == 0 then - error("No harvestable entities found within range") - end - - local queue = initialize_harvest_queue(player_index, position, count) - local expected_yield = add_entities_to_queue(queue, radius_entities, count) - -- game.print("expected "..expected_yield) - begin_mining(queue, player) - return expected_yield -end - -function harvest(entities, count, from_position, player) - if count == 0 then return 0 end - local yield = 0 - - -- Check inventory space first using the first valid entity as reference - local reference_entity = nil - for _, entity in ipairs(entities) do - if entity.valid and entity.minable then - reference_entity = entity - break - end - end - - if reference_entity then - check_inventory_space(player, reference_entity, count) - end - - - entities = sort_entities_by_distance(entities, from_position) - - ::start:: - local has_mined = false - for _, entity in ipairs(entities) do - if entity.valid and entity.minable then - - -- Calculate mining ticks before mining the entity - if global.fast then - global.elapsed_ticks = global.elapsed_ticks + calculate_mining_ticks(entity) - end - - local products = entity.prototype.mineable_properties.products - for _, product in pairs(products) do - local amount = product.amount or 1 - yield = yield + amount - entity.mine({ignore_minable=false, raise_destroyed=true}) - player.insert({name=product.name, count=amount}) - update_production_stats(player.force, product.name, amount) - has_mined = true - if yield >= count then break end - end - if yield >= count then break end - end - end - if has_mined == true and yield < count then - goto start - end - return yield -end - -function harvest_trees(entities, count, from_position, player) - -- game.print("Harvesting "..#entities.." trees") - if count == 0 then return 0 end - local yield = 0 - entities = sort_entities_by_distance(entities, from_position) - - for _, entity in ipairs(entities) do - if yield >= count then break end - if entity.valid and entity.type == "tree" then - - -- Calculate mining ticks before mining the tree - if global.fast then - global.elapsed_ticks = global.elapsed_ticks + calculate_mining_ticks(entity) - end - - local products = entity.prototype.mineable_properties.products - for _, product in pairs(products) do - if product.name == "wood" then - local amount = product.amount or 1 - player.insert({name="wood", count=amount}) - update_production_stats(player.force, "wood", amount) - yield = yield + amount - - local tree_position = entity.position - local tree_surface = entity.surface - local stump_name = entity.name.."-stump" - entity.destroy({raise_destroy=true}) - -- Try to create stump, if fails just continue - pcall(function() - tree_surface.create_entity({name=stump_name, position=tree_position}) - end) - end - end - end - end - return yield -end - --- Add function to handle simple entities like rocks and tree stumps -local function harvest_simple_entities(entities, count, from_position, player) - if count == 0 then return 0 end - local yield = 0 - entities = sort_entities_by_distance(entities, from_position) - - for _, entity in ipairs(entities) do - if entity.valid and entity.minable then - -- Calculate mining ticks before mining the entity - if global.fast then - global.elapsed_ticks = global.elapsed_ticks + calculate_mining_ticks(entity) - end - - local products = entity.prototype.mineable_properties.products - for _, product in pairs(products) do - local amount = product.amount or 1 - yield = yield + amount - entity.mine({ignore_minable=false, raise_destroyed=true}) - player.insert({name=product.name, count=amount}) - update_production_stats(player.force, product.name, amount) - - if yield >= count then break end - end - if yield >= count then break end - end - end - return yield -end - - -global.actions.harvest_resource = function(player_index, x, y, count, radius) - local player = global.agent_characters[player_index] - if not player then - error("Player not found") - end - - local player_position = player.position - local position = {x=x, y=y} - - local distance = math.sqrt((position.x - player_position.x)^2 + (position.y - player_position.y)^2) - if distance > player.resource_reach_distance then - error("Nothing within reach to harvest") - end - - local surface = player.surface - local target_type, target_name = find_entity_type_at_position(surface, position) - - if not target_type then - error("Nothing within reach to harvest") - end - - --if not global.fast then - -- return harvest_resource_slow(player, player_index, surface, position, count, radius) - --end - - local total_yield = 0 - if target_type then - total_yield = total_yield + harvest_specific_resources(player, surface, position, count, target_type, target_name) - if total_yield >= count then - -- game.print("Harvested " .. total_yield .. " items of " .. target_name) - return total_yield - end - end - - -- Try harvesting each type in sequence if we haven't met our quota - if total_yield < count then - -- Try trees first - local tree_entities = surface.find_entities_filtered{ - position = position, - radius = radius, - type = "tree" - } - total_yield = total_yield + harvest_trees(tree_entities, count - total_yield, position, player) - end - - if total_yield < count then - -- Then try simple entities (rocks, stumps, etc.) - local simple_entities = surface.find_entities_filtered{ - position = position, - radius = radius, - type = "simple-entity" - } - total_yield = total_yield + harvest_simple_entities(simple_entities, count - total_yield, position, player) - end - - if total_yield < count then - -- Finally try standard resources - local mineable_entities = surface.find_entities_filtered{ - position = position, - radius = radius, - type = "resource" - } - total_yield = total_yield + harvest(mineable_entities, count - total_yield, position, player) - end - - if total_yield == 0 then - error("Nothing within reach to harvest") - else - -- game.print("Harvested resources yielding " .. total_yield .. " items") - return total_yield - end -end --- ---global.actions.harvest_resource2 = function(player_index, x, y, count, radius) --- local player = global.agent_characters[player_index] --- if not player then --- error("Player not found") --- end --- --- local player_position = player.position --- local position = {x=x, y=y} --- --- local distance = math.sqrt((position.x - player_position.x)^2 + (position.y - player_position.y)^2) --- if distance > player.resource_reach_distance then --- error("Nothing within reach to harvest") --- end --- local surface = player.surface --- --- -- Check what's under the player first --- local target_type, target_name = find_entity_type_at_position(surface, position) --- if not target_type then --- error("Nothing within reach to harvest") --- end --- if not global.fast then --- return harvest_resource_slow(player, player_index, surface, position, count, radius) --- end --- --- --- local total_yield = 0 --- if target_type then --- -- If we found something at the exact position, harvest that specific type --- total_yield = total_yield + harvest_specific_resources(player, surface, position, count, target_type, target_name) --- if total_yield >= count then --- game.print("Harvested " .. total_yield .. " items of " .. target_name) --- return total_yield --- end --- end --- --- -- If nothing at exact position or couldn't get enough yield, fall back to original logic --- local tree_entities = surface.find_entities_filtered{position=position, radius=radius, type = "tree"} --- local tree_yield = harvest_trees(tree_entities, count - total_yield, position, player) --- local total_yield = total_yield + tree_yield --- --- if tree_yield < count then --- local mineable_entities = surface.find_entities_filtered{position=position, radius=radius, type = "resource"} --- local resource_yield = harvest(mineable_entities, count - total_yield, position, player) --- total_yield = total_yield + resource_yield --- --- if total_yield == 0 then --- error("Could not harvest at position ("..position.x..", "..position.y..").") --- end --- end --- --- if total_yield == 0 then --- error("Nothing within reach to harvest") --- else --- game.print("Harvested resources yielding " .. total_yield .. " items") --- return total_yield --- end ---end - - -global.actions.clear_harvest_queue = function(player_index) - if global.harvest_queues and global.harvest_queues[player_index] then - global.harvest_queues[player_index] = nil - end -end - -global.actions.get_harvest_queue_length = function(player_index) - if global.harvest_queues and global.harvest_queues[player_index] then - return #global.harvest_queues[player_index].entities - end - return 0 -end - -global.actions.get_resource_name_at_position = function(player_index, x, y) - local player = global.agent_characters[player_index] - if not player then - error("Player not found") - end - - local position = {x=x, y=y} - local surface = player.surface - - local entities = surface.find_entities_filtered{ - position = position, - radius = player.resource_reach_distance, - type = {"tree", "resource"}, - limit = 1 - } - local entity_name = nil - if #entities > 0 then - entity_name = entities[1].name - end - return entity_name -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/insert_item.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/insert_item.lua deleted file mode 100644 index a98cf25be..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/insert_item.lua +++ /dev/null @@ -1,244 +0,0 @@ --- Function to get inventory fullness information -local function get_inventory_info(entity) - if entity.get_inventory then - -- Try common inventory types - local inv = entity.get_inventory(defines.inventory.chest) or -- For chests - entity.get_inventory(defines.inventory.furnace_source) or -- For furnaces - entity.get_inventory(defines.inventory.assembling_machine_input) -- For assemblers - - if inv then - -- Get actual item count and inventory capacity - local item_count = 0 - for i = 1, #inv do - local stack = inv[i] - if stack and stack.valid_for_read then - item_count = item_count + stack.count - end - end - - -- Calculate total capacity (slots * stack size) - local capacity = #inv * game.item_prototypes[inv[1].name or "iron-plate"].stack_size - - return string.format("(%d/%d items)", item_count, capacity) - end - end - return "" -end - -global.actions.insert_item = function(player_index, insert_item, count, x, y, target_name) - local player = global.agent_characters[player_index] - local position = {x=x, y=y} - local surface = player.surface - - -- Check if player has enough items - local item_count = player.get_item_count(insert_item) - if item_count == 0 then - error('\"No '..insert_item..' to insert from your inventory\"') - end - - local closest_distance = math.huge - local closest_entity = nil - local area = {{position.x - 1, position.y - 1}, {position.x + 1, position.y + 1}} - local buildings = nil - - if target_name then - buildings = surface.find_entities_filtered{area = area, name=target_name} - else - buildings = surface.find_entities_filtered{area = area} - end - - -- Function to get inventory fullness information - --local function get_inventory_info(entity) - -- if entity.get_inventory then - -- local inv = entity.get_inventory(defines.inventory.chest) - -- if inv then - -- return string.format("(%d/%d)", #inv, #inv.get_bar()) - -- end - -- end - -- return "" - --end - - -- Function to check if an item can be inserted into an entity - local function can_insert_item(entity, item_name) - if entity.type == "transport-belt" then - -- All items that can be on ground can be on belts - local item_prototype = game.item_prototypes[item_name] - return item_prototype and not item_prototype.has_flag("only-in-cursor") - - elseif entity.type == "lab" then - -- Check if the item is a science pack - local item_prototype = game.item_prototypes[item_name] - return item_prototype and item_prototype.type == "tool" - - elseif entity.type == "assembling-machine" then - local recipe = entity.get_recipe() - if recipe then - -- Check if the item is an ingredient or the result of the recipe - for _, ingredient in pairs(recipe.ingredients) do - if ingredient.name == item_name then - return true - end - end - -- Check if the item is the result of the recipe - for _, product in pairs(recipe.products) do - if product.name == item_name then - return true - end - end - return false - end - elseif entity.type == "furnace" then - -- Check if it's a fuel - if game.item_prototypes[item_name].fuel_value > 0 then - return true - end - -- Check furnace inventory for incompatible items - local inventory = entity.get_inventory(defines.inventory.furnace_source) - if inventory and not inventory.is_empty() then - local existing_item = nil - for i = 1, #inventory do - local stack = inventory[i] - if stack.valid_for_read then - existing_item = stack.name - break - end - end - if existing_item and existing_item ~= item_name then - error("\"furnace already contains " .. existing_item.." so cannot insert " .. item_name .."\"") - end - end - -- Check if it's a valid ingredient for any furnace recipe - for _, recipe in pairs(game.recipe_prototypes) do - if recipe.category == "smelting" then - for _, ingredient in pairs(recipe.ingredients) do - if ingredient.name == item_name then - return true - end - end - end - end - return false - ---- Check if it's a fuel - --if game.item_prototypes[item_name].fuel_value > 0 then - -- return true - --end - ---- Check if it's a valid ingredient for any furnace recipe - --for _, recipe in pairs(game.recipe_prototypes) do - -- if recipe.category == "smelting" then - -- for _, ingredient in pairs(recipe.ingredients) do - -- if ingredient.name == item_name then - -- return true - -- end - -- end - -- end - --end - --return false - elseif entity.burner then - -- Check if it's a fuel - return game.item_prototypes[item_name].fuel_value > 0 - elseif entity.type == "container" or entity.type == "logistic-container" then - return true -- Containers can accept any item - end - -- Add more entity types as needed - return true - end - - -- Find the closest suitable building - for _, building in ipairs(buildings) do - if building.name ~= 'character' and can_insert_item(building, insert_item) then - local distance = ((position.x - building.position.x) ^ 2 + (position.y - building.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest_entity = building - end - end - end - - if closest_entity == nil then - error("\"Could not find a nearby entity that can accept " .. insert_item.."\"") - end - - -- Throw an error if the entity is too far away from the player - if closest_distance > 10 then - error("\"Entity at ("..closest_entity.position.x..", "..closest_entity.position.y..") is too far away from your position of ("..player.position.x..", "..player.position.y.."), move closer.\"") - end - - -- Function to insert items onto a transport belt - one at a time - local function insert_on_belt(belt, item_name) - local line1 = belt.get_transport_line(1) - local line2 = belt.get_transport_line(2) - - -- Try first line - if line1.can_insert_at_back() then - if line1.insert_at_back({name = item_name, count = 1}) then - return 1 - end - end - - -- If first line failed, try second line - if line2.can_insert_at_back() then - if line2.insert_at_back({name = item_name, count = 1}) then - return 1 - end - end - - return 0 -- Could not insert on either line - end - - -- Determine how many items can be inserted - local insertable_count = math.min(count, item_count) - - -- Attempt to insert items - local inserted = 0 - if closest_entity.type == "transport-belt" then - -- For transport belts, we need to use a different method - -- game.print("Inserting ".. insertable_count.. " items onto transport belt...") - inserted = insert_on_belt(closest_entity, insert_item) - elseif closest_entity.type == "assembling-machine" then - local recipe = closest_entity.get_recipe() - if recipe then - local is_product = false - for _, product in pairs(recipe.products) do - if product.name == insert_item then - is_product = true - break - end - end - - if is_product then - -- Insert into output inventory - inserted = closest_entity.get_output_inventory().insert({name=insert_item, count=insertable_count}) - else - -- Insert into input inventory - inserted = closest_entity.get_inventory(defines.inventory.assembling_machine_input).insert({name=insert_item, count=insertable_count}) - end - else - error("No recipe set for the assembling machine.") - end - else - -- For other entities, use the normal insert method - inserted = closest_entity.insert{name=insert_item, count=insertable_count} - end - - -- game.print("Inserted " .. inserted .. " items.") - if inserted > 0 then - -- Only remove successfully inserted items from player - player.remove_item{name=insert_item, count=inserted} - -- game.print("Successfully inserted " .. inserted .. " items.") - return global.utils.serialize_entity(closest_entity) - else - local inventory_info = get_inventory_info(closest_entity) - local error_msg = string.format( - "\"Failed to insert %s into %s (type %s) at position %s. " .. - "Attempted to insert %d items. %s %s\"", - insert_item, - closest_entity.name, - closest_entity.type, - serpent.line(closest_entity.position), - insertable_count, - inventory_info ~= "" and "Inventory is full " .. inventory_info or "Entity might not accept this item or has no available space.", - inventory_info - ) - error(error_msg) - end -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/inspect_inventory.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/inspect_inventory.lua deleted file mode 100644 index 1b5c3c96f..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/inspect_inventory.lua +++ /dev/null @@ -1,219 +0,0 @@ -global.actions.inspect_inventory = function(player_index, is_character_inventory, x, y, entity, all_players) - local position = {x=x, y=y} - local player = global.agent_characters[player_index] - local surface = player.surface - local is_fast = global.fast - local automatic_close = True - - local function get_player_inventory_items(player) - -- if not is_fast then - -- player.opened = player - -- script.on_nth_tick(60, function() - -- if automatic_close == True then - -- player.opened = nil - -- automatic_close = False - -- end - -- end) - -- end - - local inventory = player.get_main_inventory() - if not inventory or not inventory.valid then - return nil - end - - local item_counts = inventory.get_contents() - return item_counts - end - - local function get_inventory() - local closest_distance = math.huge - local closest_entity = nil - - local area = {{position.x - 2, position.y - 2}, {position.x + 2, position.y + 2}} - local buildings = surface.find_entities_filtered({ area = area, force = "player", name = entity }) - -- game.print("Found "..#buildings.. " "..entity) - for _, building in ipairs(buildings) do - if building.name ~= 'character' then - local distance = ((position.x - building.position.x) ^ 2 + (position.y - building.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest_entity = building - end - end - end - - if closest_entity == nil then - error("No entity at given coordinates.") - end - - if not is_fast then - player.opened = closest_entity - script.on_nth_tick(60, function() - if automatic_close == True then - player.opened = nil - automatic_close = False - end - end) - end - - if closest_entity.type == "furnace" then - local source = closest_entity.get_inventory(defines.inventory.furnace_source).get_contents() - local output = closest_entity.get_inventory(defines.inventory.furnace_result).get_contents() - for k, v in pairs(output) do - source[k] = (source[k] or 0) + v - end - return source - end - - if closest_entity.type == "assembling-machine" then - local source = closest_entity.get_inventory(defines.inventory.assembling_machine_input).get_contents() - local output = closest_entity.get_inventory(defines.inventory.assembling_machine_output).get_contents() - for k, v in pairs(output) do - source[k] = (source[k] or 0) + v - end - return source - end - - if closest_entity.type == "lab" then - return closest_entity.get_inventory(defines.inventory.lab_input).get_contents() - end - - -- Handle centrifuge inventories - if closest_entity.type == "assembling-machine" and closest_entity.name == "centrifuge" then - local source = closest_entity.get_inventory(defines.inventory.assembling_machine_input).get_contents() - local output = closest_entity.get_inventory(defines.inventory.assembling_machine_output).get_contents() - -- Merge input and output contents - for k, v in pairs(output) do - source[k] = (source[k] or 0) + v - end - return source - end - - return closest_entity.get_inventory(defines.inventory.chest).get_contents() - end - - local player = global.agent_characters[player_index] - if not player then - error("Player not found") - end - - if all_players then - local all_inventories = {} - for _, p in pairs(global.agent_characters) do - local inventory_items = get_player_inventory_items(p) - if inventory_items then - table.insert(all_inventories, inventory_items) - else - table.insert(all_inventories, {}) - end - end - return dump(all_inventories) - end - - if is_character_inventory then - local inventory_items = get_player_inventory_items(player) - if inventory_items then - return dump(inventory_items) - else - error("Could not get player inventory") - end - else - local inventory_items = get_inventory() - if inventory_items then - return dump(inventory_items) - else - error("Could not get inventory of entity at "..x..", "..y) - end - end -end - -global.actions.inspect_inventory2 = function(player_index, is_character_inventory, x, y) - local position = {x=x, y=y} - local player = global.agent_characters[player_index] - local surface = player.surface - - local function get_player_inventory_items(player) - local inventory = player.get_main_inventory() - if not inventory or not inventory.valid then - return nil - end - - local item_counts = inventory.get_contents() - return item_counts - end - - local function get_inventory() - local closest_distance = math.huge - local closest_entity = nil - - local area = {{position.x - 0.5, position.y - 0.5}, {position.x + 0.5, position.y + 0.5}} - local buildings = surface.find_entities_filtered{area = area, force = "player"} - -- Find the closest building - for _, building in ipairs(buildings) do - if building.rotatable and building.name ~= 'character' then - local distance = ((position.x - building.position.x) ^ 2 + (position.y - building.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest_entity = building - end - end - end - - if closest_entity == nil then - error("No entity at given coordinates.") - end - - -- If the closest entity is a furnace, return the inventory of the furnace - if closest_entity.type == "furnace" then - local source = closest_entity.get_inventory(defines.inventory.furnace_source).get_contents() - local output = closest_entity.get_inventory(defines.inventory.furnace_result).get_contents() - -- Merge the two tables - for k, v in pairs(output) do - source[k] = (source[k] or 0) + v - end - return source - end - - -- If the closest entity is an assembling machine, return the inventory of the assembling machine - if closest_entity.type == "assembling-machine" then - local source = closest_entity.get_inventory(defines.inventory.assembling_machine_input).get_contents() - local output = closest_entity.get_inventory(defines.inventory.assembling_machine_output).get_contents() - -- Merge the two tables - for k, v in pairs(output) do - source[k] = (source[k] or 0) + v - end - return source - end - - -- If the closest entity is a lab, return the inventory of the lab - if closest_entity.type == "lab" then - return closest_entity.get_inventory(defines.inventory.lab_input).get_contents() - end - - -- For other entities (like chests), return the chest inventory - return closest_entity.get_inventory(defines.inventory.chest).get_contents() - end - - local player = global.agent_characters[player_index] - if not player then - error("Player not found") - end - - if is_character_inventory then - local inventory_items = get_player_inventory_items(player) - - if inventory_items then - return dump(inventory_items) - else - error("Could not get player inventory") - end - else - local inventory_items = get_inventory() - - if inventory_items then - return dump(inventory_items) - else - error("Could not get inventory of entity at "..x..", "..y) - end - end -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/launch_rocket.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/launch_rocket.lua deleted file mode 100644 index 5da6ead5f..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/launch_rocket.lua +++ /dev/null @@ -1,44 +0,0 @@ --- Function to find a rocket silo at the given position -local function find_rocket_silo(surface, position) - local silo = surface.find_entities_filtered{ - name = "rocket-silo", - position = position, - limit = 1, - radius=1000 - } - return silo[1] -end - --- Function to check if the silo has a rocket ready to launch -local function is_rocket_ready(silo) - if not silo then return false end - if not silo.valid then return false end - - -- Check if rocket is ready for launch - return silo.rocket_silo_status == defines.rocket_silo_status.rocket_ready -end - --- Function to launch rocket from specified position -global.actions.launch_rocket = function(x, y) - -- Get the current game surface - local surface = game.surfaces[1] - local position = {x=x, y=y} - -- Find rocket silo at the given position - local silo = find_rocket_silo(surface, position) - - if not silo then - game.print("No rocket silo found at specified position") - return false - end - - -- Check if silo has a rocket ready - if not is_rocket_ready(silo) then - game.print("Rocket is not ready for launch") - return false - end - - -- Launch the rocket - silo.launch_rocket() - game.print("Rocket launched successfully!") - return true -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/move_to.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/move_to.lua deleted file mode 100644 index a03dce61f..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/move_to.lua +++ /dev/null @@ -1,265 +0,0 @@ --- move_to - --- Register the tick handler when the module is loaded -if not global.fast then - script.on_nth_tick(5, function(event) - if global.walking_queues then - global.actions.update_walking_queues() - end - end) -end - ---local function get_direction(from_pos, to_pos) --- local dx = to_pos.x - from_pos.x --- local dy = to_pos.y - from_pos.y --- if dx == 0 and dy == 0 then --- return nil --- elseif math.abs(dx) > math.abs(dy) then --- return dx > 0 and defines.direction.east or defines.direction.west --- else --- return dy > 0 and defines.direction.south or defines.direction.north --- end ---end - - -global.actions.move_to = function(player_index, path_handle, trailing_entity, is_trailing) - --local player = global.agent_characters[player_index] - local player = global.agent_characters[player_index] - local path = global.paths[path_handle] - local surface = player.surface - - -- Check if path is valid - if not path or type(path) ~= "table" or #path == 0 then - error("Invalid path: " .. serpent.line(path)) - end - - -- If fast mode is disabled, set up walking queue - if not global.fast then - -- Initialize walking queue if it doesn't exist - if not global.walking_queues then - global.walking_queues = {} - end - - -- Create or clear existing queue for this player - if not global.walking_queues[player_index] then - global.walking_queues[player_index] = { - positions = {}, - current_target = nil, - trailing_entity = trailing_entity, - is_trailing = is_trailing - } - else - global.walking_queues[player_index].positions = {} - global.walking_queues[player_index].current_target = nil - global.walking_queues[player_index].trailing_entity = trailing_entity - global.walking_queues[player_index].is_trailing = is_trailing - end - - -- Add all path positions to the queue - for _, point in ipairs(path) do - table.insert(global.walking_queues[player_index].positions, point.position) - end - - -- Start walking to first position - if #global.walking_queues[player_index].positions > 0 then - local target = global.walking_queues[player_index].positions[1] - global.walking_queues[player_index].current_target = target - player.walking_state = { - walking = true, - direction = global.utils.get_direction(player.position, target) - } - end - - return player.position - end - - local function rotate_entity(entity, direction) - local direction_map = {defines.direction.north, defines.direction.east, defines.direction.south, defines.direction.west} - local inserter_direction_map = {defines.direction.south, defines.direction.west, defines.direction.north, defines.direction.east} - - if entity.type == "inserter" then - orientation = inserter_direction_map[direction/2+1] - else - orientation = direction_map[direction/2+1] - end - - while entity.direction ~= orientation do - entity.rotate() - end - end - - local function place(place_position, direction) - if global.utils.can_place_entity(player, trailing_entity, place_position, direction) then - if player.get_item_count(trailing_entity) > 0 then - local created = surface.create_entity{name=trailing_entity, position=place_position, direction=direction, force='player', player=player, build_check_type=defines.build_check_type.manual, fast_replace=true} - if created then - player.remove_item({name=trailing_entity, count=1}) - end - return created - else - error("\"No ".. trailing_entity .." in the inventory\"") - end - elseif surface.can_fast_replace{name=trailing_entity, position=place_position, direction=direction, force='player'} then - local existing_entity = surface.find_entity(trailing_entity, place_position) - if existing_entity and existing_entity.direction ~= direction then - rotate_entity(existing_entity, direction) - end - return existing_entity - end - return nil - end - - local function place_diagonal(from_pos, to_pos, is_leading) - local dx = to_pos.x - from_pos.x - local dy = to_pos.y - from_pos.y - local mid_pos = {x = from_pos.x , y = to_pos.y } - - local dir_x = dx > 0 and defines.direction.east or defines.direction.west - local dir_y = dy > 0 and defines.direction.south or defines.direction.north - - if is_leading then - place(to_pos, (dir_x + 4) % 8) - - local corner_dir - if (dx > 0 and dy > 0) or (dx < 0 and dy < 0) then - corner_dir = dir_x - else - corner_dir = dir_y - end - - if dx == 1 and dy == 1 then - corner_dir = defines.direction.east - end - - place(mid_pos, (corner_dir + 4) % 8) - else - place(from_pos, dir_y) - - local corner_dir - if (dx > 0 and dy > 0) or (dx < 0 and dy < 0) then - corner_dir = dir_y - else - corner_dir = dir_x - end - - if dx == 1 and dy == 1 then - corner_dir = defines.direction.east - end - - place(mid_pos, corner_dir) - end - end - - if is_trailing == 1 or is_trailing == 0 then - if game.entity_prototypes[trailing_entity] == nil then - error('No entity exists that can be laid') - end - end - - local prev_belt = nil - local prev_pos = player.position - for i = 1, #path do - local current_position = player.position - local target_position = path[i].position - - -- Calculate and accumulate movement ticks before teleporting - global.elapsed_ticks = global.elapsed_ticks + global.utils.calculate_movement_ticks(player, prev_pos, target_position) - - - local direction = global.utils.get_direction(prev_pos, target_position) - - if not direction then - goto continue - end - - local new_belt - if is_trailing == 1 then - if math.abs(prev_pos.x - target_position.x) == 1 and math.abs(prev_pos.y - target_position.y) == 1 then - --game.print("Placing diagonal belt at " .. serpent.line(prev_pos) .. " to " .. serpent.line(target_position)) - place_diagonal(prev_pos, target_position, false) - else - --game.print("Placing at direction: " .. direction .. " Current position: " .. serpent.line(prev_pos) .. " Target position: " .. serpent.line(target_position)) - new_belt = place(prev_pos, direction) - if prev_belt then - rotate_entity(prev_belt, global.utils.get_direction(prev_belt.position, prev_pos)) - end - end - player.teleport(target_position) - elseif is_trailing == 0 then - if math.abs(prev_pos.x - target_position.x) == 1 and math.abs(prev_pos.y - target_position.y) == 1 then - place_diagonal(prev_pos, target_position, true) - else - -- game.print("Placing at direction: " .. direction .. " Current position: " .. serpent.line(prev_pos) .. " Target position: " .. serpent.line(target_position)) - directions = {defines.direction.north, defines.direction.east, defines.direction.south, defines.direction.west} - opposite_direction = {defines.direction.south, defines.direction.west, defines.direction.north, defines.direction.east} - new_direction = opposite_direction[direction/2+1] - new_belt = place(target_position, new_direction) - if prev_belt then - rotate_entity(prev_belt, global.utils.get_direction(prev_belt.position, current_position)) - end - end - player.teleport(target_position) - else - player.teleport(target_position) - end - prev_belt = new_belt - prev_pos = target_position - ::continue:: - end - - return player.position -end - --- Add this new function to handle the walking queue updates --- This should be called on every tick -global.actions.update_walking_queues = function() - if not global.walking_queues then return end - - for player_index, queue in pairs(global.walking_queues) do - local player = global.agent_characters[player_index] - if not player or not queue.current_target then goto continue end - - local distance = ((player.position.x - queue.current_target.x)^2 + - (player.position.y - queue.current_target.y)^2)^0.5 - - -- If player is close enough to current target - if distance < 1 then - -- Remove the current position from queue - table.remove(queue.positions, 1) - - -- If there are more positions, start walking to next one - if #queue.positions > 0 then - queue.current_target = queue.positions[1] - player.walking_state = { - walking = true, - direction = global.utils.get_direction_with_diagonals(player.position, queue.current_target) - } - else - -- Queue is empty, stop walking - player.walking_state = {walking = false} - queue.current_target = nil - end - else - -- Update walking direction to current target - player.walking_state = { - walking = true, - direction = global.utils.get_direction_with_diagonals(player.position, queue.current_target) - } - end - - ::continue:: - end -end - -global.actions.clear_walking_queue = function(player_index) - if global.walking_queues and global.walking_queues[player_index] then - global.walking_queues[player_index] = nil - end -end - -global.actions.get_walking_queue_length = function(player_index) - if global.walking_queues and global.walking_queues[player_index] then - return #global.walking_queues[player_index].positions - end - return 0 -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest.lua deleted file mode 100644 index 430c9a1d7..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest.lua +++ /dev/null @@ -1,71 +0,0 @@ -global.actions.nearest = function(player_index, resource) - local player = global.agent_characters[player_index] - - local function normalize_resource_name(resource) - if resource == "copper" then - return "copper-ore" - elseif resource == "iron" then - return "iron-ore" - elseif resource == "uranium" then - return "uranium-ore" - else - return resource - end - end - - local normalized_resource = normalize_resource_name(resource) - - local function find_nearest(player, resource) - local surface = player.surface - local position = player.position - local closest_distance = math.huge - local closest = nil - local entities - - if resource == "wood" then - entities = surface.find_entities_filtered{ - area = {{position.x - 500, position.y - 500}, {position.x + 500, position.y + 500}}, - type = "tree" - } - elseif resource == "water" then - local water_positions = surface.find_tiles_filtered{ - area = {{position.x - 500, position.y - 500}, {position.x + 500, position.y + 500}}, - name = "water" - } - for _, water_tile in ipairs(water_positions) do - local distance = ((position.x - water_tile.position.x) ^ 2 + (position.y - water_tile.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest = water_tile.position - end - end - if closest == nil then - error("\"Could not find an entity called "..resource.."\"") - end - --return { x = position.x - closest.x, y = position.y - closest.y } - return {x = closest.x, y = closest.y} - else - entities = surface.find_entities_filtered{ - area = {{position.x - 500, position.y - 500}, {position.x + 500, position.y + 500}}, - name = resource - } - end - - for _, entity in ipairs(entities) do - local distance = ((position.x - entity.position.x) ^ 2 + (position.y - entity.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest = entity.position - end - end - - if closest == nil then - error("\"Could not find an entity called "..resource.."\"") - end - -- game.print("Distance to "..resource.." is "..closest_distance) - return {x= closest.x, y = closest.y} - --return { x = position.x - closest.x, y = position.y - closest.y } - end - - return find_nearest(player, normalized_resource) -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest_buildable.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest_buildable.lua deleted file mode 100644 index c4c7ed83f..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/nearest_buildable.lua +++ /dev/null @@ -1,189 +0,0 @@ --- Cache math functions -local floor = math.floor -local ceil = math.ceil -local max = math.max -local abs = math.abs - -global.actions.nearest_buildable = function(player_index, entity_name, bounding_box, center_position) - local player = global.agent_characters[player_index] - local surface = player.surface - local entity_prototype = game.entity_prototypes[entity_name] - local needs_resources = entity_prototype.resource_categories ~= nil - local start_pos = center_position or player.position - local needs_oil = entity_name == "pumpjack" - - -- Cache for chunk resources - local chunk_cache = {} - - local function get_chunk_resources(chunk_x, chunk_y) - local cache_key = chunk_x .. "," .. chunk_y - if not chunk_cache[cache_key] then - chunk_cache[cache_key] = surface.find_entities_filtered{ - area = { - {chunk_x * 32, chunk_y * 32}, - {(chunk_x + 1) * 32, (chunk_y + 1) * 32} - }, - collision_mask = "resource-layer" - } - end - return chunk_cache[cache_key] - end - - local function check_resource_coverage(left_top, right_bottom) - if not needs_resources then return true end - - if needs_oil then - local oil_count = surface.count_entities_filtered{ - area = {left_top, right_bottom}, - name = "crude-oil" - } - return oil_count > 0 - end - -- Quick initial resource count check - local total_resources = surface.count_entities_filtered{ - area = {left_top, right_bottom}, - collision_mask = "resource-layer" - } - - -- Calculate required coverage - local min_x = floor(left_top.x) - local min_y = floor(left_top.y) - local max_x = ceil(right_bottom.x) - 1 - local max_y = ceil(right_bottom.y) - 1 - local required_coverage = (max_x - min_x + 1) * (max_y - min_y + 1) - - -- Early exit if not enough resources - if total_resources < required_coverage then - return false - end - - -- Set up position tracking - local positions = {} - - -- Get relevant chunks - local chunk_min_x = floor(min_x / 32) - local chunk_min_y = floor(min_y / 32) - local chunk_max_x = floor(max_x / 32) - local chunk_max_y = floor(max_y / 32) - - -- Collect resources from relevant chunks - for chunk_x = chunk_min_x, chunk_max_x do - for chunk_y = chunk_min_y, chunk_max_y do - local resources = get_chunk_resources(chunk_x, chunk_y) - for _, resource in pairs(resources) do - local x = floor(resource.position.x) - local y = floor(resource.position.y) - if x >= min_x and x <= max_x and - y >= min_y and y <= max_y then - positions[x] = positions[x] or {} - positions[x][y] = true - end - end - end - end - - -- Verify complete coverage - for x = min_x, max_x do - if not positions[x] then return false end - for y = min_y, max_y do - if not positions[x][y] then return false end - end - end - - return true - end - - local function is_buildable_box(origin, box_dimensions) - -- Calculate actual positions - local left_top = { - x = origin.x + box_dimensions.left_top.x, - y = origin.y + box_dimensions.left_top.y - } - local right_bottom = { - x = origin.x + box_dimensions.right_bottom.x, - y = origin.y + box_dimensions.right_bottom.y - } - - -- Quick collision checks first - if surface.count_tiles_filtered{ - area = {left_top, right_bottom}, - collision_mask = "water-tile" - } > 0 then - return false - end - - if surface.count_entities_filtered{ - area = {left_top, right_bottom}, - collision_mask = "object-layer" - } > 0 then - return false - end - - -- Resource coverage check - if not check_resource_coverage(left_top, right_bottom) then - return false - end - - return true, left_top, right_bottom - end - - local function spiral_search() - local dx, dy = 0, 0 - local segment_length = 1 - local segment_passed = 0 - local direction = 0 -- 0: right, 1: down, 2: left, 3: up - local MAX_RADIUS = 30 - - while max(abs(dx), abs(dy)) <= MAX_RADIUS do - local current_pos = { - x = start_pos.x + dx, - y = start_pos.y + dy - } - - if bounding_box then - local is_buildable, left_top, right_bottom = is_buildable_box(current_pos, bounding_box) - if is_buildable then - --rendering.clear() - rendering.draw_rectangle{ - only_in_alt_mode=true, - surface = surface, - left_top = left_top, - right_bottom = right_bottom, - filled = false, - color = {r=0, g=1, b=0, a=0.5}, - time_to_live = 60000 - } - return {position=current_pos, left_top=left_top, right_bottom=right_bottom} - end - else - -- Simple position check for entities without bounding box - if surface.count_entities_filtered{ - area = {{current_pos.x, current_pos.y}, - {current_pos.x + 1, current_pos.y + 1}}, - collision_mask = needs_resources and "resource-layer" or "object-layer" - } == (needs_resources and 1 or 0) then - return current_pos - end - end - - -- Spiral pattern movement - segment_passed = segment_passed + 1 - if direction == 0 then dx = dx + 1 - elseif direction == 1 then dy = dy + 1 - elseif direction == 2 then dx = dx - 1 - else dy = dy - 1 end - - if segment_passed == segment_length then - segment_passed = 0 - direction = (direction + 1) % 4 - if direction % 2 == 0 then - segment_length = segment_length + 1 - end - end - end - - error("\"Could not find a buildable position for the entity: " .. entity_name.."\"") - end - - return spiral_search() -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/pickup_entity.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/pickup_entity.lua deleted file mode 100644 index f5225e88d..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/pickup_entity.lua +++ /dev/null @@ -1,154 +0,0 @@ -global.actions.pickup_entity = function(player_index, x, y, entity) - local player = global.agent_characters[player_index] - local position = {x=x, y=y} - local surface = player.surface - local success = false - rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.25, filled = false, target = position, time_to_live = 12000} - - -- Debug print - -- game.print("Starting pickup attempt for " .. entity .. " at (" .. x .. ", " .. y .. ")") - - -- Function to check if player can receive items - local function can_receive_items(items_to_check) - local main_inventory = player.get_main_inventory() - -- Check each item individually - for _, item in pairs(items_to_check) do - if not main_inventory.can_insert({name = item.name, count = item.count}) then - return false - end - end - return true - end - - -- Function to pick up and add entity to player's inventory - local function pickup_placed_entity(entities) - for _, ent in pairs(entities) do - if ent.valid and ent.name == entity then - -- game.print("Found valid placed entity: " .. ent.name) - - -- Collect all items that need to be inserted - local items_to_insert = {} - - -- Add entity products - --local products = ent.prototype.mineable_properties.products - --if products ~= nil then - -- for _, product in pairs(products) do - -- table.insert(items_to_insert, {name=product.name, count=product.amount}) - -- end - --end - - -- Add chest contents if applicable - if ent.get_inventory(defines.inventory.chest) then - for name, count in pairs(ent.get_inventory(defines.inventory.chest).get_contents()) do - table.insert(items_to_insert, {name=name, count=count}) - end - end - - -- Add transport belt contents if applicable - if ent.type == "transport-belt" then - -- Check line 1 - local line1 = ent.get_transport_line(1) - local contents1 = line1.get_contents() - for name, count in pairs(contents1) do - table.insert(items_to_insert, {name=name, count=count}) - end - - -- Check line 2 - local line2 = ent.get_transport_line(2) - local contents2 = line2.get_contents() - for name, count in pairs(contents2) do - table.insert(items_to_insert, {name=name, count=count}) - end - end - - -- Add the entity itself - table.insert(items_to_insert, {name=ent.name, count=1}) - - -- Check if player can receive all items - if not can_receive_items(items_to_insert) then - error("Inventory is full") - end - - -- Insert all items - for _, item in pairs(items_to_insert) do - player.insert(item) - end - - if ent.can_be_destroyed() then - -- game.print("Picked up placed "..ent.name) - pcall(ent.destroy{raise_destroy=false, do_cliff_correction=false}) - return true - end - end - end - return false - end - - -- Function to pick up items on ground - local function pickup_ground_item(ground_items) - for _, item in pairs(ground_items) do - if item.valid and item.stack and item.stack.name == entity then - -- game.print("Found valid ground item: " .. item.stack.name) - local count = item.stack.count - - -- Check if player can receive the item - if not can_receive_items({{name=entity, count=count}}) then - error("Cannot pick up " .. entity .. " - inventory is full") - end - - local inserted = player.insert{name=entity, count=count} - if inserted > 0 then - -- game.print("Picked up ground item2 " .. count .. " " .. entity) - pcall(item.destroy{raise_destroy=false}) - return true - end - end - return true - end - return false - end - - -- Find both types of entities first - local player_entities = surface.find_entities_filtered{ - name=entity, - position=position, - radius=0.707, - force="player" - } - -- game.print("Found " .. #player_entities .. " placed entities") - - local ground_items = surface.find_entities_filtered{ - name="item-on-ground", - position=position, - radius=0.707 - } - -- game.print("Found " .. #ground_items .. " ground items") - - -- Try to pick up placed entities first, if any exist - if #player_entities > 0 then - success = pickup_placed_entity(player_entities) - if success then - -- game.print("Successfully picked up placed entity") - return {} - end - end - - -- Only try ground items if we haven't succeeded with placed entities - if not success and #ground_items > 0 then - success = pickup_ground_item(ground_items) - if success then - -- game.print("Successfully picked up ground item") - return {} - end - end - - if not success then - if #player_entities == 0 and #ground_items == 0 then - error("Couldn't find "..entity.." at position ("..x..", "..y..") to pick up.") - else - error("Could not pick up "..entity) - end - end - - return {} -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity.lua deleted file mode 100644 index a954efca5..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity.lua +++ /dev/null @@ -1,348 +0,0 @@ --- Helper to convert surface direction to entity direction -local function surface_to_entity_direction(surface_dir) - -- In Factorio, offshore pumps face opposite to placement direction - local direction_map = { - [defines.direction.north] = defines.direction.north, -- 0 -> 4 - [defines.direction.east] = defines.direction.east, -- 2 -> 6 - [defines.direction.south] = defines.direction.south, -- 4 -> 0 - [defines.direction.west] = defines.direction.west -- 6 -> 2 - } - return direction_map[surface_dir] -end - -local function find_offshore_pump_position(player, center_pos) - local max_radius = 20 - local search_positions = { - {dx = 0, dy = 1, dir = defines.direction.north}, - {dx = 1, dy = 0, dir = defines.direction.west}, - {dx = 0, dy = -1, dir = defines.direction.south}, - {dx = -1, dy = 0, dir = defines.direction.east} - } - - for radius = 1, max_radius do - for y = -radius, radius do - for x = -radius, radius do - if math.abs(x) == radius or math.abs(y) == radius then - local check_pos = { - x = center_pos.x + x, - y = center_pos.y + y - } - - -- Check if position is already occupied - local entities = player.surface.find_entities_filtered{ - position = check_pos, - collision_mask = "player-layer", - invert = false - } - - if #entities == 0 then - local current_tile = player.surface.get_tile(check_pos.x, check_pos.y) - - if not is_water_tile(current_tile.name) then - for _, search in ipairs(search_positions) do - local water_pos = { - x = check_pos.x + search.dx, - y = check_pos.y + search.dy - } - - -- Check for entities at water position - local water_entities = player.surface.find_entities_filtered{ - position = water_pos, - collision_mask = "water-tile", - invert = true - } - - if #water_entities == 0 then - local adjacent_tile = player.surface.get_tile(water_pos.x, water_pos.y) - - if is_water_tile(adjacent_tile.name) then - local entity_dir = surface_to_entity_direction(search.dir) - local placement = { - name = "offshore-pump", - position = check_pos, - direction = entity_dir, - force = "player" - } - - if player.surface.can_place_entity(placement) then - -- Final collision check for the exact pump dimensions - local final_check = player.surface.find_entities_filtered{ - area = {{check_pos.x - 0.5, check_pos.y - 0.5}, - {check_pos.x + 0.5, check_pos.y + 0.5}}, - collision_mask = "player-layer" - } - - if #final_check == 0 then - return { - position = check_pos, - direction = entity_dir - } - end - end - end - end - end - end - end - end - end - end - end - - return nil -end - -global.actions.place_entity = function(player_index, entity, direction, x, y, exact) - local player = global.agent_characters[player_index] - local position = {x = x, y = y} - - if not direction then - direction = 0 - end - - local entity_direction = global.utils.get_entity_direction(entity, direction) - - -- Common validation functions - local function validate_distance() - local max_distance = player.reach_distance or player.build_distance - local dx = player.position.x - x - local dy = player.position.y - y or 0 - local distance = math.sqrt(dx * dx + dy * dy) - - if distance > max_distance then - error("\"The target position is too far away to place the entity. The player position is " .. - player.position.x .. ", " .. player.position.y .. - " and the target position is " .. x .. ", " .. y .. - ". The distance is " .. string.format("%.2f", distance) .. - " and the max distance is " .. max_distance .. ". Move closer.\"") - end - end - - local function validate_entity() - if game.entity_prototypes[entity] == nil then - local name = entity:gsub(" ", "_"):gsub("-", "_") - error("\""..name .. " isn't something that exists. Did you make a typo?\"") - end - end - - local function validate_inventory() - local count = player.get_item_count(entity) - if count == 0 then - local name = entity:gsub(" ", "_"):gsub("-", "_") - error("\"No " .. name .. " in inventory.\"") - end - end - - -- Slow placement implementation - local function slow_place() - -- Set cursor ghost - player.cursor_ghost = entity - - -- Select the target position - player.update_selected_entity(position) - - -- Schedule the actual placement after delay - script.on_nth_tick(60, function(event) -- 30 ticks = 0.5 seconds - script.on_nth_tick(60, nil) -- Clear the scheduled event - - -- Verify conditions are still valid - validate_distance() - validate_inventory() - - -- Avoid entity at target position - global.utils.avoid_entity(player_index, entity, position) - - -- Perform the actual placement - local placed_entity = player.surface.create_entity{ - name = entity, - force = "player", - position = position, - direction = entity_direction, - } - - if placed_entity then - player.remove_item{name = entity, count = 1} - player.cursor_ghost = nil -- Clear the ghost - return global.utils.serialize_entity(placed_entity) - else - error("\"Failed to place entity after delay\"") - end - end) - - return { pending = true } - end - - -- Fast placement implementation (existing logic) - local function fast_place() - local entity_prototype = game.entity_prototypes[entity] - - if entity == 'offshore-pump' then - exact = false - end - - -- Check for existing entity - if exact then - local existing_entity = player.surface.find_entity(entity, position) - if existing_entity then - error("\"entity already exists at the target position " .. serpent.line(existing_entity.position) .. " - remove this before continuing.\"" ) - end - - -- Get entity prototype's collision box - local collision_box = entity_prototype.collision_box - -- Calculate the area to check for water - local check_area = { - {position.x - collision_box.left_top.x/2, position.y - collision_box.left_top.y/2}, - {position.x + collision_box.right_bottom.x/2, position.y + collision_box.right_bottom.y/2} - } - rendering.draw_circle{only_in_alt_mode=true, width = 1, color = {r = 0, g = 1, b = 0}, surface = player.surface, radius = 0.5, filled = false, target = {x=position.x, y=position.y}, time_to_live = 12000} - - -- Check each tile in the entity's footprint for water - for x = math.floor(check_area[1][1]), math.ceil(check_area[2][1]) do - for y = math.floor(check_area[1][2]), math.ceil(check_area[2][2]) do - local tile = player.surface.get_tile(x, y) - if is_water_tile(tile.name) then - error("\"Cannot place " .. entity .. " at " .. position.x .. ", " .. position.y .. - " as entity footprint overlaps water. Please try a different location\"") - end - end - end - end - global.utils.avoid_entity(player_index, entity, position, direction) - -- Use surface based validation equivalent to LuaPlayer.can_place_entity - local can_build = global.utils.can_place_entity(player, entity, position, entity_direction) - - if not can_build then - if not exact then - local new_position - local new_position = nil - local found_position = false - -- special logic for orienting offshore pumps correctly. - if entity == 'offshore-pump' then - local pos_dir = find_offshore_pump_position(player, position) - entity_direction = global.utils.get_entity_direction(entity, pos_dir['direction']/2) - new_position = pos_dir['position'] - found_position = true - else - -- Existing search logic for nearby valid position - local radius = 1 - local max_radius = 10 - - while not found_position and radius <= max_radius do - for dx = -radius, radius do - for dy = -radius, radius do - if dx == -radius or dx == radius or dy == -radius or dy == radius then - new_position = {x = position.x + dx, y = position.y + dy} - global.utils.avoid_entity(player_index, entity, position, direction) - can_build = global.utils.can_place_entity(player, entity, new_position, entity_direction) - if can_build then - found_position = true - break - end - end - end - if found_position then break end - end - radius = radius + 1 - end - end - - if found_position then - local have_built = player.surface.create_entity{ - name = entity, - force = player.force, - position = new_position, - direction = entity_direction, - } - if have_built then - player.remove_item{name = entity, count = 1} - -- game.print("Placed " .. entity .. " at " .. new_position.x .. ", " .. new_position.y) - return global.actions.get_entity(player_index, entity, new_position.x, new_position.y) - end - else - error("\"Could not find a suitable position to place " .. entity .. " near the target location.\"") - end - else - -- Clear existing entities if exact placement is required - local area = {{position.x - 0.25, position.y - 0.25}, {position.x + 0.25, position.y + 0.25}} - local entities = player.surface.find_entities_filtered{area = area, force = "player"} - if #entities ~= 0 then - if #entities == 1 then - error("\"Could not find a suitable position to place " .. entity .. " at the target location, as there is an existing object in the way\"") - else - error("\"Could not find a suitable position to place " .. entity .. " at the target location, as there are existing objects in the way\"") - end - end - end - - global.utils.avoid_entity(player_index, entity, position, direction) - - can_build = global.utils.can_place_entity(player, entity, position, entity_direction) - - if not can_build then - local entity_prototype = game.entity_prototypes[entity] - local entity_box = entity_prototype.collision_box - local entity_width = 1 - local entity_height = 1 - if direction == defines.direction.north or direction == defines.direction.south then - entity_width = math.abs(entity_box.right_bottom.x - entity_box.left_top.x) - entity_height = math.abs(entity_box.right_bottom.y - entity_box.left_top.y) - else - entity_height = math.abs(entity_box.right_bottom.x - entity_box.left_top.x) - entity_width = math.abs(entity_box.right_bottom.y - entity_box.left_top.y) - end - - rendering.draw_rectangle{ - only_in_alt_mode=true, - surface = player.surface, - left_top = {position.x - entity_width / 2, position.y - entity_height / 2}, - right_bottom = {position.x + entity_width / 2, position.y + entity_height / 2}, - filled = false, - color = {r=1, g=0, b=0, a=0.5}, - time_to_live = 60000 - } - - error("\"Cannot place " .. entity .. " at the target location - something is in the way or part of the terrain is unplaceable (water)\"") - end - end - - local have_built = player.surface.create_entity{ - name = entity, - force = player.force, - position = position, - direction = entity_direction, - } - - if have_built then - player.remove_item{name = entity, count = 1} - -- game.print("Placed " .. entity .. " at " .. position.x .. ", " .. position.y) - - -- Find and return the placed entity - local width = 0.5 - local height = 0.5 - local target_area = { - {position.x - width, position.y - height}, - {position.x + width, position.y + height} - } - local entities = player.surface.find_entities_filtered{area = target_area, name = entity} - - if #entities > 0 then - return global.utils.serialize_entity(entities[1]) - end - error("\"Could not find placed entity\"") - end - end - - -- Main execution flow - validate_distance() - validate_entity() - validate_inventory() - global.utils.avoid_entity(player_index, entity, position) - - -- Choose placement method based on global.fast setting - if global.fast then - return fast_place() - else - local result = slow_place() - return result - end -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity_next_to.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity_next_to.lua deleted file mode 100644 index 28c2f6328..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/place_entity_next_to.lua +++ /dev/null @@ -1,360 +0,0 @@ -local function validate_mining_drill_placement(surface, position, entity_name) - -- Check if the entity is a mining drill - local prototype = game.entity_prototypes[entity_name] - if prototype.type ~= "mining-drill" then - return true - end - - -- Get the mining area - local mining_area = prototype.collision_box - local area = { - {position.x + mining_area.left_top.x, position.y + mining_area.left_top.y}, - {position.x + mining_area.right_bottom.x, position.y + mining_area.right_bottom.y} - } - - -- Check for resources in the mining area - local resources = surface.find_entities_filtered({ - area = area, - type = "resource" - }) - - -- For mining drills, we need at least one valid resource - return #resources > 0 -end - - -global.actions.place_entity_next_to = function(player_index, entity, ref_x, ref_y, direction, gap) - local player = global.agent_characters[player_index] - local ref_position = {x = ref_x, y = ref_y} - - local function table_contains(tbl, element) - for _, value in ipairs(tbl) do - if value == element then - return true - end - end - return false - end - - local valid_directions = {0, 1, 2, 3} -- 0: North, 1: East, 2: South, 3: West - - if not table_contains(valid_directions, direction) then - error("Invalid direction " .. direction .. " provided. Please use 0 (north), 1 (east), 2 (south), or 3 (west).") - end - - local ref_entities = player.surface.find_entities_filtered({ - area = {{ref_x - 0.5, ref_y - 0.5}, {ref_x + 0.5, ref_y + 0.5}}, - type = {"character", "resource"}, -- Find players - invert = true - }) - local ref_entity = #ref_entities > 0 and ref_entities[1] or nil - - local function is_transport_belt(entity_name) - return entity_name == "transport-belt" or - entity_name == "fast-transport-belt" or - entity_name == "express-transport-belt" - end - - - local function calculate_position(direction, ref_pos, ref_entity, gap, is_belt, entity_to_place) - local new_pos = {x = ref_pos.x, y = ref_pos.y} - local effective_gap = gap or 0 - - -- Get reference entity's width/height based on its rotation - local ref_width, ref_height - if ref_entity then - if ref_entity.type == "inserter" then - ref_width, ref_height = 1, 1 - else - local ref_orientation = ref_entity.direction - if ref_orientation == 2 or ref_orientation == 6 then -- East or West - ref_width = ref_entity.prototype.tile_height - ref_height = ref_entity.prototype.tile_width - else -- North or South - ref_width = ref_entity.prototype.tile_width - ref_height = ref_entity.prototype.tile_height - end - end - else - ref_width = 1 - ref_height = 1 - end - - -- Check if the entity to place is an inserter - local entity_prototype = game.entity_prototypes[entity_to_place] - -- Original spacing calculation for non-inserters - local entity_width, entity_height - if direction == 1 or direction == 3 then -- East or West - entity_width = entity_prototype.tile_height - entity_height = entity_prototype.tile_width - else -- North or South - entity_width = entity_prototype.tile_width - entity_height = entity_prototype.tile_height - end - - if direction == 0 then -- North - new_pos.y = new_pos.y - (math.ceil(ref_height + entity_height)/2 + effective_gap) - elseif direction == 1 then -- East - new_pos.x = new_pos.x + (math.ceil(ref_width + entity_width)/2 + effective_gap) - elseif direction == 2 then -- South - new_pos.y = new_pos.y + (math.ceil(ref_height + entity_height)/2 + effective_gap) - else -- West - new_pos.x = new_pos.x - (math.ceil(ref_width + entity_width)/2 + effective_gap) - end - - -- Round the position to the nearest 0.5 to align with Factorio's grid - new_pos.x = math.ceil(new_pos.x * 2) / 2 - new_pos.y = math.ceil(new_pos.y * 2) / 2 - - return new_pos - end - - local function calculate_position_old(direction, ref_pos, ref_entity, gap, is_belt, entity_to_place) - local new_pos = {x = ref_pos.x, y = ref_pos.y} - local effective_gap = gap or 0 - - -- Get reference entity's width/height based on its rotation - local ref_width, ref_height - if ref_entity then - local ref_orientation = ref_entity.direction -- 0,2,4,6 = N,E,S,W - if ref_orientation == 2 or ref_orientation == 6 then -- East or West - ref_width = ref_entity.prototype.tile_height - ref_height = ref_entity.prototype.tile_width - else -- North or South - ref_width = ref_entity.prototype.tile_width - ref_height = ref_entity.prototype.tile_height - end - else - ref_width = 1 - ref_height = 1 - end - - -- Get entity to place width/height based on desired direction - local entity_prototype = game.entity_prototypes[entity_to_place] - local entity_width, entity_height - if direction == 1 or direction == 3 then -- East or West - entity_width = entity_prototype.tile_height - entity_height = entity_prototype.tile_width - else -- North or South - entity_width = entity_prototype.tile_width - entity_height = entity_prototype.tile_height - end - - -- Calculate spacing based on the relevant dimensions - if direction == 0 then -- North - new_pos.y = new_pos.y - (math.ceil(ref_height + entity_height)/2 + effective_gap) - elseif direction == 1 then -- East - new_pos.x = new_pos.x + (math.ceil(ref_width + entity_width)/2 + effective_gap) - elseif direction == 2 then -- South - new_pos.y = new_pos.y + (math.ceil(ref_height + entity_height)/2 + effective_gap) - else -- West - new_pos.x = new_pos.x - (math.ceil(ref_width + entity_width)/2 + effective_gap) - end - - -- Round the position to the nearest 0.5 to align with Factorio's grid - new_pos.x = math.ceil(new_pos.x * 2) / 2 - new_pos.y = math.ceil(new_pos.y * 2) / 2 - - return new_pos - end - - local is_belt = is_transport_belt(entity) - - local new_position = calculate_position(direction, ref_position, ref_entity, gap, is_belt, entity) - - local function player_collision(player, target_area) - local character_box = { - left_top = {x = -0.2, y = -0.2}, - right_bottom = {x = 0.2, y = 0.2} - } - local character_area = { - {player.position.x + character_box.left_top.x, player.position.y + character_box.left_top.y}, - {player.position.x + character_box.right_bottom.x, player.position.y + character_box.right_bottom.y} - } - return (character_area[1][1] < target_area[2][1] and character_area[2][1] > target_area[1][1]) and - (character_area[1][2] < target_area[2][2] and character_area[2][2] > target_area[1][2]) - end - - local nearby_entities = player.surface.find_entities_filtered({ - position = new_position, - radius = 0.5, - force = player.force - }) - - if #nearby_entities > 0 then - local colliding_entity_names = {} - for _, nearby_entity in pairs(nearby_entities) do - if nearby_entity.name ~= 'laser-beam' and nearby_entity.name ~= "character" then - table.insert(colliding_entity_names, nearby_entity.name) - end - end - if #colliding_entity_names > 0 then - local colliding_entity_name - if #colliding_entity_names == 1 then - colliding_entity_name = colliding_entity_names[1] - elseif #colliding_entity_names == 2 then - colliding_entity_name = table.concat(colliding_entity_names, " and ") - else - colliding_entity_name = table.concat(colliding_entity_names, ", ", 1, #colliding_entity_names - 1) .. ", and " .. colliding_entity_names[#colliding_entity_names] - end - error("\"A " .. colliding_entity_name .. " already exists at the new position " .. serpent.line(new_position) .. ". Consider increasing the spacing (".. gap.."), changing the direction or changing the reference position (" .. serpent.line(ref_position) .. ")\"") - end - end - - local orientation = global.utils.get_entity_direction(entity, direction) - - if ref_entity then - local prototype = game.entity_prototypes[ref_entity.name] - local collision_box = prototype.collision_box - local width = math.abs(collision_box.right_bottom.x - collision_box.left_top.x) - local height = math.abs(collision_box.right_bottom.y - collision_box.left_top.y) - - local target_area = { - {new_position.x - width / 2, new_position.y - height / 2}, - {new_position.x + width / 2, new_position.y + height / 2} - } - while player_collision(player, target_area) do - player.teleport({player.position.x + width + 1, player.position.y}) - end - end - - -- Check for player collision and move player if necessary - local entity_prototype = game.entity_prototypes[entity] - local entity_box = entity_prototype.collision_box - local entity_width = 1 - local entity_height = 1 - if orientation == defines.direction.north or orientation == defines.direction.south then - entity_width = math.abs(entity_box.right_bottom.x - entity_box.left_top.x) - entity_height = math.abs(entity_box.right_bottom.y - entity_box.left_top.y) - else - entity_height = math.abs(entity_box.right_bottom.x - entity_box.left_top.x) - entity_width = math.abs(entity_box.right_bottom.y - entity_box.left_top.y) - end - - - local target_area = { - {new_position.x - entity_width / 2, new_position.y - entity_height / 2}, - {new_position.x + entity_width / 2, new_position.y + entity_height / 2} - } - - if player_collision(player, target_area) then - local move_distance = math.max(entity_width, entity_height) + 1 - local move_direction = {x = 0, y = 0} - - if direction == 0 or direction == 2 then -- North or South - move_direction.x = 1 -- Move East - else -- East or West - move_direction.y = 1 -- Move South - end - - local new_player_position = { - x = player.position.x + move_direction.x * move_distance, - y = player.position.y + move_direction.y * move_distance - } - player.teleport(new_player_position) - end - - -- First clean up any items-on-ground at the target position - local area = {{new_position.x - entity_width / 2, new_position.y - entity_height / 2}, {new_position.x + entity_width / 2, new_position.y + entity_height / 2}} - - -- Show bounding box - rendering.draw_rectangle({ - only_in_alt_mode=true, - color = {r = 0, g = 1, b = 0}, - filled = false, - left_top = area[1], - right_bottom = area[2], - surface = player.surface, - time_to_live = 60000 - }) - rendering.draw_circle({ - only_in_alt_mode=true, - color = {r = 1, g = 0, b = 0}, - radius = 0.2, - filled = true, - target = new_position, - surface = player.surface, - time_to_live = 60000 - }) - rendering.draw_circle({ - only_in_alt_mode=true, - color = {r = 0, g = 0, b = 1}, - radius = 0.2, - filled = true, - target = ref_position, - surface = player.surface, - time_to_live = 60000 - }) - - - local items = player.surface.find_entities_filtered{ - area = area, - type = "item-on-ground" - } - for _, item in ipairs(items) do - item.destroy() - end - - global.utils.avoid_entity(player_index, entity, new_position, direction) - - local can_build = player.surface.can_place_entity({ - name = entity, - position = new_position, - direction = orientation, - force = player.force - }) - if can_build then - can_build = validate_mining_drill_placement(player.surface, new_position, entity) - if not can_build then - error("Cannot place mining drill - no resources found in mining area") - end - else - local entities = player.surface.find_entities_filtered{area = area, type = {"beam", "resource", "player", "character"}, invert=true} - local blocker_names = {} - for _, e in ipairs(entities) do - table.insert(blocker_names, e.type.."("..serpent.line(e.position)..")") - end - -- game.print(serpent.line(blocker_names)) - - local tree = player.surface.find_entities_filtered{area = area, type = {"tree"}} - for _, e in ipairs(tree) do - table.insert(blocker_names, e.name.."("..serpent.line(e.position)..")") - end - - local tiles = player.surface.find_tiles_filtered{area = area, name={"water", "deepwater", "water-green", "deepwater-green", "water-shallow", "water-mud"}} - if #tiles > 0 then - for _, e in ipairs(tiles) do - -- if e.name is not in blocker_names, we should add item - if not table_contains(blocker_names, e.name.."("..serpent.line(e.position)..")") then - table.insert(blocker_names, e.name.."("..serpent.line(e.position)..")") - end - end - end - -- game.print(serpent.line(blocker_names)) - - error("\'Cannot place entity at the position " .. serpent.line(new_position) .. " with the current direction" .. - ". Attempting to place next to - "..ref_entity.name..". There might be a collision with existing entities or this area cannot be placed on (water). Nearby entities that might be blocking the placement - " .. serpent.line(blocker_names) .. - ". Consider increasing the spacing (".. gap.."), changing the direction or changing the reference position (" .. serpent.line(ref_position) .. ")\'") - - end - - local new_entity = player.surface.create_entity({ - name = entity, - position = new_position, - force = player.force, - direction = orientation, - move_stuck_players = true, - }) - - if not new_entity then - error("Failed to create entity " .. entity .. " at position " .. serpent.line(new_position)) - end - - local item_stack = {name = entity, count = 1} - if player.get_main_inventory().can_insert(item_stack) then - player.get_main_inventory().remove(item_stack) - return global.utils.serialize_entity(new_entity) - else - error("Not enough items in inventory.") - end -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/print.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/print.lua deleted file mode 100644 index 124d2842b..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/print.lua +++ /dev/null @@ -1,4 +0,0 @@ -global.actions.print = function(message) - message = dump(message) - return '"'..message..'"' -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/rotate_entity.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/rotate_entity.lua deleted file mode 100644 index 2d0e7f1c2..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/rotate_entity.lua +++ /dev/null @@ -1,64 +0,0 @@ -global.actions.rotate_entity = function(player_index, x, y, direction, entity) - local player = global.agent_characters[player_index] - local position = {x=x, y=y} -- Round to nearest tile - local surface = player.surface - - local function table_contains(tbl, element) - for _, value in ipairs(tbl) do - if value == element then - return true - end - end - return false - end - - local closest_distance = math.huge - local closest_entity = nil - local area = {{position.x - 0.5, position.y - 0.5}, {position.x + 0.5, position.y + 0.5}} - local buildings = surface.find_entities_filtered{area = area, force = "player", name=entity} - - -- Find the closest building - for _, building in ipairs(buildings) do - if building.rotatable and building.name ~= 'character' then - local distance = ((position.x - building.position.x) ^ 2 + (position.y - building.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest_entity = building - end - end - end - - if closest_entity == nil then - error("No entity to rotate at the given coordinates.") - end - - local valid_directions = {0, 1, 2, 3} - - if not table_contains(valid_directions, direction) then - error("Invalid direction " .. direction .. " provided. Please use 0 (north), 1 (east), 2 (south), or 3 (west).") - end - - -- game.print("Direction "..direction.. ", "..closest_entity.name..", "..closest_entity.direction) - -- game.print(global.utils.get_entity_direction(closest_entity.name, closest_entity.direction)) - - -- Rotate the entity - closest_entity.direction = global.utils.get_entity_direction(closest_entity.name, direction) - - -- Ensure the entity is properly aligned to the grid - local entity_position = closest_entity.position - local aligned_position = { - x = math.floor(entity_position.x), - y = math.floor(entity_position.y) - } - -- game.print("Entity position: " .. entity_position.x .. ", " .. entity_position.y) - -- game.print("Aligned position: " .. aligned_position.x .. ", " .. aligned_position.y) - - if entity_position.x ~= aligned_position.x or entity_position.y ~= aligned_position.y then - closest_entity.teleport(aligned_position) - end - - -- game.print("Rotated " .. closest_entity.name .. " to " .. closest_entity.direction) - - local serialized = global.utils.serialize_entity(closest_entity) - return serialized -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/score.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/score.lua deleted file mode 100644 index 517b6e26f..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/score.lua +++ /dev/null @@ -1,352 +0,0 @@ -local function get_total_production_counts(production_statistics) - local produced = production_statistics.input_counts - local consumed = production_statistics.output_counts - for name, value in pairs (consumed) do - if produced[name] then - produced[name] = produced[name] - value - else - produced[name] = -value - end - end - return produced -end - -local function get_raw_resources() - local raw_resources = {} - local entities = game.entity_prototypes - for name, entity_prototype in pairs (entities) do - if entity_prototype.resource_category then - if entity_prototype.mineable_properties and entity_prototype.mineable_properties.products then - for k, product in pairs (entity_prototype.mineable_properties.products) do - raw_resources[product.name] = true - end - end - end - if entity_prototype.fluid then - raw_resources[entity_prototype.fluid.name] = true - end - end - return raw_resources -end - -local function get_product_list() - local product_list = {} - local recipes = game.recipe_prototypes - - for recipe_name, recipe_prototype in pairs (recipes) do - if recipe_prototype.allow_decomposition then - local ingredients = recipe_prototype.ingredients - local products = recipe_prototype.products - for k, product in pairs (products) do - if not product_list[product.name] then - product_list[product.name] = {} - end - local recipe_ingredients = {} - local product_amount = util.product_amount(product) - if product_amount > 0 then - for j, ingredient in pairs (ingredients) do - recipe_ingredients[ingredient.name] = ((ingredient.amount)/#products) / product_amount - end - recipe_ingredients.energy = (recipe_prototype.energy / #products) / product_amount - table.insert(product_list[product.name], recipe_ingredients) - end - end - end - end - - local items = game.item_prototypes - local entities = game.entity_prototypes - --[[Now we do some tricky stuff for space science type items]] - local rocket_silos = {} - for k, entity in pairs (entities) do - if entity.type == "rocket-silo" and entity.fixed_recipe then - local recipe = recipes[entity.fixed_recipe] - if not recipe then return end - local required_parts = entity.rocket_parts_required - local list = {} - for k, product in pairs (recipe.products) do - local product_amount = util.product_amount(product) - if product_amount > 0 then - product_amount = product_amount * required_parts - list[product.name] = product_amount - end - end - list["energy"] = recipe.energy - table.insert(rocket_silos, list) - end - end - for k, item in pairs (items) do - local launch_products = item.rocket_launch_products - if launch_products then - for k, launch_product in pairs (launch_products) do - product_list[launch_product.name] = product_list[launch_product.name] or {} - local launch_product_amount = util.product_amount(launch_product) - if launch_product_amount > 0 then - for k, silo_products in pairs (rocket_silos) do - local this_silo = {} - for product_name, product_count in pairs (silo_products) do - this_silo[product_name] = product_count / launch_product_amount - end - this_silo[item.name] = 1 / launch_product_amount - table.insert(product_list[launch_product.name], this_silo) - end - end - end - end - end - return product_list -end - -local default_seed_prices = function() - return - { - ["iron-ore"] = 3.1, - ["copper-ore"] = 3.6, - ["coal"] = 3, - ["stone"] = 2.4, - ["crude-oil"] = 0.2, - ["water"] = 1/1000, - ["steam"] = 1/1000, - ["wood"] = 3.2, - ["raw-fish"] = 100, - ["energy"] = 1, - ["uranium-ore"] = 8.2 - } -end - -local default_ingredient_exponent = function() return 2 end--1.025 end -- We use 2 as the exponent to heavily bias in favour of complex recipes. -local default_raw_resource_price = function() return 2.5 end -local default_resource_ignore = function() return {} end - -local default_param = function() - return - { - ingredient_exponent = 1.025, --[[The exponent for increase in value for each additional ingredient formula exponent^#ingredients-2]] - raw_resource_price = 2.5, --[[If a raw resource isn't given a price, it uses this price]] - resource_ignore = {} --[[This is used to account for mods removing resource generation, in which case we want the item price to be calculated from recipes.]] - } -end - -local ingredient_multiplier = function(recipe, param) - return (param.ingredient_exponent or 1) ^ (table_size(recipe) - 2) -end - -local deduce_nil_prices = function(price_list, param) - local nil_prices = {} - for name, item in pairs (game.item_prototypes) do - if not price_list[name] then - nil_prices[name] = {} - end - end - for name, item in pairs (game.fluid_prototypes) do - if not price_list[name] then - nil_prices[name] = {} - end - end - local recipes = game.recipe_prototypes - for name, recipe in pairs (recipes) do - for k, ingredient in pairs (recipe.ingredients) do - if nil_prices[ingredient.name] then - table.insert(nil_prices[ingredient.name], recipe) - end - end - end - for name, recipes in pairs (nil_prices) do - if #recipes > 0 then - local recipe_cost - local ingredient_amount - for k, recipe in pairs (recipes) do - local ingredient_value = 0 - for k, ingredient in pairs (recipe.ingredients) do - if ingredient.name == name then - ingredient_amount = ingredient.amount - else - local ingredient_price = price_list[ingredient.name] - if ingredient_price then - ingredient_value = ingredient_value + (ingredient_price * ingredient.amount) - else - ingredient_value = nil - break - end - end - end - if not ingredient_value then break end - local product_value = 0 - for k, product in pairs (recipe.products) do - local amount = util.product_amount(product) - local product_price = price_list[product.name] - if product_price then - product_value = product_value + product_price * amount - else - product_value = nil - break - end - end - if not product_value then - break - end - local reverse_price = (product_value - param.energy_addition(recipe, product_value)) / ingredient_multiplier(recipe.ingredients, param) -- Not perfect, but close enough - local this_cost = (reverse_price - ingredient_value) / ingredient_amount - if recipe_cost then - recipe_cost = math.min(recipe_cost, this_cost) - else - recipe_cost = this_cost - end - end - if recipe_cost then - price_list[name] = param.round(recipe_cost) - end - end - end -end - -local ln = math.log -local default_energy_addition = function(recipe, cost) - return ((ln(recipe.energy + 1) * (cost ^ 0.5))) -end - -local default_rounding = function(number) - return number -end - -production_score = {} - -production_score.generate_price_list = function(param) - - local param = param or {} - local price_list = param.seed_prices or default_seed_prices() - - param.ingredient_exponent = param.ingredient_exponent or default_ingredient_exponent() - param.raw_resource_price = param.raw_resource_price or default_raw_resource_price() - param.resource_ignore = param.resource_ignore or default_resource_ignore() - param.round = param.round or default_rounding - param.energy_addition = param.energy_addition or default_energy_addition - param.normalise = param.normalise or function(number) return number end - - local resource_list = get_raw_resources() - - for name, k in pairs (resource_list) do - if not price_list[name] then - price_list[name] = param.round(param.raw_resource_price) - end - end - - for k, name in pairs (param.resource_ignore or {}) do - price_list[name] = nil - end - - local product_list = get_product_list() - local get_price_recursive - get_price_recursive = function(name, current_loop, loop_force) - local price = price_list[name] - if price then return price end - price = 0 - if current_loop[name] then - if loop_force then - return param.raw_resource_price - end - return - end - current_loop[name] = true - local entry = product_list[name] - if not entry then return end - local recipe_cost - for k, recipe in pairs (entry) do - local this_recipe_cost = 0 - for ingredient_name, cost in pairs (recipe) do - if ingredient_name ~= "energy" then - local addition = get_price_recursive(ingredient_name, current_loop, loop_force) - if addition and addition > 0 then - this_recipe_cost = this_recipe_cost + (addition * cost) - else - this_recipe_cost = 0 - break - end - end - end - if this_recipe_cost > 0 then - this_recipe_cost = (this_recipe_cost * ingredient_multiplier(recipe, param)) + param.energy_addition(recipe, this_recipe_cost) - if recipe_cost then - recipe_cost = math.min(recipe_cost, this_recipe_cost) - else - recipe_cost = this_recipe_cost - end - end - end - if recipe_cost then - price = param.round(recipe_cost) - price_list[name] = price - return price - end - end - local items = game.item_prototypes - for name, item in pairs (items) do - local current_loop = {} - get_price_recursive(name, current_loop) - end - local fluids = game.fluid_prototypes - for name, fluid in pairs (fluids) do - local current_loop = {} - get_price_recursive(name, current_loop) - end - deduce_nil_prices(price_list, param) - for name, item in pairs (items) do - local current_loop = {} - get_price_recursive(name, current_loop, true) - end - for name, fluid in pairs (fluids) do - local current_loop = {} - get_price_recursive(name, current_loop, true) - end - deduce_nil_prices(price_list, param) - - for k, price in pairs (price_list) do - price_list[k] = param.normalise(price) - end - - return price_list -end - -production_score.get_production_scores = function(price_list) - local price_list = price_list or production_score.generate_price_list() - local scores = {} - for k, force in pairs (game.forces) do - local score = 0 - for name, value in pairs (get_total_production_counts(force.item_production_statistics)) do - local price = price_list[name] - if price then - score = score + (price * value) - end - end - for name, value in pairs (get_total_production_counts(force.fluid_production_statistics)) do - local price = price_list[name] - if price then - score = score + (price * value) - end - end - scores[force.name] = math.floor(score) - end - return scores -end - -global.goal = nil -global.actions.score = function() - local production_score = production_score.get_production_scores() - production_score["player"] = production_score["player"] - global.initial_score["player"] - - -- Try to get goal description from first player if available, otherwise skip - local goal_description = nil - if #game.players > 0 and game.players[1] and game.players[1].valid then - goal_description = game.players[1].get_goal_description() - end - - if global.goal ~= goal_description then - global.goal = goal_description - --game.print(goal_description) - --if goal_description ~= nil and #goal_description > 1 then - --production_score["goal"] = goal_description[1]:gsub("-", "_") - --end - return dump(production_score) - end - return dump(production_score) -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/send_message.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/send_message.lua deleted file mode 100644 index b6f2de2f6..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/send_message.lua +++ /dev/null @@ -1 +0,0 @@ --- Left blank intentionally - this functionality is handled by the A2A protocol \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_entity_recipe.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_entity_recipe.lua deleted file mode 100644 index d57a87849..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_entity_recipe.lua +++ /dev/null @@ -1,48 +0,0 @@ -global.actions.set_entity_recipe = function(player_index, recipe_name, x, y) - local player = global.agent_characters[player_index] - local surface = player.surface - --local position = player.position - local closest_distance = math.huge - local closest_building = nil - - -- Iterate through all crafting machines in the area - local area = {{x - 1, y - 1}, {x + 1, y + 1}} - local buildings = surface.find_entities_filtered{area = area, type = {"assembling-machine", "inserter"}} - - -- Find the closest building - for _, building in ipairs(buildings) do - local distance = ((x - building.position.x) ^ 2 + (y - building.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest_building = building - end - end - - -- If a closest building is found, handle based on its type - if closest_building then - local serialized - - -- Handle different entity types - if closest_building.type == "inserter" then - -- For filter inserters, set the filter - if closest_building.name == "filter-inserter" or closest_building.name == "stack-filter-inserter" then - closest_building.set_filter(1, recipe_name) -- Set first filter slot - serialized = global.utils.serialize_entity(closest_building) - end - else - -- Original assembling machine logic - local recipe = player.force.recipes[recipe_name] - if recipe and closest_building.get_recipe() ~= recipe then - closest_building.set_recipe(recipe_name) - end - serialized = global.utils.serialize_entity(closest_building) - end - - local entity_json = game.table_to_json(serialized) - -- game.print(entity_json) - return serialized - else - error("No building found that could have its recipe or filter set.") - end -end - diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_research.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_research.lua deleted file mode 100644 index cdb262e7d..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/set_research.lua +++ /dev/null @@ -1,74 +0,0 @@ -global.actions.set_research = function(player_index, technology_name) - local player = global.agent_characters[player_index] - local force = player.force - - -- Helper function to check if technology can be researched - local function can_research_technology(force, tech_name) - local tech = force.technologies[tech_name] - - if not tech then - return false, "technology doesn't exist" - end - - if tech.researched then - return false, "technology is already researched" - end - - if not tech.enabled then - return false, "technology is not enabled" - end - - -- Check prerequisites - for _, prerequisite in pairs(tech.prerequisites) do - if not prerequisite.researched then - return false, "missing prerequisite - " .. prerequisite.name - end - end - - -- Check if we have the required research ingredients - for _, ingredient in pairs(tech.research_unit_ingredients) do - if not force.recipes[ingredient.name].enabled then - return false, "missing required science pack recipe - " .. ingredient.name - end - end - - return true, tech - end - - -- Main logic - local can_research, result = can_research_technology(force, technology_name) - - if not can_research then - error(string.format("\"Cannot research %s because %s\"", - technology_name, result)) - end - if force.current_research then - force.set_saved_technology_progress(force.current_research.name, force.research_progress) - end - -- Cancel current research if any - --force.cancel_current_research() - - -- Set new research using add_research - local success = force.add_research(technology_name) - if not success then - error(string.format("\"Failed to start research for %s\"", technology_name)) - end - - -- Collect and return the research ingredients - local ingredients = {} - local tech = result -- Using the technology object from can_research_technology - - -- Get the count of research units needed - local units_required = tech.research_unit_count - - -- Collect all ingredients and their counts - for _, ingredient in pairs(tech.research_unit_ingredients) do - table.insert(ingredients, { - name = "\""..ingredient.name.."\"", - count = ingredient.amount * units_required, - type = ingredient.type - }) - end - - return ingredients -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/sleep.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/sleep.lua deleted file mode 100644 index 5373f331a..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/tools/agent/sleep.lua +++ /dev/null @@ -1,6 +0,0 @@ -global.actions.sleep = function(ticks_elapsed) - if ticks_elapsed > 0 then - global.elapsed_ticks = global.elapsed_ticks + ticks_elapsed - end - return game.tick -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/alerts.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/alerts.lua deleted file mode 100644 index eb5e6228b..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/alerts.lua +++ /dev/null @@ -1,441 +0,0 @@ -global.alerts = {} - --- Define a function to check if the transport belt is blocked -function is_transport_belt_blocked(entity) - if entity.type == "transport-belt" then - local line_1 = entity.get_transport_line(1) - local line_2 = entity.get_transport_line(2) - - local direction_vector = { - [0] = {x = 0, y = -1}, -- North - [2] = {x = 1, y = 0}, -- East - [4] = {x = 0, y = 1}, -- South - [6] = {x = -1, y = 0}, -- West - } - - local dx = direction_vector[entity.direction].x - local dy = direction_vector[entity.direction].y - local next_position = {x = entity.position.x + dx, y = entity.position.y + dy} - local next_entities = entity.surface.find_entities_filtered { - area = {{next_position.x - 0.5, next_position.y - 0.5}, {next_position.x + 0.5, next_position.y + 0.5}}, - } - - local has_sink = false - for _, next_entity in ipairs(next_entities) do - if next_entity.type == "transport-belt" or next_entity.type == "splitter" or next_entity.type == "loader" or (next_entity.type == "assembling-machine" and next_entity.get_recipe()) then - has_sink = true - break - end - end - - if not has_sink then - local line_1_moving = line_1 and line_1.get_contents() and not line_1.can_insert_at_back() - local line_2_moving = line_2 and line_2.get_contents() and not line_2.can_insert_at_back() - - if line_1_moving or line_2_moving then - return true - end - end - end - return false -end - --- Define a function to check if an entity is full -function is_full(entity) - -- Map inventory types to string names - local inventory_names = { - [defines.inventory.rocket_silo_rocket] = "rocket", - [defines.inventory.rocket_silo_input] = "rocket input", - [defines.inventory.rocket_silo_output] = "rocket output", - [defines.inventory.rocket_silo_modules] = "rocket modules", - [defines.inventory.fuel] = "fuel source", - [defines.inventory.burnt_result] = "burnt result", - [defines.inventory.assembling_machine_input] = "assembling machine input", - [defines.inventory.assembling_machine_output] = "assembling machine output", - [defines.inventory.chest] = "chest", - [defines.inventory.furnace_source] = "furnace source", - [defines.inventory.furnace_result] = "furnace result", - } - - local full_inventories = {} - - -- Iterate over all possible inventory types - for inventory_id, inventory_name in pairs(inventory_names) do - local inventory = entity.get_inventory(inventory_id) - -- Check if the entity has this type of inventory and if it's full - if inventory and #inventory > 0 then - for i = 1, #inventory do - -- Check if the slot is completely filled - local stack = inventory[i] - if not stack.valid_for_read or stack.count < stack.prototype.stack_size then -- Slot is not completely filled - goto continue - end - end - - -- Determine a more specific message depending on the entity type - if entity.type == "furnace" then - if inventory_id == defines.inventory.fuel then - inventory_name = "furnace fuel source" - elseif inventory_id == defines.inventory.furnace_source then - inventory_name = "furnace source" - elseif inventory_id == defines.inventory.furnace_result then - inventory_name = "furnace result" - end - elseif entity.type == "assembling-machine" then - if inventory_id == defines.inventory.assembling_machine_input then - inventory_name = "assembler input" - elseif inventory_id == defines.inventory.assembling_machine_output then - inventory_name = "assembler output" - end - elseif entity.type == "rocket-silo" then - if inventory_id == defines.inventory.rocket_silo_input then - inventory_name = "rocket silo input" - elseif inventory_id == defines.inventory.rocket_silo_output then - inventory_name = "rocket silo output" - elseif inventory_id == defines.inventory.rocket_silo_modules then - inventory_name = "rocket silo modules" - elseif inventory_id == defines.inventory.rocket then - inventory_name = "rocket" - end - elseif entity.type == "mining-drill" then - if inventory_id == defines.inventory.chest then - inventory_name = "fuel source" - end - elseif entity.type == "chest" then - if inventory_id == defines.inventory.chest then - inventory_name = "chest storage" - end - end - - table.insert(full_inventories, inventory_name .. " is full") -- Add the name of the full inventory to the list - - ::continue:: - end - end - - return full_inventories -end - -function can_mine(entity) - if entity.type == "mining-drill" then - -- Check if there's no resource under the drill - if entity.mining_target == nil then - return false - end - end - return true -end --- Define a function to check if the entity is a burner and has fuel -function has_fuel(entity) - if entity.burner then - if not entity.burner.currently_burning then - local fuel_inventory = entity.get_inventory(defines.inventory.fuel) - for item_name, item_count in pairs(fuel_inventory.get_contents()) do - local fuel_value = game.item_prototypes[item_name].fuel_value - if fuel_value and fuel_value > 0 then - return true - end - end - return false - else - return true - end - end - return true -end - --- Define a function to check if the entity (furnace) has ingredients -function has_ingredients(entity) - if entity.type == "furnace" and entity.get_recipe() == nil then - return false - end - return true -end --- Define a function to check if the entity is a drill and has space to output ---function has_output_space(entity) --- if entity.type == "mining-drill" and entity.mining_target then --- local output_inventory = entity.get_output_inventory() --- if output_inventory and output_inventory.is_full() then --- return false --- end --- end --- if entity.type == "mining-drill" then --- local resource = entity.surface.find_entities_filtered{position = entity.position, type = "resource"}[1] --- local output_inventory = entity.get_output_inventory() --- local drop_position = entity.drop_position --- local items_on_ground = entity.surface.find_entities_filtered{area = {{drop_position.x - 0.5, drop_position.y - 0.5}, {drop_position.x + 0.5, drop_position.y + 0.5}}, type = "item-entity"} --- --- if #items_on_ground >= 1 or (resource and output_inventory and not output_inventory.is_empty() and output_inventory and output_inventory.can_insert(resource.prototype.mined_item) == false) then --- return false --- end --- end --- return true ---end - -function has_output_space(entity) - if entity.status == defines.entity_status.waiting_for_space_in_destination then - return false - end - if entity.type == "mining-drill" and entity.mining_target then - local output_inventory = entity.get_output_inventory() - if output_inventory and output_inventory.is_full() then - return false - end - - local drop_position = entity.drop_position - local items_on_ground = entity.surface.find_entities_filtered{ - area = {{drop_position.x - 0.5, drop_position.y - 0.5}, {drop_position.x + 0.5, drop_position.y + 0.5}}, - type = "item-entity" - } - - local destination_entity = entity.surface.find_entities_filtered{ - position = drop_position, - type = {"container", "transport-belt", "underground-belt", "splitter"} - }[1] - - if #items_on_ground >= 1 then - return false - elseif destination_entity then - if destination_entity.type == "container" then - local chest_inventory = destination_entity.get_inventory(defines.inventory.chest) - return not chest_inventory.is_full() - else - -- For belts, undergrounds, and splitters, assume there's space - return true - end - else - -- No destination entity, check if output inventory is not empty and can't insert more - local resource = entity.mining_target - return not (output_inventory and not output_inventory.is_empty() and - output_inventory.can_insert({name = resource.name, count = 1}) == false) - end - end - return true -end - ---function has_electricity(entity) --- if entity.prototype.electric_energy_source_prototype then --- if entity.electric_drain <= 0 then --- return false --- end --- end --- return true ---end - -function has_electricity(entity) - if entity.prototype.electric_energy_source_prototype then - -- Check if the entity is connected to an electric network - if entity.electric_network_id then - -- Check if the entity is receiving power - local energy_usage = entity.energy / entity.electric_buffer_size - return energy_usage > 0 - else - -- Entity is not connected to an electric network - return false - end - end - -- Entity doesn't require electricity - return true -end - --- Define a function to check if the entity (boiler) has necessary input liquid -function has_input_liquid(entity) - if entity.type == "boiler" then - local fluid_box = entity.fluidbox[1] - if fluid_box == nil or fluid_box.amount <= 0 then - return false - end - end - -- If the entity is a steam engine, check if it has water - if entity.type == "generator" then - local fluid_box = entity.fluidbox[1] - if fluid_box == nil or fluid_box.amount <= 0 then - return false - end - end - return true -end - -function is_inserter_waiting_for_source(entity) - if entity.type == "inserter" then - local pickup_target = entity.pickup_target - if pickup_target and pickup_target.valid then - local pickup_inventory = pickup_target.get_inventory(defines.inventory.chest) - if pickup_inventory and pickup_inventory.is_empty() then - return true - end - end - end - return false -end - - -function get_issues(entity) - local issues = {} - - if not can_mine(entity) then - table.insert(issues, "\'nothing to mine\'") - end - if not has_fuel(entity) then - table.insert(issues, "\'out of fuel\'") - end - if not has_ingredients(entity) then - table.insert(issues, "\'no ingredients to smelt\'") - end - if is_inserter_waiting_for_source(entity) then - table.insert(issues, "\'inserter waiting for source items\'") - end - - local full_inventories = is_full(entity) - for _, full_inventory in ipairs(full_inventories) do - table.insert(issues, "\'"..full_inventory.."\'") - end - - --if not has_output_space(entity) then - -- if entity.drop_position ~= nil then - -- local rounded_x = round_to_half(entity.drop_position.x) - -- local rounded_y = round_to_half(entity.drop_position.y) - -- - -- table.insert(issues, "\'waiting for space in destination as the output is full. Place a sink object at (" .. entity.drop_position.x .. ", ".. entity.drop_position.y .. ") to unblock.\'") - -- else - -- table.insert(issues, "\'waiting for space in destination\'") - -- end - --end - if not has_output_space(entity) then - if entity.drop_position then - local rounded_x = round_to_half(entity.drop_position.x) - local rounded_y = round_to_half(entity.drop_position.y) - - local destination_entity = entity.surface.find_entities_filtered{ - position = entity.drop_position, - type = {"container", "transport-belt", "underground-belt", "splitter", "furnace"} - }[1] - - if destination_entity then - if destination_entity.type == "container" then - table.insert(issues, "\'chest at drop position is full. Empty the chest at (" .. rounded_x .. ", " .. rounded_y .. ") to continue mining.\'") - elseif destination_entity.type == "furnace" then - table.insert(issues, "\'furnace belt at drop position is blocked.\'") - else - table.insert(issues, "\'transport belt at drop position is blocked. Clear the belt at (" .. rounded_x .. ", " .. rounded_y .. ") to continue mining.\'") - end - else - table.insert(issues, "\'output blocked by item on the ground. There is no sink entity in place to accept the output.\'") - end - else - table.insert(issues, "\'waiting for space in destination\'") - end - end - - if not has_electricity(entity) then - if entity.electric_network_id and (entity.type ~= 'solar-panel' and entity.type ~= 'generator') then - table.insert(issues, "\'not receiving electricity\'") - else - table.insert(issues, "\'not connected to power network\'") - end - end - - local assembler_issue = lacks_assembler_resources(entity) - if assembler_issue then - table.insert(issues, assembler_issue) - end - - if is_transport_belt_blocked(entity) then - table.insert(issues, "\'transport belt blocked. Place an inserter to drain entities from it.\'") - end - - if not has_input_liquid(entity) then - table.insert(issues, "\'no input liquid\'") - end - return issues -end -function is_not_fluid(name) - local items = { "crude-oil", "petroleum-gas", "sulfuric-acid", "water", "steam", "light-oil", "heavy-oil", "lubricant" } - for _,v in pairs(items) do - if v == name then - return false - end - end - return true -end --- Define a function to check if the entity is an assembler machine and lacks resources -function lacks_assembler_resources(entity) - if entity.type == "assembling-machine" then - local recipe = entity.get_recipe() - if recipe then - local ingredients = recipe.ingredients - local input_inventory = entity.get_inventory(defines.inventory.assembling_machine_input) - local missing_resources = {} - - for _, ingredient in pairs(ingredients) do - -- don't perform a check in the case of liquids like crude-oil, water etc. - if is_not_fluid(ingredient.name) then - if game.item_prototypes[ingredient.name].type ~= "fluid" then - local available_amount = input_inventory.get_item_count(ingredient.name) - local missing_amount = ingredient.amount - available_amount - if missing_amount > 0 then - table.insert(missing_resources, ingredient.name .. " (" .. tostring(missing_amount) .. ")") - end - end - end - end - - if #missing_resources > 0 then - return "\'cannot create " .. recipe.name .. " due to missing resources: " .. table.concat(missing_resources, ",_").."\'" - - end - end - end - return false -end - --- Define a helper function to round a number to the nearest 0.5 -function round_to_half(number) - return math.floor(number * 2 + 0.5) / 2 -end - - --- Define a function to be called every tick -local function on_tick(event) - -- Run the check every 60 ticks (1 second) - if event.tick % 60 == 0 then - for _, surface in pairs(game.surfaces) do - local entities = surface.find_entities_filtered({force = "player"}) - for _, entity in pairs(entities) do - local issues = get_issues(entity) - - if #issues > 0 then - local position = entity.position - local entity_key = entity.name .. "_" .. position.x .. "_" .. position.y - local name = '"'..entity.name:gsub(" ", "_")..'"' - if not global.alerts[entity_key] then - global.alerts[entity_key] = { - position = position, - issues = issues, - entity_name = name, - tick = event.tick - } - end - end - end - end - end -end - --- Define a function to get alerts older than the number of seconds -global.get_alerts = function(seconds) - local current_tick = game.tick - local old_alerts = {} - - for key, alert in pairs(global.alerts) do - if current_tick - alert.tick > 60*seconds then - table.insert(old_alerts, alert) - global.alerts[key] = nil - end - end - - return old_alerts -end - --- Register the on_tick function to the on_tick event -script.on_event(defines.events.on_tick, on_tick) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/build_checkerboard.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/build_checkerboard.lua deleted file mode 100644 index eb9f267dc..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/build_checkerboard.lua +++ /dev/null @@ -1,4 +0,0 @@ -local player = global.agent_characters[arg1] -local surface = player.surface - -surface.build_checkerboard({{-100, -100}, {100, 100}}) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/checksum.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/checksum.lua deleted file mode 100644 index cd2c73dbe..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/checksum.lua +++ /dev/null @@ -1,15 +0,0 @@ -if not global.__lua_script_checksums then - global.__lua_script_checksums = {} -end - -global.get_lua_script_checksums = function() - return game.table_to_json(global.__lua_script_checksums) -end - -global.set_lua_script_checksum = function(name, checksum) - global.__lua_script_checksums[name] = checksum -end - -global.clear_lua_script_checksums = function() - global.__lua_script_checksums = {} -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_entities.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_entities.lua deleted file mode 100644 index ea8224337..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_entities.lua +++ /dev/null @@ -1,65 +0,0 @@ -global.actions.clear_entities = function(player_index) - local function clear_area_of_entities(player, area, force_filter) - local surface = player.surface - local entities = surface.find_entities_filtered{ - area = area, - force = force_filter, - type = { - "accumulator", "ammo-turret", "arithmetic-combinator", "artillery-turret", - "assembling-machine", "beacon", "boiler", "constant-combinator", - "container", "curved-rail", "decider-combinator", "electric-pole", - "electric-turret", "fluid-turret", "furnace", "gate", "generator", - "heat-interface", "heat-pipe", "inserter", "lab", "lamp", - "land-mine", "linked-belt", "linked-container", "loader", - "loader-1x1", "market", "mining-drill", "offshore-pump", - "pipe", "pipe-to-ground", "power-switch", "programmable-speaker", - "pump", "radar", "rail-chain-signal", "rail-signal", - "reactor", "roboport", "rocket-silo", "solar-panel", - "splitter", "storage-tank", "straight-rail", "train-stop", - "transport-belt", "underground-belt", "wall" - } - } - - for _, entity in ipairs(entities) do - if entity and entity.valid and entity ~= player then - entity.destroy() - end - end - - -- Clear dropped items separately - local dropped_items = surface.find_entities_filtered{ - area = area, - name = "item-on-ground" - } - for _, item in ipairs(dropped_items) do - if item and item.valid then - item.destroy() - end - end - end - - local function reset_character_inventory(player) - for inventory_id, inventory in pairs(defines.inventory) do - local character_inventory = player.get_inventory(inventory) - if character_inventory then - character_inventory.clear() - end - end - end - - -- Main execution - local player = global.agent_characters[player_index] - local area = { - {player.position.x - 1000, player.position.y - 1000}, - {player.position.x + 1000, player.position.y + 1000} - } - - -- Clear player force entities - clear_area_of_entities(player, area, player.force) - -- Clear neutral force entities - clear_area_of_entities(player, area, "neutral") - - reset_character_inventory(player) - player.force.reset() - return 1 -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_inventory.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_inventory.lua deleted file mode 100644 index ed66682d1..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/clear_inventory.lua +++ /dev/null @@ -1,2 +0,0 @@ -global.agent_characters[arg1].clear_items_inside() -rcon.print(1) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/connection_points.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/connection_points.lua deleted file mode 100644 index 31ece9561..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/connection_points.lua +++ /dev/null @@ -1,295 +0,0 @@ --- This is primarily used to get the connection points of entities for the purpose of blocking them during pathing. --- This is to prevent bad cases where connections are blocked by belts / pipes etc. - --- Function to get connection points for storage tanks based on their direction -global.utils.get_storage_tank_connection_points = function(entity) - local x, y = entity.position.x, entity.position.y - local connection_points = {} - - -- Storage tanks have two possible orientations: - -- 1. TopRight/BottomLeft connections (direction = 0 or direction = 2) - -- 2. TopLeft/BottomRight connections (direction = 1 or direction = 3) - - -- Note: entity.direction is in Factorio's 8-way direction system (0-7) - -- We need to handle both orientations (0/2 and 1/3) - if entity.direction == 1 or entity.direction == 2 then - -- TopRight/BottomLeft connections - table.insert(connection_points, {x = x + 1, y = y - 2}) -- Top right - Top - table.insert(connection_points, {x = x + 2, y = y - 1}) -- Top right - Right - - table.insert(connection_points, {x = x - 1, y = y + 2}) -- Bottom left - Bottom - table.insert(connection_points, {x = x - 2, y = y + 1}) -- Bottom left - Left - else - -- TopLeft/BottomRight connections - table.insert(connection_points, {x = x - 1, y = y - 2}) -- Top left - Top - table.insert(connection_points, {x = x - 2, y = y - 1}) -- Top left - Left - - table.insert(connection_points, {x = x + 1, y = y + 2}) -- Bottom right - Bottom - table.insert(connection_points, {x = x + 2, y = y + 1}) -- Bottom right - Right - - end - - return connection_points -end - -global.utils.get_chemical_plant_connection_points = function(plant) - local positions = {} - local x, y = plant.position.x, plant.position.y - - if plant.direction == defines.direction.north then - -- Input pipes - table.insert(positions, {x = x - 1, y = y + 1.5}) - table.insert(positions, {x = x + 1, y = y + 1.5}) - -- Output pipes - table.insert(positions, {x = x - 1, y = y - 1.5}) - table.insert(positions, {x = x + 1, y = y - 1.5}) - elseif plant.direction == defines.direction.south then - -- Input pipes - table.insert(positions, {x = x - 1, y = y - 1.5}) - table.insert(positions, {x = x + 1, y = y - 1.5}) - -- Output pipes - table.insert(positions, {x = x - 1, y = y + 1.5}) - table.insert(positions, {x = x + 1, y = y + 1.5}) - elseif plant.direction == defines.direction.east then - -- Input pipes - table.insert(positions, {x = x - 1.5, y = y - 1}) - table.insert(positions, {x = x - 1.5, y = y + 1}) - -- Output pipes - table.insert(positions, {x = x + 1.5, y = y - 1}) - table.insert(positions, {x = x + 1.5, y = y + 1}) - elseif plant.direction == defines.direction.west then - -- Input pipes - table.insert(positions, {x = x + 1.5, y = y - 1}) - table.insert(positions, {x = x + 1.5, y = y + 1}) - -- Output pipes - table.insert(positions, {x = x - 1.5, y = y - 1}) - table.insert(positions, {x = x - 1.5, y = y + 1}) - end - - return positions -end - - -global.utils.get_generator_connection_positions = function(entity) - local x, y = entity.position.x, entity.position.y - local orientation = entity.orientation - local entity_prototype = game.entity_prototypes[entity.name] - local dx, dy = 0, 0 - local offsetx, offsety = 0, 0 - if orientation == 0 or orientation == defines.direction.north then - dx, dy = 0, -1 - --offsetx, offsety = 0, 0 - elseif orientation == 0.25 or orientation == defines.direction.east then - dx, dy = 1, 0 - --offsetx, offsety = -0.5, 0 - elseif orientation == 0.5 or orientation == defines.direction.south then - dx, dy = 0, -1 - --offsetx, offsety = 0, -0.5 - elseif orientation == 0.75 or orientation == defines.direction.west then - dx, dy = 1, 0 - --offsetx, offsety = 0, 0 - else - -- Log detailed information about the unexpected orientation - local orientation_info = string.format( - "Numeric value: %s, Type: %s, Defines values: N=%s, E=%s, S=%s, W=%s", - tostring(orientation), - type(orientation), - tostring(defines.direction.north), - tostring(defines.direction.east), - tostring(defines.direction.south), - tostring(defines.direction.west) - ) - error(string.format( - "Unexpected orientation for entity: %s at position: %s. Orientation info: %s", - entity.name, - serpent.line(entity.position), - orientation_info - )) - end - local height = entity_prototype.tile_height/2 - local pipe_positions = { - {x = x + (height*dx) + offsetx, y = y + (height*dy) + offsety}, - {x = x - (height*dx) + offsetx, y = y - (height*dy) + offsety} - } - - return pipe_positions -end - -global.utils.get_pumpjack_connection_points = function(entity) - local x, y = entity.position.x, entity.position.y - local orientation = entity.orientation - - local dx, dy - if orientation == 0 or orientation == defines.direction.north then - dx, dy = 1, -2 - elseif orientation == 0.25 or orientation == defines.direction.east then - dx, dy = 2, -1 - elseif orientation == 0.5 or orientation == defines.direction.south then - dx, dy = -1, 2 - elseif orientation == 0.75 or orientation == defines.direction.west then - dx, dy = -2, 1 - end - - local pipe_position = {{x = x + dx, y = y + dy}} - - return pipe_position -end - -global.utils.get_boiler_connection_points = function(entity) - local x, y = entity.position.x, entity.position.y - local orientation = entity.orientation * 8 - - local dx, dy = 0, 0 - if orientation == defines.direction.north then - dx, dy = 1.5, 0.5 - local pipe_positions = { - --water_inputs = water_inputs, - --steam_output = {x = x, y = y - 1*dy} - {x = x + 1*dx, y = y + 1*dy}, - {x = x - 1*dx, y = y + 1*dy}, - {x = x, y = y - 1*dy} - } - - return pipe_positions - elseif orientation == defines.direction.south then - dx, dy = -1.5, -0.5 - local pipe_positions = { - --water_inputs = water_inputs, - --steam_output = {x = x, y = y - 1*dy} - {x = x + 1*dx, y = y + 1*dy}, - {x = x - 1*dx, y = y + 1*dy}, - {x = x, y = y - 1*dy} - } - return pipe_positions - elseif orientation == defines.direction.east then - dx, dy = 0.5, 1.5 - local pipe_positions = { - --water_inputs = water_inputs, - --steam_output = {x = x, y = y - 1*dy} - {x = x + 1*dx, y = y + 1*dy}, - {x = x + 1*dx, y = y - 1*dy}, - {x = x - 1*dx, y = y} - } - return pipe_positions - elseif orientation == defines.direction.west then - dx, dy = -0.5, -1.5 - local pipe_positions = { - --water_inputs = water_inputs, - --steam_output = {x = x, y = y - 1*dy} - {x = x + 1*dx, y = y + 1*dy}, - {x = x + 1*dx, y = y - 1*dy}, - {x = x - 1*dx, y = y} - } - - return pipe_positions - end - --local water_inputs = {} - --water_inputs[1] = {x = x + 1*dx, y = y + 1*dy} - --water_inputs[2] = {x = x - 1*dx, y = y - 1*dy} - - --local pipe_positions = { - -- --water_inputs = water_inputs, - -- --steam_output = {x = x, y = y - 1*dy} - -- {x = x + 1*dx, y = y + 1*dy}, - -- {x = x - 1*dx, y = y + 1*dy}, - -- {x = x, y = y - 1*dy} - --} - --return pipe_positions -end - -global.utils.get_offshore_pump_connection_points = function(entity) - local x, y = entity.position.x, entity.position.y - local orientation = entity.orientation * 8 - - local dx, dy - if orientation == defines.direction.north then - dx, dy = 0, 1 - elseif orientation == defines.direction.south then - dx, dy = 0, -1 - elseif orientation == defines.direction.east then - dx, dy = -1, 0 - elseif orientation == defines.direction.west then - dx, dy = 1, 0 - end - - if dy == nil then - return { {x = x, y = y - 1} } - end - - return { {x = x + dx, y = y + dy} } -end - -global.utils.get_refinery_connection_points = function(refinery) - -- Block the middle input point also - local positions = {} - local x, y = refinery.position.x, refinery.position.y - - if refinery.direction == defines.direction.north then - -- Crude oil input - table.insert(positions, {x = x+1, y = y + 3}) - --table.insert(positions, {x = x+1, y = y + 4}) -- additional clearance - table.insert(positions, {x = x-1, y = y + 3}) - --table.insert(positions, {x = x-1, y = y + 4}) -- additional clearance - - table.insert(positions, {x = x, y = y + 3}) - - -- Outputs (petroleum, light oil, heavy oil) - table.insert(positions, {x = x - 2, y = y - 3}) - --table.insert(positions, {x = x - 2, y = y - 4}) -- additional clearance - - table.insert(positions, {x = x - 1, y = y - 3}) - - table.insert(positions, {x = x, y = y - 3}) - --table.insert(positions, {x = x, y = y - 4}) -- additional clearance - - table.insert(positions, {x = x + 1, y = y - 3}) - - table.insert(positions, {x = x + 2, y = y - 3}) - --table.insert(positions, {x = x + 2, y = y - 4}) -- additional clearance - elseif refinery.direction == defines.direction.south then - -- Crude oil input - table.insert(positions, {x = x+1, y = y - 3}) - --table.insert(positions, {x = x+1, y = y - 4}) -- additional clearance - - table.insert(positions, {x = x, y = y - 3}) - table.insert(positions, {x = x-1, y = y - 3}) - --table.insert(positions, {x = x-1, y = y - 4}) -- additional clearance - - -- Outputs - table.insert(positions, {x = x - 2, y = y + 3}) - --table.insert(positions, {x = x - 2, y = y + 4}) -- additional clearance - - table.insert(positions, {x = x - 1, y = y + 3}) - table.insert(positions, {x = x, y = y + 3}) - --table.insert(positions, {x = x, y = y + 4}) -- additional clearance - table.insert(positions, {x = x + 2, y = y + 3}) - --table.insert(positions, {x = x + 2, y = y + 4}) -- additional clearance - table.insert(positions, {x = x + 1, y = y + 3}) - elseif refinery.direction == defines.direction.east then - -- Crude oil input - table.insert(positions, {x = x - 3, y = y+1}) - table.insert(positions, {x = x - 4, y = y+1}) - table.insert(positions, {x = x - 3, y = y}) - table.insert(positions, {x = x - 3, y = y-1}) - table.insert(positions, {x = x - 4, y = y-1}) - -- Outputs - table.insert(positions, {x = x + 3, y = y - 2}) - table.insert(positions, {x = x + 3, y = y - 1}) - table.insert(positions, {x = x + 3, y = y}) - table.insert(positions, {x = x + 3, y = y + 1}) - table.insert(positions, {x = x + 3, y = y + 2}) - elseif refinery.direction == defines.direction.west then - -- Crude oil input - table.insert(positions, {x = x + 3, y = y+1}) - table.insert(positions, {x = x + 3, y = y}) - table.insert(positions, {x = x + 3, y = y-1}) - -- Outputs - table.insert(positions, {x = x - 3, y = y - 2}) - table.insert(positions, {x = x - 3, y = y - 1}) - table.insert(positions, {x = x - 3, y = y}) - table.insert(positions, {x = x - 3, y = y + 1}) - table.insert(positions, {x = x - 3, y = y + 2}) - end - - return positions -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/enemies.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/enemies.lua deleted file mode 100644 index 95c64a104..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/enemies.lua +++ /dev/null @@ -1,7 +0,0 @@ -game.forces["enemy"].kill_all_units() -- Removes all biters -game.map_settings.enemy_expansion.enabled = false -- Stops biters from expanding -game.map_settings.enemy_evolution.enabled = false -- Stops biters from evolving -local surface = game.surfaces[1] -for _, entity in pairs(surface.find_entities_filtered({type="unit-spawner"})) do - entity.destroy() -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise.lua deleted file mode 100644 index 9d401830d..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise.lua +++ /dev/null @@ -1,1168 +0,0 @@ --- Initialise.lua - --- If the global actions table doesn't exist, create it -if not global.actions then - global.actions = {} -end -if not global.utils then - global.utils = {} -end - --- Initialize debug flags -if global.debug == nil then - global.debug = { - rendering = false -- Flag to toggle debug rendering of polygons and shapes - } -end - - --- Note: The debug_rendering.lua library will be loaded separately by the LuaScriptManager ---local player = game.players[arg1] -player.surface.always_day=true ---game.players[1].character_collision_mask = "not-colliding-with-itself" -player.force.character_build_distance_bonus = 100 -player.force.research_all_technologies() - - -local beam_duration = 9 -local surface=player.surface -local pp = player.position -local cnt = 0 -local directions = {'north', 'northeast', 'east', 'southeast', 'south', 'southwest', 'west', 'northwest'} - ---Initialise.lua ---local position = player.position - ---player.game_view_settings.show_controller_gui = false ---player.game_view_settings.show_minimap = false ---player.game_view_settings.show_side_menu = false ---player.game_view_settings.show_quickbar = false ---player.game_view_settings.show_shortcut_bar = false ---player.game_view_settings.show_map_view_options = false ---player.game_view_settings.show_shortcut_bar = false - -global.points_of_interest = {} -global.distances_to_nearest = {} -global.relative_points_of_interest = {} -global.interesting_entities = { - ['enemy']=true, - ['trees']=true, - ['iron-ore']=true, - ['stone']=true, - ['copper-ore']=true, - ['uranium-ore']=true, - ['crude-oil']=true, - ['water']=true, - ['coal']=true -} -global.initial_score = {} - --- Check if a player's inventory is empty -local function check_player_inventory_empty(player) - local inventory = player.get_main_inventory() - return inventory.is_empty() -end - -global.utils.get_direction = function(from_position, to_position) - local dx = to_position.x - from_position.x - local dy = to_position.y - from_position.y - local adx = math.abs(dx) - local ady = math.abs(dy) - local diagonal_threshold = 0.5 - - if adx > ady then - if dx > 0 then - return (ady / adx > diagonal_threshold) and (dy > 0 and 3 or 1) or 2 - else - return (ady / adx > diagonal_threshold) and (dy > 0 and 5 or 7) or 6 - end - else - if dy > 0 then - return (adx / ady > diagonal_threshold) and (dx > 0 and 3 or 5) or 4 - else - return (adx / ady > diagonal_threshold) and (dx > 0 and 1 or 7) or 0 - end - end -end - -global.utils.get_direction_with_diagonals = function(from_pos, to_pos) - local dx = to_pos.x - from_pos.x - local dy = to_pos.y - from_pos.y - - if dx == 0 and dy == 0 then - return nil - end - - -- Check for cardinal directions first - local cardinal_margin = 0.20 --0.25 - if math.abs(dx) < cardinal_margin then - return dy > 0 and defines.direction.south or defines.direction.north - elseif math.abs(dy) < cardinal_margin then - return dx > 0 and defines.direction.east or defines.direction.west - end - - -- Handle diagonal directions - if dx > 0 then - return dy > 0 and defines.direction.southeast or defines.direction.northeast - else - return dy > 0 and defines.direction.southwest or defines.direction.northwest - end -end - - -global.utils.get_closest_entity = function(player, position) - local closest_distance = math.huge - local closest_entity = nil - local entities = player.surface.find_entities_filtered{ - position = position, - force = "player", - radius = 3 - } - - for _, entity in ipairs(entities) do - if entity.name ~= 'character' and entity.name ~= 'laser-beam' then - local distance = ((position.x - entity.position.x) ^ 2 + (position.y - entity.position.y) ^ 2) ^ 0.5 - if distance < closest_distance then - closest_distance = distance - closest_entity = entity - end - end - end - - return closest_entity -end - -global.utils.is_position_blocked = function(player, position) - rendering.draw_circle{only_in_alt_mode=true, width = 0.5, color = {r = 1, g = 0, b = 1}, surface = player.surface, radius = 0.5, filled = false, target = position, time_to_live = 60000} - - local can_place = player.surface.can_place_entity{ - name = "simple-entity-with-owner", - position = position, - } - return not can_place -end - - -global.utils.calculate_movement_ticks = function(player, from_pos, to_pos) - -- Calculate distance between points - local dx = to_pos.x - from_pos.x - local dy = to_pos.y - from_pos.y - local distance = math.sqrt(dx * dx + dy * dy) - - -- Get player's walking speed (tiles per tick) - -- Character base speed is 0.15 tiles/tick - local walking_speed = player.character_running_speed - if not walking_speed or walking_speed == 0 then - walking_speed = 0.15 -- Default walking speed - end - - -- Calculate ticks needed for movement - return math.ceil(distance / walking_speed) -end - --- Wrapper around LuaSurface.can_place_entity that replicates all checks LuaPlayer.can_place_entity performs. --- This allows our code to validate placement without relying on an actual LuaPlayer instance. --- extra_params can be provided by callers to pass additional flags (e.g. fast_replace) if needed. -global.utils.can_place_entity = function(player, entity_name, position, direction, extra_params) - local params = extra_params or {} - params.name = entity_name - params.position = position - params.direction = direction - params.force = player.force - -- Use the manual build-check path so the engine applies the same rules as when a human player builds. - params.build_check_type = defines.build_check_type.manual - return player.surface.can_place_entity(params) -end - -global.actions.can_reach = function(player, x, y) - local dx = player.position.x - x - local dy = player.position.y - y - local distance = math.sqrt(dx * dx + dy * dy) - - if distance > player.reach_distance then - return false - end - return true -end - -global.utils.get_placement_issues = function(player, entity_name, position, direction) - local reasons = {} - - -- Check if entity prototype exists - local prototype = game.entity_prototypes[entity_name] - if not prototype then - table.insert(reasons, "invalid entity name " .. entity_name) - return reasons - end - - -- Check if player has required technology - if not player.force.recipes[entity_name] then - table.insert(reasons, "required technology not researched") - end - - -- Get rotated collision box - local collision_box = prototype.collision_box - local box_width = collision_box.right_bottom.x - collision_box.left_top.x - local box_height = collision_box.right_bottom.y - collision_box.left_top.y - - local rotated_box - if direction and (direction == defines.direction.east or direction == defines.direction.west) then - -- Swap width and height for east/west orientations - rotated_box = { - left_top = {x = position.x - box_height/2, y = position.y - box_width/2}, - right_bottom = {x = position.x + box_height/2, y = position.y + box_width/2} - } - else - rotated_box = { - left_top = {x = position.x - box_width/2, y = position.y - box_height/2}, - right_bottom = {x = position.x + box_width/2, y = position.y + box_height/2} - } - end - - -- Check for collisions with existing entities using rotated box - local entities = player.surface.find_entities_filtered{area = rotated_box} - if #entities > 0 then - local colliding = {} - for _, entity in pairs(entities) do - if entity.valid and entity.name ~= 'character' then - local entity_prototype = game.entity_prototypes[entity.name] - if entity_prototype and entity_prototype.collision_mask and prototype.collision_mask then - -- Check if collision masks overlap - for mask_type in pairs(prototype.collision_mask) do - if entity_prototype.collision_mask[mask_type] then - table.insert(colliding, entity.name) - break - end - end - end - end - end - if #colliding > 0 then - table.insert(reasons, "colliding with " .. table.concat(colliding, ", ")) - end - end - - -- Check all tiles in the rotated area for collision and water - local left = math.floor(rotated_box.left_top.x) - local right = math.ceil(rotated_box.right_bottom.x) - local top = math.floor(rotated_box.left_top.y) - local bottom = math.ceil(rotated_box.right_bottom.y) - - local water_tiles = 0 - local total_tiles = 0 - local problematic_tiles = {} - - for x = left, right do - for y = top, bottom do - local tile = player.surface.get_tile(x, y) - if tile then - total_tiles = total_tiles + 1 - - -- Water checks - if tile.prototype.collision_mask["water-tile"] then - water_tiles = water_tiles + 1 - end - - -- General tile collision check - if prototype.collision_mask then - for mask_type in pairs(prototype.collision_mask) do - if mask_type ~= "water-tile" and tile.prototype.collision_mask[mask_type] then - if not problematic_tiles[tile.name] then - problematic_tiles[tile.name] = true - end - break - end - end - end - end - end - end - - -- Water placement logic - if not prototype.collision_mask["water-tile"] and water_tiles == 0 then - table.insert(reasons, "must be placed on water") - elseif prototype.collision_mask["water-tile"] and water_tiles > 0 then - table.insert(reasons, "cannot build on water") - end - - -- Add problematic tiles to reasons - for tile_name, _ in pairs(problematic_tiles) do - table.insert(reasons, "cannot build on " .. tile_name) - end - - -- Check if outside map bounds - local map_bounds = {{-1000000, -1000000}, {1000000, 1000000}} -- Adjust as needed - if rotated_box.left_top.x < map_bounds[1][1] or rotated_box.right_bottom.x > map_bounds[2][1] or - rotated_box.left_top.y < map_bounds[1][2] or rotated_box.right_bottom.y > map_bounds[2][2] then - table.insert(reasons, "position outside map bounds") - end - - -- Check if player has required items in inventory - if prototype.items_to_place_this then - for _, stack in ipairs(prototype.items_to_place_this) do - local count = stack.count or 1 - local inventory_count = player.get_item_count(stack.name) - if inventory_count < count then - table.insert(reasons, string.format("missing %d %s", count - inventory_count, stack.name)) - end - end - end - - return reasons -end - -global.utils.describe_placement_issues = function(player, entity_name, position, direction) - local issues = global.utils.get_placement_issues(player, entity_name, position, direction) - if #issues > 0 then - return "Cannot build because: " .. table.concat(issues, "; ") - end - return "" -end - -global.utils.avoid_entity = function(player_index, entity, position, direction) - local player = global.agent_characters[player_index] - local player_position = player.position - for i=0, 10 do - local can_place = player.surface.can_place_entity{ - name = entity, - force = "player", - position = position, - direction = global.utils.get_entity_direction(entity, direction) - } - if can_place then - return true - end - player.teleport({player_position.x + i, player_position.y + i}) - end - player.teleport(player_position) - return false -end - - - ----- Define a function to be called every tick ---local function on_tick(event) --- -- Run the check every 60 ticks (1 second) --- if event.tick % 60 == 0 then --- for _, player in pairs(game.connected_players) do --- if check_player_inventory_empty(player) then --- -- Perform an action or notify the player when their inventory is empty --- player.print("Your inventory is empty!") --- end --- end --- end ---end --- ----- Register the on_tick function to the on_tick event ---script.on_event(defines.events.on_tick, on_tick) - ---script.on_nth_tick(3600, function(event) --- game.take_screenshot{ --- surface=game.surfaces[1], --- position={0,0}, --- resolution={2560, 1600}, --- zoom=0.2, --- path="timelapse/" .. string.format("%06d", event.tick/event.nth_tick) .. ".jpg", --- show_entity_info=true, --- allow_in_replay=true, --- daytime=1 --- } ---end) - - -for _, ent in pairs(surface.find_entities_filtered({force="player", position=pp, radius=50})) do - if ent.name ~= "character" then - ent.destroy() - end -end - -for key, entity in pairs(surface.find_entities_filtered({force="enemy", radius=250, position=pp })) do - cnt = cnt+1 - entity.destroy() - end - -global.crafting_queue = {} - -script.on_event(defines.events.on_tick, function(event) - -- Iterate over the crafting queue and update the remaining ticks - for i, task in ipairs(global.crafting_queue) do - task.remaining_ticks = task.remaining_ticks - 1 - - -- If the crafting is finished, consume the ingredients, insert the crafted entity, and remove the task from the queue - if task.remaining_ticks <= 0 then - for _, ingredient in pairs(task.recipe.ingredients) do - task.player.remove_item({name = ingredient.name, count = ingredient.amount * task.count}) - end - task.player.insert({name = task.entity_name, count = task.count}) - table.remove(global.crafting_queue, i) - end - end -end) - -function abort(message) - local msg = tostring(message):gsub(" ", "_") - rcon.print(msg) -end - -function create_beam_bounding_box (player, surface, direction, top_left, bottom_right) - local bottom_left = {x=top_left.x, y=bottom_right.y} - local top_right = {x=bottom_right.x, y=top_left.y} - local direction = 0 - surface.create_entity{name='laser-beam', position=player.position, source_position=top_left, target_position=top_right, duration=beam_duration, direction=direction, force='neutral', player=player} - surface.create_entity{name='laser-beam', position=player.position, source_position=top_right, target_position=bottom_right, duration=beam_duration, direction=direction, force='neutral', player=player} - surface.create_entity{name='laser-beam', position=player.position, source_position=bottom_right, target_position=bottom_left, duration=beam_duration, direction=direction, force='neutral', player=player} - surface.create_entity{name='laser-beam', position=player.position, source_position=bottom_left, target_position=top_left, duration=beam_duration, direction=direction, force='neutral', player=player} - surface.create_entity{name='laser-beam', position=player.position, source_position=player.position, duration=beam_duration, target_position={x=player.position.x, y=player.position.y+0.1}, direction=direction, force='neutral', player=player} -end - -function create_arrow_with_direction(player, direction, position) - local beam_length = 0.75 - local arrow_width = 0.5 - - -- Calculate the end position of the main beam - local end_position = {x = position.x, y = position.y} - local dx, dy = 0, 0 - - if direction == 0 then -- North - dy = -beam_length - elseif direction == 2 then -- East - dx = beam_length - elseif direction == 4 then -- South - dy = beam_length - elseif direction == 6 then -- West - dx = -beam_length - elseif direction == 1 then -- Northeast - dx, dy = beam_length * 0.7071, -beam_length * 0.7071 - elseif direction == 3 then -- Southeast - dx, dy = beam_length * 0.7071, beam_length * 0.7071 - elseif direction == 5 then -- Southwest - dx, dy = -beam_length * 0.7071, beam_length * 0.7071 - elseif direction == 7 then -- Northwest - dx, dy = -beam_length * 0.7071, -beam_length * 0.7071 - end - - end_position.x = position.x + dx - end_position.y = position.y + dy - - -- Create the main beam - player.surface.create_entity{ - name = 'laser-beam', - position = position, - source_position = position, - target_position = end_position, - duration = 6000, - force = 'neutral', - player = player - } - - -- Calculate and create the two side beams for the arrowhead - local perpendicular_dx = -dy * arrow_width - local perpendicular_dy = dx * arrow_width - - local arrow_left = { - x = end_position.x + perpendicular_dx - dx * 0.3, - y = end_position.y + perpendicular_dy - dy * 0.3 - } - local arrow_right = { - x = end_position.x - perpendicular_dx - dx * 0.3, - y = end_position.y - perpendicular_dy - dy * 0.3 - } - - player.surface.create_entity{ - name = 'laser-beam', - position = end_position, - source_position = end_position, - target_position = arrow_left, - duration = 100000, - force = 'neutral', - player = player - } - - player.surface.create_entity{ - name = 'laser-beam', - position = end_position, - source_position = end_position, - target_position = arrow_right, - duration = 100000, - force = 'neutral', - player = player - } -end - -function create_beam_point_with_direction (player, direction, position) - -- Calculate the end position of the beam based on the direction. - local end_position = {x=position.x, y=position.y} - local length = 0.5 - if direction == defines.direction.north then - end_position.y = end_position.y - length - elseif direction == defines.direction.south then - end_position.y = end_position.y + length - elseif direction == defines.direction.west then - end_position.x = end_position.x - length - elseif direction == defines.direction.east then - end_position.x = end_position.x + length - elseif direction == defines.direction.northeast then - end_position.x = end_position.x + length - end_position.y = end_position.y - length - elseif direction == defines.direction.southeast then - end_position.x = end_position.x + length - end_position.y = end_position.y + length - elseif direction == defines.direction.southwest then - end_position.x = end_position.x - length - end_position.y = end_position.y + length - elseif direction == defines.direction.northwest then - end_position.x = end_position.x - length - end_position.y = end_position.y - length - end - - -- Create the beam entity. - player.surface.create_entity{ - name='laser-beam', - position=position, - source_position=position, - target_position=end_position, - duration=60000, - direction=direction, - force='neutral', - player=player - } -end -function create_beam_point (player, position) - -- Create beams in all four cardinal directions. - create_beam_point_with_direction(player, defines.direction.north, position) - create_beam_point_with_direction(player, defines.direction.south, position) - create_beam_point_with_direction(player, defines.direction.west, position) - create_beam_point_with_direction(player, defines.direction.east, position) -end - - -function observe_points_of_interest (surface, player, search_radius) - local enemy = surface.find_nearest_enemy{position=player.position, max_distance=search_radius} - if enemy ~= nil then - global.points_of_interest['enemy'] = enemy.position - end - - for k, v in pairs(global.points_of_interest) do - global.relative_points_of_interest[k] = { - x=v.x-player.position.x, - y=v.y-player.position.y - } - end - - rcon.print(1) - return dump(global.relative_points_of_interest) -end - -function find_passable_tiles(player, localBoundingBox) - -- Generate a 100x100 bounding box centered on the player - local origin = player.position - local offset = localBoundingBox / 2 - - local left = origin.x - offset - local right = origin.x + offset - local top = origin.y - offset - local bottom = origin.y + offset - - local bounding_box = { - left_top = {x = left, y = top}, - right_bottom = {x = right, y = bottom} - } - local impassable_tiles = {} - for x = left, right do - for y = top, bottom do - local tile = player.surface.get_tile(x, y) - local is_impassable = tile.prototype.collision_mask["player-layer"] - - if is_impassable then - --(x - xmin) * localBoundingBox + (y - ymin) - local relative_x, relative_y = x + offset, y + offset - --local index = coords_to_index(relative_x, relative_y) - local index = relative_y * localBoundingBox + relative_x - if not impassable_tiles[index] then - impassable_tiles[index] = 100 - end - end - end - end - - -- Find all entities within the bounding box - local entities = player.surface.find_entities_filtered{area = {{left, top}, {right, bottom}}, force = player.force} - - for _, entity in ipairs(entities) do - local collision_box = entity.prototype.collision_box - local is_passable = (collision_box.left_top.x == 0 and collision_box.left_top.y == 0 and - collision_box.right_bottom.x == 0 and collision_box.right_bottom.y == 0) - if not is_passable then -- Change this condition to check for impassable entities - local x, y = math.floor(entity.position.x), math.floor(entity.position.y) - local relative_x, relative_y = x - left, y - top - local index = relative_y * localBoundingBox + relative_x - - if not impassable_tiles[index] then - impassable_tiles[index] = 99--{x = relative_x, y = relative_y, entity = entity} - end - end - end - - -- Convert the X, Y coordinates of each entity into coordinates relative to the origin - local relative_entities = {} - for _, entity in ipairs(entities) do - table.insert(relative_entities, {x = left, y = top, entity = entity}) - end - - -- Create a 1D sparse index of the x, y coordinate and entity, starting from the top left of the bounding box - local sparse_index = {} - for _, relative_entity in ipairs(relative_entities) do - -- Ensure the coordinates are integers by rounding them - local index_x = math.floor(relative_entity.x + offset) - local index_y = math.floor(relative_entity.y + offset) - - local index = index_y * localBoundingBox + index_x - --impassable_tiles[index] = 99 - end - - rcon.print(1) - return impassable_tiles -end - -function get_locality(position, surface, localBoundingBox) - local origin = position - local offset = localBoundingBox / 2 - - local left = origin.x - offset - local right = origin.x + offset - local top = origin.y - offset - local bottom = origin.y + offset - - local bounding_box = { - left_top = {x = left, y = top}, - right_bottom = {x = right, y = bottom} - } - - local entities = surface.find_entities(bounding_box) - - local relative_entities = {} - for _, entity in ipairs(entities) do - local relative_x = entity.position.x - origin.x - local relative_y = entity.position.y - origin.y - table.insert(relative_entities, {x = relative_x, y = relative_y, entity = entity}) - end - - local sparse_index = {} - for _, relative_entity in ipairs(relative_entities) do - local index_x = math.floor(relative_entity.x + offset) - local index_y = math.floor(relative_entity.y + offset) - - local index = index_y * localBoundingBox + index_x - local name = relative_entity.entity.name - sparse_index[index] = name:gsub("-", "_") - end - - -- Find water tiles - local water_tiles = surface.find_tiles_filtered{area=bounding_box, name={"water", "deepwater"}} - for _, tile in ipairs(water_tiles) do - local relative_x = tile.position.x - origin.x - local relative_y = tile.position.y - origin.y - - local index_x = math.floor(relative_x + offset) - local index_y = math.floor(relative_y + offset) - - local index = index_y * localBoundingBox + index_x - local name = "water" - sparse_index[index] = name:gsub("-", "_") - end - - rcon.print(1) - return sparse_index -end - - -function has_value(tbl, val) - for _, value in ipairs(tbl) do - if value == val then - return true - end - end - return false -end - -function get_local_environment (position, surface, localBoundingBox, field_x, field_y, debug) - - local top = position.y-localBoundingBox/2 - local left = position.x-localBoundingBox/2 - local bottom = position.y+localBoundingBox/2 - local right = position.x+localBoundingBox/2 - - if field_x > 0 then - right = right + field_x - left = right - field_x - elseif field_x < 0 then - left = left + field_x - right = left - field_x - end - - if field_y < 0 then - top = top + field_y - bottom = top-field_y - elseif field_y > 0 then - bottom = bottom + field_y - top = bottom-field_y - end - - bounding_box_top_left = {x=left, y=top} - bounding_box_bottom_right = {x=right, y=bottom} - - if debug then - create_beam_bounding_box(player, surface, direction, bounding_box_top_left, bounding_box_bottom_right) - entities = surface.find_entities_filtered{name="laser-beam", invert=true, area={bounding_box_top_left, bounding_box_bottom_right}} - else - entities = surface.find_entities({bounding_box_top_left, bounding_box_bottom_right}) - end - - mt = {} -- create the matrix - for i,entity in pairs(entities) do - - local x_pos = left-entity.position.x - local y_pos = top-entity.position.y - local name = entity.name - - mt[math.floor(x_pos*(top-bottom) + y_pos)] = name:gsub("-", "_") - set_points_of_interest(player, name, 1, x_pos, y_pos) - - end - - rcon.print(1) - return mt -end - -function observe_statistics (player) - performance = { - item = { - input_counts=player.force.item_production_statistics.input_counts, - output_counts=player.force.item_production_statistics.output_counts - }, - fluid = { - input_counts=player.force.fluid_production_statistics.input_counts, - output_counts=player.force.fluid_production_statistics.output_counts - }, - kills = { - input_counts=player.force.kill_count_statistics.input_counts, - output_counts=player.force.kill_count_statistics.output_counts - }, - built = { - input_counts=player.force.entity_build_count_statistics.input_counts, - output_counts=player.force.entity_build_count_statistics.output_counts - } - } - return performance -end - -function clean_nils(t) - local ans = {} - for _,v in pairs(t) do - ans[ #ans+1 ] = v - end - return ans -end - -function observe_chunk(player, surface, chunk_x, chunk_y) - counts = {} - key = chunk_x .. ", " ..chunk_y - - chunk_area = {{chunk_x*32, chunk_y*32}, {(chunk_x+1)*32, (chunk_y+1)*32}} - - counts['all'] = surface.count_entities_filtered{area=chunk_area} - - -- Environment - counts['trees'] = surface.count_entities_filtered{area=chunk_area, type = "tree"} - counts['water'] = surface.count_tiles_filtered{area=chunk_area, name = 'water'} - counts['pollution'] = surface.get_pollution({chunk_x*32,chunk_y*32}) - - -- Forces - counts['factory'] = surface.count_entities_filtered{area=chunk_area, force = "player"} - counts['enemy'] = surface.count_entities_filtered{area=chunk_area, force = "enemy"} - - -- Resources - counts['iron-ore'] = surface.count_entities_filtered{area=chunk_area, name = "iron-ore"} - counts['coal'] = surface.count_entities_filtered{area=chunk_area, name = "coal"} - counts['copper-ore'] = surface.count_entities_filtered{area=chunk_area, name = "copper-ore"} - counts['uranium-ore'] = surface.count_entities_filtered{area=chunk_area, name = "uranium-ore"} - counts['stone'] = surface.count_entities_filtered{area=chunk_area, name = "stone"} - counts['crude-oil'] = surface.count_entities_filtered{area=chunk_area, name = "crude-oil"} - - for field_name, count in pairs(counts) do - --if count ~= 0 then - set_points_of_interest(player, field_name, count, chunk_x, chunk_y) - --else - --table.remove(counts, field_name) - --end - end - - rcon.print(1) - return counts -end - -function observe_buildable (player, inventory) - local recipes = {} - for field_name, recipe in pairs(player.force.recipes) do - local name = field_name:gsub("-", "_") - local can_build = 0 - for i, ingredient in pairs(recipe.ingredients) do - if inventory[ingredient.name] ~= nil then - local max_build = inventory[ingredient.name] / ingredient.amount - can_build = math.floor(max_build) - else - can_build = 0 - break - end - end - if can_build ~= 0 then - recipes[name] = can_build - end - end - return serpent.line(recipes) -end - -function dump(o) - if type(o) == 'table' then - local s = '{ ' - for k,v in pairs(o) do - if type(k) ~= 'number' then k = '"'..k..'"' end - s = s .. '['..k..'] = ' .. dump(v) .. ',' - end - return s .. '} ' - else - return tostring(o) - end -end - -function set_points_of_interest (player, field, count, chunk_x, chunk_y) - if count > 0 and global.interesting_entities[field] then - x_distance = (chunk_x*32 - player.position.x) * (chunk_x*32 - player.position.x) - y_distance = (chunk_y*32 - player.position.y) * (chunk_y*32 - player.position.y) - distance_square = x_distance + y_distance - - if global.distances_to_nearest[field] == nil then - global.distances_to_nearest[field] = 100000 - end - - if global.distances_to_nearest[field] > distance_square then - global.distances_to_nearest[field] = math.sqrt(distance_square) --only compute expensive sqrt when necessary - global.points_of_interest[field] = {x=chunk_x*32, y=chunk_y*32} - end - end -end - --- Define a function to calculate the raw material value of an item -function raw_material_value(item_name) - local values = { - ["iron-ore"] = 1, - ["copper-ore"] = 1, - ["coal"] = 1, - ["stone"] = 1, - ["crude-oil"] = 1 - } - -- Add more items and their raw material values here if needed - - return values[item_name] or 0 -end - -function get_productivity(player) - local force = player.force - local production_statistics = force.item_production_statistics - - -- Calculate production rates - local production_rates = {} - for _, prototype in pairs(game.item_prototypes) do - local name = prototype.name - local count = production_statistics.get_flow_count{name = name, input = true, precision_index = 5, count = true} - if count > 0 then - production_rates[name] = count - end - end - - -- Calculate consumption rates - local consumption_rates = {} - for _, prototype in pairs(game.item_prototypes) do - local name = prototype.name - local count = production_statistics.get_flow_count{name = name, input = false, precision_index = 5, count = true} - if count > 0 then - consumption_rates[name] = count - end - end - - -- Use the raw_material_value function to calculate the total value created - local total_production_value = 0 - local total_consumption_value = 0 - - for name, rate in pairs(production_rates) do - local value = raw_material_value(name) * rate - total_production_value = total_production_value + value - end - - for name, rate in pairs(consumption_rates) do - local value = raw_material_value(name) * rate - total_consumption_value = total_consumption_value + value - end - - local net_value_created = total_production_value - total_consumption_value - - - return { - production=production_rates, - consumption=consumption_rates - } -end - -function craft_entity(player, entity_name, count) - -- Ensure the player and entity_name are valid - if not player or not player.valid or not entity_name then - return "Entity not valid" - end - - -- Get the recipe for the entity - local recipe = player.force.recipes[entity_name] - if not recipe then - return "Recipe doesnt exist" - end - - -- Check if the entity can be crafted by hand - if recipe.category ~= "crafting" then - return "It requires " .. entity_name:gsub("-", "_") .. " which cannot be crafted by hand" - end - - - -- Check if the player has enough items to craft the entity - for _, ingredient in pairs(recipe.ingredients) do - local has_count = player.get_item_count(ingredient.name) - local need_count = ingredient.amount * count - if has_count < need_count then - return "Insufficient "..ingredient.name:gsub('-',"_"):gsub(' ',"_").." "..(need_count-has_count).." needed" - end - end - - -- Craft the entity, consuming the ingredients - for _, ingredient in pairs(recipe.ingredients) do - player.remove_item({name = ingredient.name, count = ingredient.amount * count}) - end - - -- Insert the crafted entity into the player's inventory - player.insert({name = entity_name, count = count}) - - return 1 -end - -function get_missing_ingredients(player, recipe, count, checked_recipes) - local missing_ingredients = {} - checked_recipes = checked_recipes or {} - for _, ingredient in pairs(recipe.ingredients) do - if game.item_prototypes[ingredient.name] then - local count_that_player_has = player.get_item_count(ingredient.name) - local needed = ingredient.amount * count - if count_that_player_has < needed then - local difference = needed - count_that_player_has - - - -- Check if the ingredient can be crafted - local ingredient_recipe = player.force.recipes[ingredient.name] - if ingredient_recipe and not checked_recipes[ingredient.name] then - checked_recipes[ingredient.name] = true - local sub_missing_ingredients = get_missing_ingredients(player, ingredient_recipe, difference, checked_recipes) - for sub_ingredient_name, sub_count in pairs(sub_missing_ingredients) do - if missing_ingredients[sub_ingredient_name] then - missing_ingredients[sub_ingredient_name] = missing_ingredients[sub_ingredient_name] + sub_count - else - missing_ingredients[sub_ingredient_name] = sub_count - end - end - if sub_missing_ingredients ~= nil then - missing_ingredients[ingredient.name] = difference - end - end - end - else - if game.fluid_prototypes[ingredient.name] then - abort("Crafting requires fluid ingredient " .. ingredient.name) - else - abort("Unknown ingredient " .. ingredient.name) - end - end - end - return missing_ingredients -end - -function recursively_craft_missing_ingredients(player, missing_ingredients) - local uncraftable_ingredients = {} - for ingredient_name, count in pairs(missing_ingredients) do - local success = craft_entity(player, ingredient_name, count) - if not success then - local recipe = player.force.recipes[ingredient_name] - if recipe then - local sub_missing_ingredients = get_missing_ingredients(player, recipe, count) - recursively_craft_missing_ingredients(player, sub_missing_ingredients) - else - uncraftable_ingredients[ingredient_name] = count - end - end - end - return uncraftable_ingredients -end - -function add_to_crafting_queue(player, entity_name, count) - -- Ensure the player and entity_name are valid - if not player or not player.valid or not entity_name then - return "not_valid_entity" - end - - -- Get the recipe for the entity - local recipe = player.force.recipes[entity_name] - if not recipe then - return "no_valid_recipe" - end - - -- Check if the player has enough items to craft the entity - local missing_ingredients = get_missing_ingredients(player, recipe, count) - if next(missing_ingredients) == nil then - -- Craft the requested entity - return craft_entity(player, entity_name, count) - else - local uncraftable_ingredients = recursively_craft_missing_ingredients(player, missing_ingredients) - - if next(uncraftable_ingredients) ~= nil then - local uncraftable_message = "Cannot craft the following missing ingredients " - for ingredient_name, count in pairs(uncraftable_ingredients) do - uncraftable_message = uncraftable_message .. count .. "x " .. ingredient_name .. "___" - end - return uncraftable_message:sub(1, -3) - - else - -- Craft the requested entity - return craft_entity(player, entity_name, count) - end - - end -end - -function inspect(player, radius, position) - local surface = player.surface - local bounding_box = { - left_top = {x = position.x - radius, y = position.y - radius}, - right_bottom = {x = position.x + radius, y = position.y + radius} - } - - local entities = surface.find_entities_filtered({bounding_box, force = "player"}) - local entity_data = {} - - for _, entity in ipairs(entities) do - if entity.name ~= 'character' then - local data = { - name = entity.name:gsub("-", "_"), - position = entity.position, - direction = entity.direction,--directions[entity.direction+1], - health = entity.health, - force = entity.force.name, - energy = entity.energy, - status = entity.status, - --crafted_items = entity.crafted_items or nil - } - - -- Get entity contents if it has an inventory - if entity.get_inventory(defines.inventory.chest) then - local inventory = entity.get_inventory(defines.inventory.chest).get_contents() - data.contents = inventory - end - - data.warnings = get_issues(entity) - - -- Get entity orientation if it has an orientation attribute - if entity.type == "train-stop" or entity.type == "car" or entity.type == "locomotive" then - data.orientation = entity.orientation - end - - -- Get connected entities for pipes and transport belts - if entity.type == "pipe" or entity.type == "transport-belt" then - local path_ends = find_path_ends(entity) - data.path_ends = {} - for _, path_end in pairs(path_ends) do - local path_position = {x=path_end.position.x - player.position.x, y=path_end.position.y - player.position.y} - table.insert(data.path_ends, {name = path_end.name:gsub("-", "_"), position = path_position, unit_number = path_end.unit_number}) - end - end - - table.insert(entity_data, data) - else - local data = { - name = "player_character", - position = entity.position, - direction = directions[entity.direction+1], - } - table.insert(entity_data, data) - end - end - - -- Sort entities with path_ends by the length of path_ends in descending order - table.sort(entity_data, function(a, b) - if a.path_ends and b.path_ends then - return #a.path_ends > #b.path_ends - elseif a.path_ends then - return true - else - return false - end - end) - - -- Remove entities that exist in the path_ends of other entities - local visited_paths = {} - local filtered_entity_data = {} - for _, data in ipairs(entity_data) do - if data.path_ends then - local should_add = true - for _, path_end in ipairs(data.path_ends) do - if visited_paths[path_end.unit_number] then - should_add = false - break - end - end - if should_add then - for _, path_end in ipairs(data.path_ends) do - visited_paths[path_end.unit_number] = true - end - table.insert(filtered_entity_data, data) - else - data.path_ends = nil - --table.insert(filtered_entity_data, data) - end - else - table.insert(filtered_entity_data, data) - end - end - entity_data = filtered_entity_data - - return entity_data -end - -function required_resource_present(entity, position, surface) - local prototype = game.entity_prototypes[entity] - local entity_type = prototype.type - - if entity_type == "mining-drill" then - local resources = surface.find_entities_filtered{position=position, type="resource"} - for _, resource in ipairs(resources) do - if resource.prototype.mineable_properties.minable then - return true, nil - end - end - return false, "minable resource" - - elseif entity_type == "offshore-pump" then - local tile = surface.get_tile(position) - if tile.prototype.name == "water" or tile.prototype.name == "deepwater" then - return true, nil - end - return false, "water" - - else - return true, nil - end -end - -rcon.print(1) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise_inventory.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise_inventory.lua deleted file mode 100644 index e1c280ad9..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/initialise_inventory.lua +++ /dev/null @@ -1,9 +0,0 @@ -global.actions.initialise_inventory = function(player_index, item_names_and_counts_json) - local player = global.agent_characters[player_index] - local item_names_and_counts = game.json_to_table(item_names_and_counts_json) - - -- Loop through the entity names and insert them into the player's inventory - for item, count in pairs(item_names_and_counts) do - player.get_main_inventory().insert{name=item, count=count} - end -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/priority_queue.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/priority_queue.lua deleted file mode 100644 index c87ad1d60..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/priority_queue.lua +++ /dev/null @@ -1,61 +0,0 @@ -local PriorityQueue = {} -PriorityQueue.__index = PriorityQueue - --- Create a new PriorityQueue. -function PriorityQueue.new(comparator) - return setmetatable({queue = {}, comparator = comparator or function(a, b) return a < b end}, PriorityQueue) -end - --- Insert a new element into the queue. -function PriorityQueue:insert(element) - -- Insert the element at the end of the queue. - table.insert(self.queue, element) - - -- Bubble up the element to its correct place. - local i = #self.queue - while i > 1 do - local parent = math.floor(i / 2) - if not self.comparator(self.queue[i], self.queue[parent]) then break end - self.queue[i], self.queue[parent] = self.queue[parent], self.queue[i] - i = parent - end -end - --- Remove and return the top element of the queue. -function PriorityQueue:pop() - if #self.queue == 0 then error("Queue is empty") end - - local top = self.queue[1] - - -- Replace the root with the last element in the queue. - self.queue[1] = self.queue[#self.queue] - table.remove(self.queue) - - -- Bubble down the root element to its correct place. - local i = 1 - while true do - local left = 2 * i - local right = left + 1 - local smallest = i - - if left <= #self.queue and self.comparator(self.queue[left], self.queue[smallest]) then - smallest = left - end - if right <= #self.queue and self.comparator(self.queue[right], self.queue[smallest]) then - smallest = right - end - if smallest == i then break end - - self.queue[i], self.queue[smallest] = self.queue[smallest], self.queue[i] - i = smallest - end - - return top -end - --- Check if the queue is empty. -function PriorityQueue:is_empty() - return #self.queue == 0 -end - -return PriorityQueue diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/production_score.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/production_score.lua deleted file mode 100644 index 0c9decc70..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/production_score.lua +++ /dev/null @@ -1,339 +0,0 @@ ---local player = game.game.get_player(arg1) - -local function get_total_production_counts(production_statistics) - local produced = production_statistics.input_counts - local consumed = production_statistics.output_counts - for name, value in pairs (consumed) do - if produced[name] then - produced[name] = produced[name] - value - else - produced[name] = -value - end - end - return produced -end - -local function get_raw_resources() - local raw_resources = {} - local entities = game.entity_prototypes - for name, entity_prototype in pairs (entities) do - if entity_prototype.resource_category then - if entity_prototype.mineable_properties and entity_prototype.mineable_properties.products then - for k, product in pairs (entity_prototype.mineable_properties.products) do - raw_resources[product.name] = true - end - end - end - if entity_prototype.fluid then - raw_resources[entity_prototype.fluid.name] = true - end - end - return raw_resources -end - -local function get_product_list() - local product_list = {} - local recipes = game.recipe_prototypes - - for recipe_name, recipe_prototype in pairs (recipes) do - if recipe_prototype.allow_decomposition then - local ingredients = recipe_prototype.ingredients - local products = recipe_prototype.products - for k, product in pairs (products) do - if not product_list[product.name] then - product_list[product.name] = {} - end - local recipe_ingredients = {} - local product_amount = util.product_amount(product) - if product_amount > 0 then - for j, ingredient in pairs (ingredients) do - recipe_ingredients[ingredient.name] = ((ingredient.amount)/#products) / product_amount - end - recipe_ingredients.energy = (recipe_prototype.energy / #products) / product_amount - table.insert(product_list[product.name], recipe_ingredients) - end - end - end - end - - local items = game.item_prototypes - local entities = game.entity_prototypes - --[[Now we do some tricky stuff for space science type items]] - local rocket_silos = {} - for k, entity in pairs (entities) do - if entity.type == "rocket-silo" and entity.fixed_recipe then - local recipe = recipes[entity.fixed_recipe] - if not recipe then return end - local required_parts = entity.rocket_parts_required - local list = {} - for k, product in pairs (recipe.products) do - local product_amount = util.product_amount(product) - if product_amount > 0 then - product_amount = product_amount * required_parts - list[product.name] = product_amount - end - end - list["energy"] = recipe.energy - table.insert(rocket_silos, list) - end - end - for k, item in pairs (items) do - local launch_products = item.rocket_launch_products - if launch_products then - for k, launch_product in pairs (launch_products) do - product_list[launch_product.name] = product_list[launch_product.name] or {} - local launch_product_amount = util.product_amount(launch_product) - if launch_product_amount > 0 then - for k, silo_products in pairs (rocket_silos) do - local this_silo = {} - for product_name, product_count in pairs (silo_products) do - this_silo[product_name] = product_count / launch_product_amount - end - this_silo[item.name] = 1 / launch_product_amount - table.insert(product_list[launch_product.name], this_silo) - end - end - end - end - end - return product_list -end - -local default_seed_prices = function() - return - { - ["iron-ore"] = 3.1, - ["copper-ore"] = 3.6, - ["coal"] = 3, - ["stone"] = 2.4, - ["crude-oil"] = 0.2, - ["water"] = 1/1000, - ["steam"] = 1/1000, - ["wood"] = 3.2, - ["raw-fish"] = 100, - ["energy"] = 1, - ["uranium-ore"] = 8.2 - } -end - -local default_ingredient_exponent = function() return 2 end --1.025 end -local default_raw_resource_price = function() return 2.5 end -local default_resource_ignore = function() return {} end - -local default_param = function() - return - { - ingredient_exponent = 1.025, --[[The exponent for increase in value for each additional ingredient formula exponent^#ingredients-2]] - raw_resource_price = 2.5, --[[If a raw resource isn't given a price, it uses this price]] - resource_ignore = {} --[[This is used to account for mods removing resource generation, in which case we want the item price to be calculated from recipes.]] - } -end - -local ingredient_multiplier = function(recipe, param) - return (param.ingredient_exponent or 1) ^ (table_size(recipe) - 2) -end - -local deduce_nil_prices = function(price_list, param) - local nil_prices = {} - for name, item in pairs (game.item_prototypes) do - if not price_list[name] then - nil_prices[name] = {} - end - end - for name, item in pairs (game.fluid_prototypes) do - if not price_list[name] then - nil_prices[name] = {} - end - end - local recipes = game.recipe_prototypes - for name, recipe in pairs (recipes) do - for k, ingredient in pairs (recipe.ingredients) do - if nil_prices[ingredient.name] then - table.insert(nil_prices[ingredient.name], recipe) - end - end - end - for name, recipes in pairs (nil_prices) do - if #recipes > 0 then - local recipe_cost - local ingredient_amount - for k, recipe in pairs (recipes) do - local ingredient_value = 0 - for k, ingredient in pairs (recipe.ingredients) do - if ingredient.name == name then - ingredient_amount = ingredient.amount - else - local ingredient_price = price_list[ingredient.name] - if ingredient_price then - ingredient_value = ingredient_value + (ingredient_price * ingredient.amount) - else - ingredient_value = nil - break - end - end - end - if not ingredient_value then break end - local product_value = 0 - for k, product in pairs (recipe.products) do - local amount = util.product_amount(product) - local product_price = price_list[product.name] - if product_price then - product_value = product_value + product_price * amount - else - product_value = nil - break - end - end - if not product_value then - break - end - local reverse_price = (product_value - param.energy_addition(recipe, product_value)) / ingredient_multiplier(recipe.ingredients, param) -- Not perfect, but close enough - local this_cost = (reverse_price - ingredient_value) / ingredient_amount - if recipe_cost then - recipe_cost = math.min(recipe_cost, this_cost) - else - recipe_cost = this_cost - end - end - if recipe_cost then - price_list[name] = param.round(recipe_cost) - end - end - end -end - -local ln = math.log -local default_energy_addition = function(recipe, cost) - return ((ln(recipe.energy + 1) * (cost ^ 0.5))) -end - -local default_rounding = function(number) - return number -end - -production_score = {} - -global.actions.generate_price_list = function(param) - - local param = param or {} - local price_list = param.seed_prices or default_seed_prices() - - param.ingredient_exponent = param.ingredient_exponent or default_ingredient_exponent() - param.raw_resource_price = param.raw_resource_price or default_raw_resource_price() - param.resource_ignore = param.resource_ignore or default_resource_ignore() - param.round = param.round or default_rounding - param.energy_addition = param.energy_addition or default_energy_addition - param.normalise = param.normalise or function(number) return number end - - local resource_list = get_raw_resources() - - for name, k in pairs (resource_list) do - if not price_list[name] then - price_list[name] = param.round(param.raw_resource_price) - end - end - - for k, name in pairs (param.resource_ignore or {}) do - price_list[name] = nil - end - - local product_list = get_product_list() - local get_price_recursive - get_price_recursive = function(name, current_loop, loop_force) - local price = price_list[name] - if price then return price end - price = 0 - if current_loop[name] then - if loop_force then - return param.raw_resource_price - end - return - end - current_loop[name] = true - local entry = product_list[name] - if not entry then return end - local recipe_cost - for k, recipe in pairs (entry) do - local this_recipe_cost = 0 - for ingredient_name, cost in pairs (recipe) do - if ingredient_name ~= "energy" then - local addition = get_price_recursive(ingredient_name, current_loop, loop_force) - if addition and addition > 0 then - this_recipe_cost = this_recipe_cost + (addition * cost) - else - this_recipe_cost = 0 - break - end - end - end - if this_recipe_cost > 0 then - this_recipe_cost = (this_recipe_cost * ingredient_multiplier(recipe, param)) + param.energy_addition(recipe, this_recipe_cost) - if recipe_cost then - recipe_cost = math.min(recipe_cost, this_recipe_cost) - else - recipe_cost = this_recipe_cost - end - end - end - if recipe_cost then - price = param.round(recipe_cost) - price_list[name] = price - return price - end - end - local items = game.item_prototypes - for name, item in pairs (items) do - local current_loop = {} - get_price_recursive(name, current_loop) - end - local fluids = game.fluid_prototypes - for name, fluid in pairs (fluids) do - local current_loop = {} - get_price_recursive(name, current_loop) - end - deduce_nil_prices(price_list, param) - for name, item in pairs (items) do - local current_loop = {} - get_price_recursive(name, current_loop, true) - end - for name, fluid in pairs (fluids) do - local current_loop = {} - get_price_recursive(name, current_loop, true) - end - deduce_nil_prices(price_list, param) - - for k, price in pairs (price_list) do - price_list[k] = param.normalise(price) - end - - return price_list -end - -production_score.get_production_scores = function(price_list) - local price_list = price_list or global.actions.generate_price_list() - local scores = {} - for k, force in pairs (game.forces) do - local score = 0 - for name, value in pairs (get_total_production_counts(force.item_production_statistics)) do - local price = price_list[name] - if price then - score = score + (price * value) - end - end - for name, value in pairs (get_total_production_counts(force.fluid_production_statistics)) do - local price = price_list[name] - if price then - score = score + (price * value) - end - end - scores[force.name] = math.floor(score) - end - return scores -end - -local scores = production_score.get_production_scores() -if scores then - global.initial_score = scores -else - global.initial_score = {["player"] = 0} -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/recipe_fluid_connection_mappings.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/recipe_fluid_connection_mappings.lua deleted file mode 100644 index c94bfd388..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/recipe_fluid_connection_mappings.lua +++ /dev/null @@ -1,174 +0,0 @@ --- Helper function to get fluid type from ingredient/product -local function get_fluid_type(item) - if item.type == "fluid" then - return item.name - end - return nil -end - --- Helper function to get position offsets based on fluid type and recipe -local function get_refinery_position_offsets(fluid_type, recipe, is_input) - -- Basic oil processing - if #recipe.ingredients == 1 then - if not is_input then - return 2, -3 -- Top right - else - return 1, 3 -- Bottom right - end - end - - -- game.print(serpent.block(recipe.ingredients)) - -- Advanced oil processing - if #recipe.ingredients == 2 and recipe.ingredients[2].name == "crude-oil" then - if is_input then - if fluid_type == "crude-oil" then - return 1, 3 -- Top left - else - return -1, 3 -- Top right (water) - end - else - if fluid_type == "heavy-oil" then - return -2, -3 -- Bottom left - elseif fluid_type == "light-oil" then - return 0, -3 -- Bottom middle - else - return 2, -3 -- Bottom right (heavy oil) - end - end - end - - -- Coal liquefaction - if #recipe.ingredients == 3 and recipe.ingredients[3].name == "steam" then - if is_input then - if fluid_type == "steam" then - return 1, 3 -- Top left - else - return -1, 3 -- Top right (heavy oil) - end - else - if fluid_type == "petroleum-gas" then - return -2, -3 -- Bottom left - elseif fluid_type == "light-oil" then - return 0, -3 -- Bottom middle - else - return 2, -3 -- Bottom right (heavy oil) - end - end - end - return 0,0 -end - --- Helper function to get chemical plant position offsets -local function get_chemical_plant_position_offsets(index, total, is_input) - if is_input then - if total == 1 then - return -1, -1.5 -- Bottom left for single input - else - -- For two inputs, space them out - return -1 + (index - 1) * 2, -1.5 - end - else - -- Outputs always go on the left side - return -1, 1.5 - end -end - --- Helper function to rotate coordinates based on entity direction -local function rotate_coordinates(x_offset, y_offset, direction) - if direction == defines.direction.north then - return x_offset, y_offset - elseif direction == defines.direction.south then - return -x_offset, -y_offset - elseif direction == defines.direction.east then - return -y_offset, x_offset - elseif direction == defines.direction.west then - return y_offset, -x_offset - end -end - -global.utils.get_refinery_fluid_mappings = function(entity, recipe) - if not entity or not recipe then return nil end - if not entity.position then return nil end - - local x, y = entity.position.x, entity.position.y - local input_points = {} - local output_points = {} - - -- Map inputs - for _, ingredient in pairs(recipe.ingredients) do - if ingredient.type == "fluid" then - local x_offset, y_offset = get_refinery_position_offsets(ingredient.name, recipe, true) - local rotated_x, rotated_y = rotate_coordinates(x_offset, y_offset, entity.direction) - table.insert(input_points, { - x = x + rotated_x, - y = y + rotated_y, - type = "\""..ingredient.name.."\"" - }) - end - end - - -- Map outputs - for _, product in pairs(recipe.products) do - if product.type == "fluid" then - local x_offset, y_offset = get_refinery_position_offsets(product.name, recipe, false) - local rotated_x, rotated_y = rotate_coordinates(x_offset, y_offset, entity.direction) - table.insert(output_points, { - x = x + rotated_x, - y = y + rotated_y, - type = "\""..product.name.."\"" - }) - end - end - - return { - inputs = input_points, - outputs = output_points - } -end - --- Helper function to map connection points to fluid types for chemical plant -global.utils.get_chemical_plant_fluid_mappings = function(entity, recipe) - if not entity or not recipe then return nil end - if not entity.position then return nil end - - local x, y = entity.position.x, entity.position.y - local input_points = {} - local output_points = {} - - -- Count fluid inputs - local fluid_inputs = {} - for _, ingredient in pairs(recipe.ingredients) do - if ingredient.type == "fluid" then - table.insert(fluid_inputs, ingredient) - end - end - - -- Map inputs - for i, ingredient in ipairs(fluid_inputs) do - local x_offset, y_offset = get_chemical_plant_position_offsets(i, #fluid_inputs, true) - local rotated_x, rotated_y = rotate_coordinates(x_offset, y_offset, entity.direction) - table.insert(input_points, { - x = x + rotated_x, - y = y + rotated_y, - type = "\""..ingredient.name.."\"" - }) - end - - -- Map outputs - for _, product in pairs(recipe.products) do - if product.type == "fluid" then - local x_offset, y_offset = get_chemical_plant_position_offsets(1, 1, false) - local rotated_x, rotated_y = rotate_coordinates(x_offset, y_offset, entity.direction) - table.insert(output_points, { - x = x + rotated_x, - y = y + rotated_y, - type = "\""..product.name.."\"" - }) - end - end - - return { - inputs = input_points, - outputs = output_points - } -end \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset.lua deleted file mode 100644 index 9667a0d21..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset.lua +++ /dev/null @@ -1 +0,0 @@ -game.reset_game_state() \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset_position.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset_position.lua deleted file mode 100644 index 9b91c04a2..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/reset_position.lua +++ /dev/null @@ -1,2 +0,0 @@ -global.agent_characters[arg1].teleport({arg2, arg3}) -rcon.print(1) \ No newline at end of file diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/serialize.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/serialize.lua deleted file mode 100644 index 3934682da..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/serialize.lua +++ /dev/null @@ -1,1873 +0,0 @@ --- Library for serializing items in Factorio --- Based on code from playerManager and trainTeleports - -if not global.utils then - global.utils = {} -end - -local function version_to_table(version) - local t = {} - for p in string.gmatch(version, "%d+") do - t[#t + 1] = tonumber(p) - end - return t -end - --- 0.17 compatibility -local supports_bar, get_bar, set_bar, version -if (pcall(function() local mods = script.active_mods end)) then - supports_bar = "supports_bar" - get_bar = "get_bar" - set_bar = "set_bar" - version = version_to_table(script.active_mods.base) -else - supports_bar = "hasbar" - get_bar = "getbar" - set_bar = "setbar" - version = version_to_table("0.17.69") -end - --- returns true if the game version is greater than or equal to the given version -local function version_ge(comp) - comp = version_to_table(comp) - for i=1, 3 do - if comp[i] > version[i] then - return false - elseif comp[i] < version[i] then - return true - end - end - return true -end - -local has_create_grid = version_ge("1.1.7") - --- Add this helper function at the top level -local function is_fluid_handler(entity_type) - return entity_type == "boiler" or - entity_type == "offshore-pump" or - entity_type == "pump" or - entity_type == "generator" or - entity_type == "oil-refinery" or - entity_type == "chemical-plant" - --entity_type == "pipe" or - --entity_type == "pipe-to-ground" -end - - --- Equipment Grids are serialized into an array of equipment entries --- where ench entry is a table with the following fields: --- n: name --- p: position (array of 2 numbers corresponding to x and y) --- s: shield (optional) --- e: energy (optional) --- If the equipment is a burner the following is also present: --- i: burner inventory --- r: result inventory --- b: curently burning (optional) --- f: remaining_burning_fuel (optional) -global.utils.serialize_equipment_grid = function(grid) - local serialized = {} - local processed = {} - for y = 0, grid.height - 1 do - for x = 0, grid.width - 1 do - local equipment = grid.get({x, y}) - if equipment ~= nil then - local pos = equipment.position - local combined_pos = pos.x + pos.y * grid.width + 1 - if not processed[combined_pos] then - processed[combined_pos] = true - local entry = { - n = equipment.name, - p = {pos.x, pos.y}, - } - if equipment.shield > 0 then entry.s = equipment.shield end - if equipment.energy > 0 then entry.e = equipment.energy end - -- TODO: Test with Industrial Revolution - if equipment.burner then - local burner = equipment.burner - entry.i = global.utils.serialize_inventory(burner.inventory) - entry.r = global.utils.serialize_inventory(burner.burnt_result_inventory) - if burner.curently_burning then - entry.b = {} - global.utils.serialize_item_stack(burner.curently_burning, entry.b) - entry.f = burner.remaining_burning_fuel - end - end - table.insert(serialized, entry) - end - end - end - end - return serialized -end - -global.utils.deserialize_equipment_grid = function(grid, serialized) - grid.clear() - for _, entry in ipairs(serialized) do - local equipment = grid.put({ - name = entry.n, - position = entry.p, - }) - if equipment then - if entry.s then equipment.shield = entry.s end - if entry.e then equipment.energy = entry.e end - if entry.i then - if entry.b then global.utils.deserialize_item_stack(burner.currently_burning, entry.b) end - if entry.f then burner.remaining_burning_fuel = entry.f end - global.utils.deserialize_inventory(burner.burnt_result_inventory, entry.r) - global.utils.deserialize_inventory(burner.inventory, entry.i) - end - end - end -end - --- Item stacks are serialized into a table with the following fields: --- n: name --- c: count --- h: health (optional) --- d: durability (optional) --- a: ammo count (optional) --- l: label (optional) --- g: equipment grid (optional) --- i: item inventory (optional) --- If the item stack is exportable it has the following property instead --- e: export string --- Label is a table with the following fields: --- t: label text (optional) --- c: color (optional) --- a: allow manual label change -global.utils.serialize_item_stack = function(slot, entry) - if - slot.is_blueprint - or slot.is_blueprint_book - or slot.is_upgrade_item - or slot.is_deconstruction_item - or slot.is_item_with_tags - then - local call_success, call_return = pcall(slot.export_stack) - if not call_success then - print("Error: '" .. call_return .. "' thrown exporting '" .. slot.name .. "'") - else - entry.e = call_return - end - - return - end - - entry.n = slot.name - entry.c = slot.count - if slot.health < 1 then entry.h = slot.health end - if slot.durability then entry.d = slot.durability end - if slot.type == "ammo" then entry.a = slot.ammo end - if slot.is_item_with_label then - local label = {} - if slot.label then label.t = slot.label end - if slot.label_color then label.c = slot.label_color end - label.a = slot.allow_manual_label_change - entry.l = label - end - - if slot.grid then - entry.g = global.utils.serialize_equipment_grid(slot.grid) - end - - if slot.is_item_with_inventory then - local sub_inventory = slot.get_inventory(defines.inventory.item_main) - entry.i = global.utils.serialize_inventory(sub_inventory) - end -end - -global.utils.deserialize_item_stack = function(slot, entry) - if entry.e then - local success = slot.import_stack(entry.e) - if success == 1 then - print("Error: import of '" .. entry.e .. "' succeeded with errors") - elseif success == -1 then - print("Error: import of '" .. entry.e .. "' failed") - end - - return - end - - local item_stack = { - name = entry.n, - count = entry.c, - } - if entry.h then item_stack.health = entry.h end - if entry.d then item_stack.durability = entry.d end - if entry.a then item_stack.ammo = entry.a end - - local call_success, call_return = pcall(slot.set_stack, item_stack) - if not call_success then - print("Error: '" .. call_return .. "' thrown setting stack ".. serpent.line(entry)) - - elseif not call_return then - print("Error: Failed to set stack " .. serpent.line(entry)) - - else - if entry.l then - -- TODO test this with AAI's unit-remote-control - local label = entry.l - if label.t then slot.label = label.t end - if label.c then slot.label_color = label.c end - slot.allow_manual_label_change = label.a - end - if entry.g then - if slot.grid then - global.utils.deserialize_equipment_grid(slot.grid, entry.g) - elseif slot.type == "item-with-entity-data" and has_create_grid then - slot.create_grid() - global.utils.deserialize_equipment_grid(slot.grid, entry.g) - else - print("Error: Attempt to deserialize equipment grid on an unsupported entity") - end - end - if entry.i then - local sub_inventory = slot.get_inventory(defines.inventory.item_main) - global.utils.deserialize_inventory(sub_inventory, entry.i) - end - end -end - ------ DEPRECATED ---global.utils.serialize_inventory = function(inventory) --- local serialized = {} --- for i = 1, #inventory do --- local slot = inventory[i] --- if slot.valid_for_read then --- local item_name = "\"" .. slot.name .. "\"" --- if serialized[item_name] then --- serialized[item_name] = serialized[item_name] + slot.count --- else --- serialized[item_name] = slot.count --- end --- end --- end --- return serialized ---end - ------ DEPRECATED ---global.utils.serialize_inventory_old = function(inventory) --- local serialized = {} --- if inventory[supports_bar]() and inventory[get_bar]() <= #inventory then --- serialized.b = inventory[get_bar]() --- end --- --- serialized.i = {} --- local previous_index = 0 --- local previous_serialized = nil --- for i = 1, #inventory do --- local item = {} --- local slot = inventory[i] --- if inventory.supports_filters() then --- item.f = inventory.get_filter(i) --- end --- --- if slot.valid_for_read then --- global.utils.serialize_item_stack(slot, item) --- end --- --- if item.n or item.f or item.e then --- local item_serialized = game.table_to_json(item) --- if item_serialized == previous_serialized then --- local previous_item = serialized.i[#serialized.i] --- previous_item.r = (previous_item.r or 0) + 1 --- previous_index = i --- --- else --- if i ~= previous_index + 1 then --- item.s = i --- end --- --- previous_index = i --- previous_serialized = item_serialized --- table.insert(serialized.i, item) --- end --- --- else --- -- Either an empty slot or serilization failed --- previous_index = 0 --- previous_serialized = nil --- end --- end --- --- return serialized ---end - -global.utils.deserialize_inventory = function(inventory, serialized) - if serialized.b and inventory[supports_bar]() then - inventory[set_bar](serialized.b) - end - - local last_slot_index = 0 - for _, entry in ipairs(serialized.i) do - local base_index = entry.s or last_slot_index + 1 - - local repeat_count = entry.r or 0 - for offset = 0, repeat_count do - -- XXX what if the inventory is smaller on this instance? - local index = base_index + offset - local slot = inventory[index] - if entry.f then - local call_success, call_return = pcall(inventory.set_filter, index, entry.f) - if not call_success then - print("Error: '" .. call_return .. "' thrown setting filter " .. entry.f) - - elseif not call_return then - print("Error: Failed to set filter " .. entry.f) - end - end - - if entry.n or entry.e then - global.utils.deserialize_item_stack(slot, entry) - end - end - last_slot_index = base_index + repeat_count - end -end - -global.utils.serialize_recipe = function(recipe) - local serialized = { - name = "\"" .. recipe.name .. "\"", - category = "\"" .. recipe.category .. "\"", - enabled = recipe.enabled, - --hidden = recipe.hidden, - energy = recipe.energy, - --order = recipe.order, - --group = recipe.group and "\"" .recipe.group.name.. "\"" or nil, - --subgroup = recipe.subgroup and "\"" .recipe.subgroup.name.. "\"" or nil, - --force = recipe.force and recipe.force.name or nil, - } - - -- Serialize ingredients - serialized.ingredients = {} - for _, ingredient in pairs(recipe.ingredients) do - table.insert(serialized.ingredients, {name = "\""..ingredient.name.."\"", type = "\""..ingredient.type.."\"", amount = ingredient.amount}) - end - - -- Serialize products - serialized.products = {} - - -- First try normal products - local output_list = recipe.products - if output_list then - for _, product in pairs(output_list) do - local product_table = {name = "\""..product.name.."\"", type = "\""..product.type.."\"", amount = product.amount} - if product.probability then product_table.probability = product.probability end - table.insert(serialized.products, product_table) - end - end - - return serialized -end - -global.utils.serialize_fluidbox = function(fluidbox) - local serialized = { - length = #fluidbox, - } - if fluidbox.owner and fluidbox.owner.name then - serialized[fluidbox.owner] = fluidbox.owner.name - end - - -- Serialize each fluid box - serialized.fluidboxes = {} - for i = 1, #fluidbox do - local box = fluidbox[i] - local prototype = fluidbox.get_prototype(i) - local connections = fluidbox.get_connections(i) - local filter = fluidbox.get_filter(i) - local flow = fluidbox.get_flow(i) - local locked_fluid = fluidbox.get_locked_fluid(i) - local fluid_system_id = fluidbox.get_fluid_system_id(i) - - local serialized_box = { - prototype = prototype and prototype.object_name or nil, - capacity = fluidbox.get_capacity(i), - connections = {}, - filter = filter, - flow = flow, - locked_fluid = locked_fluid, - fluid_system_id = fluid_system_id, - } - - -- Serialize fluid - if box then - serialized_box.fluid = { - name = box.name, - amount = box.amount, - temperature = box.temperature, - } - end - - -- Serialize connections - for _, connection in pairs(connections) do - if connection.owner and connection.owner.name then - table.insert(serialized_box.connections, "\""..connection.owner.name .. "\"") - - end - end - - serialized.fluidboxes[i] = serialized_box - end - - return serialized -end - - --- Helper function to get relative direction of neighbor -local function get_neighbor_direction(entity, neighbor) - local dx = neighbor.position.x - entity.position.x - local dy = neighbor.position.y - entity.position.y - - -- Determine primary direction based on which delta is larger - if math.abs(dx) > math.abs(dy) then - return dx > 0 and defines.direction.east or defines.direction.west - else - return dy > 0 and defines.direction.south or defines.direction.north - end -end - --- Helper function to expand a bounding box by a given amount -local function expand_box(box, amount) - return { - left_top = { - x = box.left_top.x - amount, - y = box.left_top.y - amount - }, - right_bottom = { - x = box.right_bottom.x + amount, - y = box.right_bottom.y + amount - } - } -end - -local function serialize_neighbours(entity) - local neighbours = {} - - -- Get entity's prototype collision box - local prototype = game.entity_prototypes[entity.name] - local collision_box = prototype.collision_box - - -- Create a slightly larger search box - local search_box = expand_box(collision_box, 0.707) -- Expand by 0.5 tiles - - -- Convert to world coordinates - local world_box = { - left_top = { - x = entity.position.x + search_box.left_top.x, - y = entity.position.y + search_box.left_top.y - }, - right_bottom = { - x = entity.position.x + search_box.right_bottom.x, - y = entity.position.y + search_box.right_bottom.y - } - } - - -- Find entities within the expanded collision box - local nearby = entity.surface.find_entities_filtered{ - area = world_box - } - - -- Process each nearby entity - for _, neighbor in pairs(nearby) do - -- Skip if it's the same entity or if it has no unit number - if neighbor.unit_number and neighbor.unit_number ~= entity.unit_number then - table.insert(neighbours, { - unit_number = neighbor.unit_number, - direction = get_neighbor_direction(entity, neighbor), - name = "\""..neighbor.name.."\"", - position = neighbor.position - --type = neighbor.type - }) - end - end - - return neighbours -end - -function add_burner_inventory(serialized, burner) - local fuel_inventory = burner.inventory - if fuel_inventory and #fuel_inventory > 0 then - serialized.fuel_inventory = {} - serialized.remaining_fuel = 0 - for i = 1, #fuel_inventory do - local item = fuel_inventory[i] - if item and item.valid_for_read then - - table.insert(serialized.fuel_inventory, {name = "\""..item.name.."\"", count = item.count}) - serialized.remaining_fuel = serialized.remaining_fuel + item.count - end - end - end -end - -function get_entity_direction(entity, direction) - - -- If direction is nil then return north - if direction == nil then - return defines.direction.north - end - --game.print("Getting direction: " .. entity .. " with direction: " .. direction) - - - local prototype = game.entity_prototypes[entity] - -- if prototype is nil (e.g because the entity is a ghost or player character) then return the direction as is - if prototype == nil then - return direction - end - --game.print(game.entity_prototypes[entity].name, {skip=defines.print_skip.never}) - - local cardinals = { - defines.direction.north, - defines.direction.east, - defines.direction.south, - defines.direction.west - } - if prototype and (prototype.name == "boiler" or prototype.type == "generator" or prototype.name == "heat-exchanger") then - if direction == 0 then - return defines.direction.north - elseif direction == 1 then - return defines.direction.east - elseif direction == 2 then - return defines.direction.south - else - return defines.direction.west - end - elseif prototype and prototype.name == "offshore-pump" then - if direction == 2 then - return defines.direction.north - elseif direction == 3 then - return defines.direction.east - elseif direction == 0 then - return defines.direction.south - else - return defines.direction.west - end - elseif prototype and prototype.name == "oil-refinery" then - if direction == 0 then - return defines.direction.north - elseif direction == 1 then - return defines.direction.east - elseif direction == 2 then - return defines.direction.south - else - return defines.direction.west - end - elseif prototype and prototype.name == "chemical-plant" then - if direction == 2 then - return defines.direction.north - elseif direction == 3 then - return defines.direction.east - elseif direction == 0 then - return defines.direction.south - else - return defines.direction.west - end - elseif prototype and prototype.type == "transport-belt" or prototype.type == "splitter" then - --game.print("Transport belt direction: " .. direction) - if direction == 0 then - return defines.direction.north - elseif direction == 3 then - return defines.direction.west - elseif direction == 2 then - return defines.direction.south - else - return defines.direction.east - end - elseif prototype and prototype.type == "inserter" then - --return cardinals[(direction % 4)] - if direction == 0 then - return defines.direction.south - elseif direction == 1 then - return defines.direction.west - elseif direction == 2 then - return defines.direction.north - else - return defines.direction.east - end - elseif prototype.type == "mining-drill" then - if direction == 1 then - return cardinals[2] - elseif direction == 2 then - return cardinals[3] - elseif direction == 3 then - return cardinals[4] - else - return cardinals[1] - end - elseif prototype.type == "underground-belt" then - if direction == 1 then - return defines.direction.east - elseif direction == 2 then - return defines.direction.south - elseif direction == 3 then - return defines.direction.west - else - return defines.direction.north - end - elseif prototype.type == "pipe-to-ground" then - if direction == 1 then - return defines.direction.west - elseif direction == 2 then - return defines.direction.north - elseif direction == 3 then - return defines.direction.east - else - return defines.direction.south - end - elseif prototype.type == "assembling-machine" then - if direction == 0 then - return defines.direction.north - elseif direction == 1 then - return defines.direction.east - elseif direction == 2 then - return defines.direction.south - else - return defines.direction.west - end - elseif prototype.type == "storage-tank" then - if direction == 0 then - return defines.direction.north - elseif direction == 1 then - return defines.direction.east -- Only 2 directions - elseif direction == 2 then - return defines.direction.south -- Only 2 directions - else - return defines.direction.west -- Only 2 directions - end - else - return direction - end - return direction -end - - - -function get_inverse_entity_direction(entity, factorio_direction) - local prototype = game.entity_prototypes[entity] - - if not factorio_direction then - return 0 -- Assuming 0 is the default direction in your system - end - - if prototype and prototype.name == "offshore-pump" then - if factorio_direction == defines.direction.west then - return defines.direction.east - elseif factorio_direction == defines.direction.east then - return defines.direction.west - elseif factorio_direction == defines.direction.south then - return defines.direction.north - else - return defines.direction.south - end - end - --game.print("Getting inverse direction: " .. entity .. " with direction: " .. factorio_direction) - if prototype and prototype.type == "inserter" then - if factorio_direction == defines.direction.south then - return defines.direction.north - elseif factorio_direction == defines.direction.west then - return defines.direction.east - elseif factorio_direction == defines.direction.north then - return defines.direction.south - elseif factorio_direction == defines.direction.east then - return defines.direction.west - else - return -1 - end - else - --game.print("Returning direction: " .. math.floor(factorio_direction / 2) .. ', '.. factorio_direction) - -- For other entity types, convert Factorio's direction to 0-3 range - return factorio_direction - end -end - --- Helper function to check if a position is valid (not colliding with water or other impassable tiles) -global.utils.is_valid_connection_point = function(surface, position) - -- Get the tile at the position - local tile = surface.get_tile(position.x, position.y) - - -- Check if the tile is water or other impassable tiles - local invalid_tiles = { - ["water"] = true, - ["deepwater"] = true, - ["water-green"] = true, - ["deepwater-green"] = true, - ["water-shallow"] = true, - ["water-mud"] = true, - } - - -- Return false if the tile is invalid, true otherwise - return not invalid_tiles[tile.name] -end - --- Helper function to filter connection points -local function filter_connection_points(entity, points) - if not points then return nil end - - local filtered_points = {} - for _, point in ipairs(points) do - if global.utils.is_valid_connection_point(entity.surface, point) then - table.insert(filtered_points, point) - end - end - - -- If all points were filtered out, return nil - if #filtered_points == 0 then - return nil - end - - return filtered_points -end - ----- Modified pipe position functions to include filtering ---local function get_pipe_positions_filtered(entity) --- local positions = global.utils.get_generator_connection_positions(entity) --- return filter_connection_points(entity, positions) ---end --- ---local function get_pumpjack_pipe_position_filtered(entity) --- local positions = global.utils.get_pumpjack_connection_points(entity) --- return filter_connection_points(entity, positions) ---end --- ---local function get_boiler_pipe_positions_filtered(entity) --- local positions = global.utils.get_boiler_connection_points(entity) --- --- -- Special handling for boiler since it has a different structure --- if not positions then return nil end --- --- local filtered = { --- water_inputs = filter_connection_points(entity, positions.water_inputs), --- steam_output = positions.steam_output -- Usually steam output doesn't need filtering as it connects to pipes above ground --- } --- --- -- If all water inputs were filtered out, return nil --- if not filtered.water_inputs or #filtered.water_inputs == 0 then --- return nil --- end --- --- return filtered ---end --- ---local function get_offshore_pump_pipe_position_filtered(entity) --- local positions = global.utils.get_offshore_pump_connection_points(entity) --- return filter_connection_points(entity, positions) ---end - - -global.entity_status_names = { - [defines.entity_status.working] = "working", - [defines.entity_status.normal] = "normal", - [defines.entity_status.no_power] = "no_power", - [defines.entity_status.low_power] = "low_power", - [defines.entity_status.no_fuel] = "no_fuel", - [defines.entity_status.disabled_by_control_behavior] = "disabled_by_control_behavior", - [defines.entity_status.opened_by_circuit_network] = "opened_by_circuit_network", - [defines.entity_status.closed_by_circuit_network] = "closed_by_circuit_network", - [defines.entity_status.disabled_by_script] = "disabled_by_script", - [defines.entity_status.marked_for_deconstruction] = "marked_for_deconstruction", - [defines.entity_status.not_plugged_in_electric_network] = "not_plugged_in_electric_network", - [defines.entity_status.networks_connected] = "networks_connected", - [defines.entity_status.networks_disconnected] = "networks_disconnected", - [defines.entity_status.charging] = "charging", - [defines.entity_status.discharging] = "discharging", - [defines.entity_status.fully_charged] = "fully_charged", - [defines.entity_status.out_of_logistic_network] = "out_of_logistic_network", - [defines.entity_status.no_recipe] = "no_recipe", - [defines.entity_status.no_ingredients] = "no_ingredients", - [defines.entity_status.no_input_fluid] = "no_input_fluid", - [defines.entity_status.no_research_in_progress] = "no_research_in_progress", - [defines.entity_status.no_minable_resources] = "no_minable_resources", - [defines.entity_status.low_input_fluid] = "low_input_fluid", - [defines.entity_status.fluid_ingredient_shortage] = "fluid_ingredient_shortage", - [defines.entity_status.full_output] = "full_output", - [defines.entity_status.full_burnt_result_output] = "full_burnt_result_output", - [defines.entity_status.item_ingredient_shortage] = "item_ingredient_shortage", - [defines.entity_status.missing_required_fluid] = "missing_required_fluid", - [defines.entity_status.missing_science_packs] = "missing_science_packs", - [defines.entity_status.waiting_for_source_items] = "waiting_for_source_items", - [defines.entity_status.waiting_for_space_in_destination] = "waiting_for_space_in_destination", - [defines.entity_status.preparing_rocket_for_launch] = "preparing_rocket_for_launch", - [defines.entity_status.waiting_to_launch_rocket] = "waiting_to_launch_rocket", - [defines.entity_status.launching_rocket] = "launching_rocket", - [defines.entity_status.no_modules_to_transmit] = "no_modules_to_transmit", - [defines.entity_status.recharging_after_power_outage] = "recharging_after_power_outage", - [defines.entity_status.waiting_for_target_to_be_built] = "waiting_for_target_to_be_built", - [defines.entity_status.waiting_for_train] = "waiting_for_train", - [defines.entity_status.no_ammo] = "no_ammo", - [defines.entity_status.low_temperature] = "low_temperature", - [defines.entity_status.disabled] = "disabled", - [defines.entity_status.turned_off_during_daytime] = "turned_off_during_daytime", - [defines.entity_status.not_connected_to_rail] = "not_connected_to_rail", - [defines.entity_status.cant_divide_segments] = "cant_divide_segments", -} - -global.utils.get_entity_direction = get_entity_direction - -global.utils.serialize_entity = function(entity) - - if entity == nil then - return {} - end - --game.print("Serializing entity: " .. entity.name .. " with direction: " .. entity.direction) - local direction = entity.direction - - if direction ~= nil then - direction = get_entity_direction(entity.name, entity.direction) - else - direction = 0 - end - - - --game.print("Serialized direction: ", {skip=defines.print_skip.never}) - local serialized = { - name = "\""..entity.name.."\"", - position = entity.position, - direction = direction, - health = entity.health, - energy = entity.energy, - type = "\""..entity.type.."\"", - status = global.entity_status_names[entity.status] or "\"normal\"", - } - - if entity.grid then - serialized.grid = global.utils.serialize_equipment_grid(entity.grid) - end - --game.print(serpent.line(entity.get_inventory(defines.inventory.turret_ammo))) - serialized.warnings = get_issues(entity) - --if entity.get_inventory then - -- for i = 1, #defines.inventory do - -- local inventory = entity.get_inventory(i) - -- if inventory and #inventory > 0 then - -- serialized["inventory_" .. i] = global.utils.serialize_inventory(inventory) - -- end - -- end - --end - - - local inventory_types = { - {name = "fuel", define = defines.inventory.fuel}, - {name = "burnt_result", define = defines.inventory.burnt_result}, - {name = "inventory", define = defines.inventory.chest}, - {name = "furnace_source", define = defines.inventory.furnace_source}, - {name = "furnace_result", define = defines.inventory.furnace_result}, - {name = "furnace_modules", define = defines.inventory.furnace_modules}, - {name = "assembling_machine_input", define = defines.inventory.assembling_machine_input}, - {name = "assembling_machine_output", define = defines.inventory.assembling_machine_output}, - {name = "assembling_machine_modules", define = defines.inventory.assembling_machine_modules}, - {name = "lab_input", define = defines.inventory.lab_input}, - {name = "lab_modules", define = defines.inventory.lab_modules}, - {name = "turret_ammo", define = defines.inventory.turret_ammo} - } - - for _, inv_type in ipairs(inventory_types) do - local inventory = entity.get_inventory(inv_type.define) - if inventory then - serialized[inv_type.name] = inventory.get_contents() - end - end - - -- Add dimensions of the entity - local prototype = game.entity_prototypes[entity.name] - local collision_box = prototype.collision_box - serialized.dimensions = { - width = math.abs(collision_box.right_bottom.x - collision_box.left_top.x), - height = math.abs(collision_box.right_bottom.y - collision_box.left_top.y), - } - --- Add specific check for gun turrets - --if entity.type == "ammo-turret" then - -- local ammo_inventory = entity.get_inventory(defines.inventory.turret_ammo) - -- if ammo_inventory and #ammo_inventory > 0 then - -- serialized.ammo_inventory = global.utils.serialize_inventory(ammo_inventory) - -- end - --end - serialized.neighbours = serialize_neighbours(entity) - - - -- Add input and output locations if the entity is a transport belt - --if entity.type == "transport-belt" or entity.type == "underground-belt" then - -- -- input_position is the position upstream of the belt - -- --local direction = entity.direction - -- - -- local x, y = entity.position.x, entity.position.y - -- if entity.direction == defines.direction.north then - -- y = y + 1 - -- elseif entity.direction == defines.direction.south then - -- y = y - 1 - -- elseif entity.direction == defines.direction.east then - -- x = x - 1 - -- elseif entity.direction == defines.direction.west then - -- x = x + 1 - -- elseif entity.direction == defines.direction.northeast then - -- game.print("NORTHEAST") - -- x = x - 1 - -- y = y + 1 - -- elseif entity.direction == defines.direction.southeast then - -- x = x - 1 - -- y = y - 1 - -- elseif entity.direction == defines.direction.northwest then - -- x = x + 1 - -- y = y + 1 - -- elseif entity.direction == defines.direction.southwest then - -- x = x + 1 - -- y = y - 1 - -- end - -- - -- serialized.input_position = {x = x, y = y} - -- - -- -- output_position is the position downstream of the belt - -- local x, y = entity.position.x, entity.position.y - -- if entity.direction == defines.direction.north then - -- y = y - 1 - -- elseif entity.direction == defines.direction.south then - -- y = y + 1 - -- elseif entity.direction == defines.direction.east then - -- x = x + 1 - -- elseif entity.direction == defines.direction.west then - -- x = x - 1 - -- elseif entity.direction == defines.direction.northeast then - -- x = x + 1 - -- y = y - 1 - -- elseif entity.direction == defines.direction.southeast then - -- x = x + 1 - -- y = y + 1 - -- elseif entity.direction == defines.direction.northwest then - -- x = x - 1 - -- y = y - 1 - -- elseif entity.direction == defines.direction.southwest then - -- x = x - 1 - -- y = y + 1 - -- end - -- --create_beam_point_with_direction(global.agent_characters[1], entity.direction , {x = x, y = y}) - -- serialized.output_position = {x = x, y = y} - -- serialized.position = {x = entity.position.x, y = entity.position.y} - -- --serialized.inventory = entity.get_transport_line(1).get_contents() - -- - -- -- Get contents from both lines - -- local line1 = entity.get_transport_line(1) - -- local line2 = entity.get_transport_line(2) - -- - -- -- Calculate if belt is full at the end (can't insert more items) - -- local is_full = not line1.can_insert_at_back() and not line2.can_insert_at_back() - -- - -- serialized.belt_status = { - -- status = is_full and "full_output" or "normal" - -- } - -- - -- -- Get and merge contents from both lines - -- serialized.inventory = {} - -- local line1_contents = line1.get_contents() - -- local line2_contents = line2.get_contents() - -- - -- serialized.is_terminus = #entity.belt_neighbours["outputs"] == 0 - -- serialized.is_source = #entity.belt_neighbours["inputs"] == 0 - -- - -- for item_name, count in pairs(line1_contents) do - -- serialized.inventory[item_name] = (serialized.inventory[item_name] or 0) + count - -- end - -- for item_name, count in pairs(line2_contents) do - -- serialized.inventory[item_name] = (serialized.inventory[item_name] or 0) + count - -- end - -- - -- -- Add warning if belt is full - -- if not serialized.warnings then - -- serialized.warnings = {} - -- end - -- if is_full then - -- table.insert(serialized.warnings, "Belt output is full") - -- end - -- - -- if entity.type == "underground-belt" then - -- serialized.is_input = entity.belt_to_ground_type == "input" - -- if serialized.is_input then - -- serialized.is_terminus = entity.neighbours == nil - -- else - -- serialized.is_source = entity.neighbours == nil - -- end - -- if entity.neighbours ~= nil then - -- --game.print(serpent.block(entity.neighbours)) - -- serialized.connected_to = entity.neighbours.unit_number - -- end - -- end - --end - -- Add input and output locations if the entity is a transport belt - if entity.type == "transport-belt" or entity.type == "underground-belt" then - local x, y = entity.position.x, entity.position.y - - -- Initialize positions with default offsets based on belt direction - local input_offset = { - [defines.direction.north] = {x = 0, y = 1}, - [defines.direction.south] = {x = 0, y = -1}, - [defines.direction.east] = {x = -1, y = 0}, - [defines.direction.west] = {x = 1, y = 0} - } - - local output_offset = { - [defines.direction.north] = {x = 0, y = -1}, - [defines.direction.south] = {x = 0, y = 1}, - [defines.direction.east] = {x = 1, y = 0}, - [defines.direction.west] = {x = -1, y = 0} - } - - -- Set input position based on upstream connections - local input_pos = {x = x, y = y} - if #entity.belt_neighbours["inputs"] > 0 then - -- Use the position of the first connected input belt - local input_belt = entity.belt_neighbours["inputs"][1] - input_pos = {x = input_belt.position.x, y = input_belt.position.y} - else - -- No input connection, use default offset - local offset = input_offset[entity.direction] - input_pos.x = x + offset.x - input_pos.y = y + offset.y - end - serialized.input_position = input_pos - - -- Set output position based on downstream connections - local output_pos = {x = x, y = y} - if #entity.belt_neighbours["outputs"] > 0 then - -- Use the position of the first connected output belt - local output_belt = entity.belt_neighbours["outputs"][1] - output_pos = {x = output_belt.position.x, y = output_belt.position.y} - else - -- No output connection, use default offset - local offset = output_offset[entity.direction] - output_pos.x = x + offset.x - output_pos.y = y + offset.y - end - serialized.output_position = output_pos - - -- Store the belt's own position - serialized.position = {x = x, y = y} - - -- Handle belt contents and status - local line1 = entity.get_transport_line(1) - local line2 = entity.get_transport_line(2) - - -- Calculate if belt is full at the output - local is_full = not line1.can_insert_at_back() and not line2.can_insert_at_back() - - serialized.belt_status = { - status = is_full and "\"full_output\"" or "\"normal\"" - } - - -- Get and merge contents from both lines - serialized.inventory = {} - local line1_contents = line1.get_contents() - local line2_contents = line2.get_contents() - - -- Set terminus and source flags based on connections - serialized.is_terminus = #entity.belt_neighbours["outputs"] == 0 - serialized.is_source = #entity.belt_neighbours["inputs"] == 0 - - -- Merge contents from both belt lines - for item_name, count in pairs(line1_contents) do - serialized.inventory[item_name] = (serialized.inventory[item_name] or 0) + count - end - for item_name, count in pairs(line2_contents) do - serialized.inventory[item_name] = (serialized.inventory[item_name] or 0) + count - end - - -- Add warning if belt is full - if not serialized.warnings then - serialized.warnings = {} - end - if is_full then - table.insert(serialized.warnings, "Belt output is full") - end - - -- Special handling for underground belts - if entity.type == "underground-belt" then - serialized.is_input = entity.belt_to_ground_type == "input" - if serialized.is_input then - serialized.is_terminus = entity.neighbours == nil - else - serialized.is_source = entity.neighbours == nil - end - if entity.neighbours ~= nil then - serialized.connected_to = entity.neighbours.unit_number - end - end - end - - serialized.id = entity.unit_number - -- Special handling for power poles - if entity.type == "electric-pole" then - local stats = entity.electric_network_statistics - local contents_count = 0 - for name, count in pairs(stats.input_counts) do - contents_count = contents_count + count - end - - serialized.flow_rate = contents_count --stats.get_flow_count{name=…, input=…, precision_index=} - end - - -- Add input and output positions if the entity is a splitter - if entity.type == "splitter" then - -- Initialize positions based on entity center - local x, y = entity.position.x, entity.position.y - - -- Calculate the offset for left/right positions (0.5 tiles) - local lateral_offset = 0.5 - - if entity.direction == defines.direction.north then - -- Input positions (south side) - serialized.input_positions = { - {x = x - lateral_offset, y = y + 1}, - {x = x + lateral_offset, y = y + 1} - } - -- Output positions (north side) - serialized.output_positions = { - {x = x - lateral_offset, y = y - 1}, - {x = x + lateral_offset, y = y - 1} - } - elseif entity.direction == defines.direction.south then - -- Input positions (north side) - serialized.input_positions = { - {x = x + lateral_offset, y = y - 1}, - {x = x - lateral_offset, y = y - 1} - } - -- Output positions (south side) - serialized.output_positions = { - {x = x + lateral_offset, y = y + 1}, - {x = x - lateral_offset, y = y + 1} - } - elseif entity.direction == defines.direction.east then - -- Input positions (west side) - serialized.input_positions = { - {x = x - 1, y = y - lateral_offset}, - {x = x - 1, y = y + lateral_offset} - } - -- Output positions (east side) - serialized.output_positions = { - {x = x + 1, y = y - lateral_offset}, - {x = x + 1, y = y + lateral_offset} - } - elseif entity.direction == defines.direction.west then - -- Input positions (east side) - serialized.input_positions = { - {x = x + 1, y = y + lateral_offset}, - {x = x + 1, y = y - lateral_offset} - } - -- Output positions (west side) - serialized.output_positions = { - {x = x - 1, y = y + lateral_offset}, - {x = x - 1, y = y - lateral_offset} - } - end - - -- Get the contents of both output lines - serialized.inventory = { - entity.get_transport_line(1).get_contents(), - entity.get_transport_line(2).get_contents() - } - end - - -- Add input and output locations if the entity is an inserter - if entity.type == "inserter" then - serialized.pickup_position = entity.pickup_position - serialized.drop_position = entity.drop_position - - ---- round to the nearest 0.5 - serialized.pickup_position.x = math.round(serialized.pickup_position.x * 2 ) / 2 - serialized.pickup_position.y = math.round(serialized.pickup_position.y * 2 ) / 2 - serialized.drop_position.x = math.round(serialized.drop_position.x * 2 ) / 2 - serialized.drop_position.y = math.round(serialized.drop_position.y * 2 ) / 2 - - -- if pickup_position is nil, compute it from the entity's position and direction - if not serialized.pickup_position then - --local direction = entity.direction - local x, y = entity.position.x, entity.position.y - if entity.direction == defines.direction.north then - serialized.pickup_position = {x = x, y = y - 1} - elseif entity.direction == defines.direction.south then - serialized.pickup_position = {x = x, y = y + 1} - elseif entity.direction == defines.direction.east then - serialized.pickup_position = {x = x + 1, y = y} - elseif entity.direction == defines.direction.west then - serialized.pickup_position = {x = x - 1, y = y} - end - end - - local burner = entity.burner - if burner then - add_burner_inventory(serialized, burner) - end - end - - -- Add input and output locations if the entity is a splitter - --if entity.type == "splitter" then - -- serialized.input_position = entity.input_position - -- serialized.output_position = entity.output_position - --end - - -- Add input and output locations if the entity is a pipe - if entity.type == "pipe" then - serialized.connections = {} - local fluid_name = nil - for _, connection in pairs(entity.fluidbox.get_pipe_connections(1)) do - table.insert(serialized.connections, connection.position) - end - local contents_count = 0 - for name, count in pairs(entity.fluidbox.get_fluid_system_contents(1)) do - contents_count = contents_count + count - fluid_name = "\""..name.."\"" - end - serialized.contents = contents_count - serialized.fluid = fluid_name - serialized.fluidbox_id = entity.fluidbox.get_fluid_system_id(1) - serialized.flow_rate = entity.fluidbox.get_flow(1) - end - - -- Add input and output locations if the entity is a pipe-to-ground - if entity.type == "pipe-to-ground" then - serialized.connections = {} - local fluid_name = nil - for _, connection in pairs(entity.fluidbox.get_pipe_connections(1)) do - table.insert(serialized.connections, connection.position) - end - local contents_count = 0 - for name, count in pairs(entity.fluidbox.get_fluid_system_contents(1)) do - contents_count = contents_count + count - fluid_name = "\""..name.."\"" - end - serialized.fluidbox_id = entity.fluidbox.get_fluid_system_id(1) - serialized.flow_rate = entity.fluidbox.get_flow(1) - serialized.contents = contents_count - serialized.fluid = fluid_name - --serialized.input_position = entity.fluidbox.get_connections(1)[1].position - --serialized.output_position = entity.fluidbox.get_connections(2)[1].position - end - - -- Add input and output locations if the entity is a pump - if entity.type == "pump" then - serialized.input_position = entity.fluidbox.get_connections(1)[1].position - serialized.output_position = entity.fluidbox.get_connections(2)[1].position - end - - -- Add the current research to the lab - if entity.name == "lab" then - if global.agent_characters[1].force.current_research ~= nil then - serialized.research = global.agent_characters[1].force.current_research.name - else - serialized.research = nil - end - end - - -- Add input and output locations if the entity is a offshore pump - if entity.type == "offshore-pump" then - local burner = entity.burner - if burner then - add_burner_inventory(serialized, burner) - end - end - - - if entity.name == "oil-refinery" then - local x, y = entity.position.x, entity.position.y - serialized.input_connection_points = {} - serialized.output_connection_points = {} - - local recipe = entity.get_recipe() - local mappings = global.utils.get_refinery_fluid_mappings(entity, recipe) - if mappings then - serialized.input_connection_points = mappings.inputs - serialized.output_connection_points = mappings.outputs - end - --if entity.direction == defines.direction.north then - -- -- Two crude oil inputs at the bottom - -- table.insert(serialized.input_connection_points, - -- {x = x + 1, y = y + 3 - -- }) - -- table.insert(serialized.input_connection_points, - -- {x = x - 1, y = y + 3 - -- }) - -- -- Three outputs at the top (petroleum, light oil, heavy oil) - -- table.insert(serialized.output_connection_points, - -- {x = x - 2, y = y - 3 - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x, y = y - 3 - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x + 2, y = y - 3 - -- }) - --elseif entity.direction == defines.direction.south then - -- -- Two crude oil inputs at the top - -- table.insert(serialized.input_connection_points, - -- {x = x + 1, y = y - 3 - -- }) - -- table.insert(serialized.input_connection_points, - -- {x = x - 1, y = y - 3 - -- }) - -- -- Three outputs at the bottom - -- table.insert(serialized.output_connection_points, - -- {x = x - 2, y = y + 3 - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x, y = y + 3 - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x + 2, y = y + 3 - -- }) - --elseif entity.direction == defines.direction.east then - -- -- Two crude oil inputs on the left - -- table.insert(serialized.input_connection_points, - -- {x = x - 3, y = y + 1 - -- }) - -- table.insert(serialized.input_connection_points, - -- {x = x - 3, y = y - 1 - -- }) - -- -- Three outputs on the right - -- table.insert(serialized.output_connection_points, - -- {x = x + 3, y = y - 2 - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x + 3, y = y - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x + 3, y = y + 2 - -- }) - --elseif entity.direction == defines.direction.west then - -- -- Two crude oil inputs on the right - -- table.insert(serialized.input_connection_points, - -- {x = x + 3, y = y + 1 - -- }) - -- table.insert(serialized.input_connection_points, - -- {x = x + 3, y = y - 1 - -- }) - -- -- Three outputs on the left - -- table.insert(serialized.output_connection_points, - -- {x = x - 3, y = y - 2 - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x - 3, y = y - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x - 3, y = y + 2 - -- }) - --end - - ---- Filter out any invalid connection points - --local filtered_input_points = {} - --for _, point in ipairs(serialized.input_connection_points) do - -- if global.utils.is_valid_connection_point(game.surfaces[1], point.position) then - -- table.insert(filtered_input_points, point.positio) - -- end - --end - --serialized.input_connection_points = filtered_input_points - -- - --local filtered_output_points = {} - --for _, point in ipairs(serialized.output_connection_points) do - -- if global.utils.is_valid_connection_point(game.surfaces[1], point.position) then - -- table.insert(filtered_output_points, point.position) - -- end - --end - --serialized.output_connection_points = filtered_output_points - end - - if entity.name == "chemical-plant" then - local x, y = entity.position.x, entity.position.y - serialized.input_connection_points = {} - serialized.output_connection_points = {} - - local recipe = entity.get_recipe() - local mappings = global.utils.get_chemical_plant_fluid_mappings(entity, recipe) - if mappings then - serialized.input_connection_points = mappings.inputs - serialized.output_connection_points = mappings.outputs - end - - --if direction == defines.direction.north then - -- -- Input pipes at the bottom - -- table.insert(serialized.input_connection_points, - -- {x = x - 1, y = y + 1.5 - -- }) - -- table.insert(serialized.input_connection_points, - -- {x = x + 1, y = y + 1.5 - -- }) - -- -- Output pipes at the top - -- table.insert(serialized.output_connection_points, - -- {x = x - 1, y = y - 1.5 - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x + 1, y = y - 1.5 - -- }) - --elseif direction == defines.direction.south then - -- -- Input pipes at the top - -- table.insert(serialized.input_connection_points, - -- {x = x - 1, y = y - 1.5 - -- }) - -- table.insert(serialized.input_connection_points, - -- {x = x + 1, y = y - 1.5 - -- }) - -- -- Output pipes at the bottom - -- table.insert(serialized.output_connection_points, - -- {x = x - 1, y = y + 1.5 - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x + 1, y = y + 1.5 - -- }) - --elseif direction == defines.direction.east then - -- -- Input pipes on the left - -- table.insert(serialized.input_connection_points, - -- {x = x - 1.5, y = y - 1 - -- }) - -- table.insert(serialized.input_connection_points, - -- {x = x - 1.5, y = y + 1, - -- }) - -- -- Output pipes on the right - -- table.insert(serialized.output_connection_points, - -- {x = x + 1.5, y = y - 1, - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x + 1.5, y = y + 1 - -- }) - --elseif direction == defines.direction.west then - -- -- Input pipes on the right - -- table.insert(serialized.input_connection_points, - -- {x = x + 1.5, y = y - 1, - -- }) - -- table.insert(serialized.input_connection_points, - -- {x = x + 1.5, y = y + 1 - -- }) - -- -- Output pipes on the left - -- table.insert(serialized.output_connection_points, - -- {x = x - 1.5, y = y - 1 - -- }) - -- table.insert(serialized.output_connection_points, - -- {x = x - 1.5, y = y + 1 - -- }) - --end - - -- Filter out any invalid connection points - local filtered_input_points = {} - for _, point in ipairs(serialized.input_connection_points) do - if global.utils.is_valid_connection_point(game.surfaces[1], point) then - table.insert(filtered_input_points, point) - end - end - serialized.input_connection_points = filtered_input_points - - -- Filter out any invalid connection points - local filtered_output_points = {} - for _, point in ipairs(serialized.output_connection_points) do - if global.utils.is_valid_connection_point(game.surfaces[1], point) then - table.insert(filtered_output_points, point) - end - end - serialized.output_connection_points = filtered_output_points - - end - - - if entity.type == "storage-tank" then - -- Get and filter connection points - local connection_points = global.utils.get_storage_tank_connection_points(entity) - local filtered_points = {} - - -- Filter out invalid connection points (e.g., those in water) - for _, point in ipairs(connection_points) do - if global.utils.is_valid_connection_point(entity.surface, point) then - table.insert(filtered_points, point) - end - end - - -- Add connection points to serialized data - serialized.connection_points = filtered_points - - -- Add fluid box information - if entity.fluidbox and #entity.fluidbox > 0 then - game.print("There is a fluidbox") - local fluid = entity.fluidbox[1] - if fluid then - serialized.fluid = string.format("\"%s\"", fluid.name) - serialized.fluid_amount = fluid.amount - serialized.fluid_temperature = fluid.temperature - serialized.fluid_system_id = entity.fluidbox.get_fluid_system_id(1) - end - end - - -- Add warning if some connection points were filtered - if #filtered_points < #connection_points then - if not serialized.warnings then - serialized.warnings = {} - end - table.insert(serialized.warnings, "\"some connection points were filtered due to being blocked by water\"") - end - end - - - if entity.type == "assembling-machine" then - local x, y = entity.position.x, entity.position.y - serialized.connection_points = {} - - -- Assembling machine connection points are similar to chemical plants - if entity.direction == defines.direction.north then - table.insert(serialized.connection_points, - {x = x, y = y - 2 - }) - elseif entity.direction == defines.direction.south then - table.insert(serialized.connection_points, - {x = x, y = y + 2 - }) - elseif entity.direction == defines.direction.east then - table.insert(serialized.connection_points, - {x = x + 2, y = y - }) - elseif entity.direction == defines.direction.west then - table.insert(serialized.connection_points, - {x = x - 2, y = y - }) - end - - -- Filter out any invalid connection points - local filtered_connection_points = {} - for _, point in ipairs(serialized.connection_points) do - game.print(serpent.line(point)) - if global.utils.is_valid_connection_point(game.surfaces[1], point) then - table.insert(filtered_connection_points, point) - end - end - - serialized.connection_points = filtered_connection_points - end - -- Add tile dimensions of the entity - serialized.tile_dimensions = { - tile_width = prototype.tile_width, - tile_height = prototype.tile_height, - } - - -- Add drop position if the entity is a mining drill - --game.print("Entity type: " .. entity.type) - --game.print("Entity has burner: " .. tostring(entity.burner ~= nil)) - --game.print("Burner is burning: " .. tostring(entity.burner and entity.burner.currently_burning ~= nil)) - - --if entity.type == "mining-drill" then - -- serialized.drop_position = { - -- x = entity.drop_position.x, - -- y = entity.drop_position.y - -- } - -- serialized.drop_position.x = math.round(serialized.drop_position.x * 2 ) / 2 - -- serialized.drop_position.y = math.round(serialized.drop_position.y * 2 ) / 2 - -- game.print("Mining drill drop position: " .. serpent.line(serialized.drop_position)) - -- local burner = entity.burner - -- if burner then - -- add_burner_inventory(serialized, burner) - -- end - --end - - if entity.type == "mining-drill" then - serialized.drop_position = { - x = entity.drop_position.x, - y = entity.drop_position.y - } - serialized.drop_position.x = math.round(serialized.drop_position.x * 2) / 2 - serialized.drop_position.y = math.round(serialized.drop_position.y * 2) / 2 - -- game.print("Mining drill drop position: " .. serpent.line(serialized.drop_position)) - - -- Get the mining area - local prototype = game.entity_prototypes[entity.name] - local mining_area = prototype.mining_drill_radius * 2 - - local position = entity.position - - -- Initialize resources table - serialized.resources = {} - - -- Calculate the area to check based on mining drill radius - local start_x = position.x - mining_area/2 - local start_y = position.y - mining_area/2 - local end_x = position.x + mining_area/2 - local end_y = position.y + mining_area/2 - - local resources = game.surfaces[1].find_entities_filtered{ - area = {{start_x, start_y}, {end_x, end_y}}, - type = "resource", - } - - for _, resource in pairs(resources) do - if not serialized.resources[resource.name] then - serialized.resources[resource.name] = { - name = "\""..resource.name.."\"", - count = 0, - } - end - - -- Add the resource amount and position - serialized.resources[resource.name].count = serialized.resources[resource.name].count + resource.amount - end - - - -- Convert resources table to array for consistent ordering - local resources_array = {} - for _, resource_data in pairs(serialized.resources) do - table.insert(resources_array, resource_data) - end - serialized.resources = resources_array - - -- Add mining status - if #resources_array == 0 then - serialized.status = "\"no_minable_resources\"" - if not serialized.warnings then serialized.warnings = {} end - table.insert(serialized.warnings, "\"nothing to mine\"") - end - - -- Add burner info if applicable - local burner = entity.burner - if burner then - add_burner_inventory(serialized, burner) - end - end - - -- Add recipes if the entity is a crafting machine - if entity.type == "assembling-machine" or entity.type == "furnace" then - if entity.get_recipe() then - serialized.recipe = global.utils.serialize_recipe(entity.get_recipe()) - end - end - - -- Add fluid input point if the entity is a boiler - if entity.type == "boiler" then - local burner = entity.burner - if burner then - add_burner_inventory(serialized, burner) - end - - --local direction = entity.direction - local x, y = entity.position.x, entity.position.y - - if entity.direction == defines.direction.north then - -- game.print("Boiler direction is north") - serialized.connection_points = {{x = x - 2, y = y + 0.5}, {x = x + 2, y = y + 0.5}} - serialized.steam_output_point = {x = x, y = y - 1.5} - elseif entity.direction == defines.direction.south then - -- game.print("Boiler direction is south") - serialized.connection_points = {{x = x - 2, y = y - 0.5}, {x = x + 2, y = y - 0.5}} - serialized.steam_output_point = {x = x, y = y + 1.5} - elseif entity.direction == defines.direction.east then - -- game.print("Boiler direction is east") - serialized.connection_points = {{x = x - 0.5, y = y - 2}, {x = x - 0.5, y = y + 2}} - serialized.steam_output_point = {x = x + 1.5, y = y} - elseif entity.direction == defines.direction.west then - -- game.print("Boiler direction is west") - serialized.connection_points = {{x = x + 0.5, y = y - 2}, {x = x + 0.5, y = y + 2}} - serialized.steam_output_point = {x = x - 1.5, y = y} - end - end - - if entity.type == "rocket-silo" then - -- Basic rocket silo properties - serialized.rocket_parts = 0 -- Will be updated with actual count - serialized.rocket_progress = 0.0 -- Will be updated with actual progress - serialized.launch_count = entity.launch_count or 0 - - -- Get the current rocket details if one exists - --local rocket_inventory = entity.get_inventory(defines.inventory.rocket_silo_rocket) - --local rocket = entity.rocket or nil - - --if rocket then - -- -- If there's a rocket, get its state - -- serialized.rocket = { - -- status = global.entity_status_names[rocket.status] or "normal", - -- launch_progress = rocket.launch_progress * 100.0 -- Convert to percentage - -- } - -- - -- -- Check for payload - -- local payload_inventory = rocket.get_inventory(defines.inventory.cargo_unit) - -- if payload_inventory and not payload_inventory.is_empty() then - -- serialized.rocket.payload = payload_inventory.get_contents() - -- end - --end - - -- Get part construction progress - local parts_inventory = entity.get_inventory(defines.inventory.rocket_silo_input) - if parts_inventory then - serialized.rocket_parts = parts_inventory.get_item_count("rocket-part") - -- Each rocket needs 100 parts, calculate progress - serialized.rocket_progress = (serialized.rocket_parts / 100.0) * 100.0 - end - - -- Get input inventories for rocket components - local rocket_inventory = { - rocket_part = entity.get_inventory(defines.inventory.rocket_silo_input), - result = entity.get_inventory(defines.inventory.rocket_silo_output) - } - - -- Serialize the component inventories - for name, inventory in pairs(rocket_inventory) do - if inventory and not inventory.is_empty() then - serialized[name .. "_inventory"] = inventory.get_contents() - end - end - - -- Update status based on rocket state - if serialized.rocket then - if serialized.rocket.launch_progress > 0 then - serialized.status = "\"launching_rocket\"" - elseif serialized.rocket.payload then - serialized.status = "\"waiting_to_launch_rocket\"" - end - elseif serialized.rocket_parts < 100 then - if serialized.rocket_parts > 0 then - serialized.status = "\"preparing_rocket_for_launch\"" - end - end - - -- Add warnings based on state - if not serialized.warnings then - serialized.warnings = {} - end - if serialized.rocket_parts < 100 and serialized.rocket_parts > 0 then - table.insert(serialized.warnings, "\"waiting for rocket parts\"") - elseif serialized.status == "\"waiting_to_launch_rocket\"" then - table.insert(serialized.warnings, "\"ready to launch\"") - end - end - - if entity.type == "solar-panel" then - serialized.electric_output_flow_limit = entity.electric_output_flow_limit - end - - if entity.type == 'accumulator' then - --serialized.energy_source = entity.energy_source - --serialized.power_usage = entity.power_usage - --serialized.emissions = entity.emissions - serialized.energy = entity.energy - end - - if entity.type == "generator" then - serialized.connection_points = global.utils.get_generator_connection_positions(entity) - serialized.energy_generated_last_tick = entity.energy_generated_last_tick - --serialized.power_production = entity.power_production - end - - if entity.name == "pumpjack" then - serialized.connection_points = global.utils.get_pumpjack_connection_points(entity) - end - - -- Add fuel and input ingredients if the entity is a furnace or burner - if entity.type == "furnace" or entity.type == "burner" then - local burner = entity.burner - if burner then - add_burner_inventory(serialized, burner) - end - local input_inventory = entity.get_inventory(defines.inventory.furnace_source) - if input_inventory and #input_inventory > 0 then - serialized.input_inventory = {} - for i = 1, #input_inventory do - local item = input_inventory[i] - if item and item.valid_for_read then - table.insert(serialized.input_inventory, {name = "\""..item.name.."\"", count = item.count}) - end - end - end - end - - -- Add fluid box if the entity is an offshore pump - if entity.type == "offshore-pump" then - serialized.connection_points = global.utils.get_offshore_pump_connection_points(entity) - end - - -- If entity has a fluidbox - if entity.fluidbox then - local fluid_box = entity.fluidbox - if fluid_box and #fluid_box > 0 then - serialized.fluid_box = {} - for i = 1, #fluid_box do - -- game.print("Fluid!") - local fluid = fluid_box[i] - if fluid then - table.insert(serialized.fluid_box, {name = "\""..fluid.name.."\"", amount = fluid.amount, temperature = fluid.temperature}) - end - end - end - end - - -- Add fluid handler status check - if is_fluid_handler(entity.type) then - -- Check if the entity has a fluidbox - if not entity.fluidbox or #entity.fluidbox == 0 then - serialized.status = "\"not_connected\"" - if not serialized.warnings then - serialized.warnings = {} - end - table.insert(serialized.warnings, "\"missing fluid connection\"") - else - -- Additional fluid-specific checks - local fluid_systems = {} - local has_fluid = false - local fluid_contents = nil - for i = 1, #entity.fluidbox do - if entity.fluidbox[i] then - local system_id = entity.fluidbox.get_fluid_system_id(i) - if system_id then - table.insert(fluid_systems, system_id) - end - has_fluid = true - fluid_contents = "\""..entity.fluidbox[i].name.."\"" - break - end - end - serialized.fluid_systems = fluid_systems - - if not has_fluid then - serialized.status = "not_connected" - if not serialized.warnings then - serialized.warnings = {} - end - table.insert(serialized.warnings, "\"no fluid present in connections\"") - else - serialized.fluid = fluid_contents - end - --serialized.fluidbox = global.utils.serialize_fluidbox(entity.fluidbox) - end - end - - if entity.electric_network_id then - serialized.electrical_id = entity.electric_network_id - end - - serialized.direction = get_inverse_entity_direction(entity.name, entity.direction) --api_direction_map[entity.direction] - -- Post-process connection points if they exist - if serialized.connection_points then - local filtered_points = {} - for _, point in ipairs(serialized.connection_points) do - if global.utils.is_valid_connection_point(game.surfaces[1], point) then - table.insert(filtered_points, point) - end - end - - -- Update connection points or remove if all were filtered - if #filtered_points > 0 then - serialized.connection_points = filtered_points - else - serialized.connection_points = nil - end - - -- Add warning if points were filtered - if not serialized.warnings then - serialized.warnings = {} - end - - if serialized.connection_points ~= nil then - if #filtered_points < #serialized.connection_points then - table.insert(serialized.warnings, "\"some connection points were filtered due to being blocked by water\"") - end - end - end - - -- Handle special case for boilers which have separate steam output points - if serialized.steam_output_point then - if not global.utils.is_valid_connection_point(game.surfaces[1], serialized.steam_output_point) then - serialized.steam_output_point = nil - if not serialized.warnings then - serialized.warnings = {} - end - table.insert(serialized.warnings, "\"steam output point was filtered due to being blocked by water\"") - end - end - - return serialized -end diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/util.lua b/fle/env/factorio/scenarios/default_lab_scenario/script/utils/util.lua deleted file mode 100644 index c383f2033..000000000 --- a/fle/env/factorio/scenarios/default_lab_scenario/script/utils/util.lua +++ /dev/null @@ -1,690 +0,0 @@ -util = -{ - table = {} -} - --- Helper to check if a tile is water -function is_water_tile(tile_name) - return tile_name == "water" or - tile_name == "deepwater" or - tile_name == "water-green" or - tile_name == "deepwater-green" or - tile_name == "water-shallow" or - tile_name == "water-mud" -end - -function table.deepcopy(object) - local lookup_table = {} - local function _copy(object) - if type(object) ~= "table" then - return object - -- don't copy factorio rich objects - elseif object.__self then - return object - elseif lookup_table[object] then - return lookup_table[object] - end - local new_table = {} - lookup_table[object] = new_table - for index, value in pairs(object) do - new_table[_copy(index)] = _copy(value) - end - return setmetatable(new_table, getmetatable(object)) - end - return _copy(object) -end - -function table.compare( tbl1, tbl2 ) - if tbl1 == tbl2 then return true end - for k, v in pairs( tbl1 ) do - if type(v) == "table" and type(tbl2[k]) == "table" then - if not table.compare( v, tbl2[k] ) then return false end - else - if ( v ~= tbl2[k] ) then return false end - end - end - for k, v in pairs( tbl2 ) do - if tbl1[k] == nil then return false end - end - return true -end - -util.table.deepcopy = table.deepcopy -util.table.compare = table.compare -util.copy = util.table.deepcopy - -function util.distance(position1, position2) - local x1 = position1[1] or position1.x - local y1 = position1[2] or position1.y - local x2 = position2[1] or position2.x - local y2 = position2[2] or position2.y - return ((x1 - x2) ^ 2 + (y1 - y2) ^ 2) ^ 0.5 -end - -function util.positiontostr(pos) - return string.format("[%g, %g]", pos[1] or pos.x, pos[2] or pos.y) -end - -function util.formattime(ticks) - local seconds = ticks / 60 - local minutes = math.floor((seconds)/60) - local seconds = math.floor(seconds - 60*minutes) - return string.format("%d:%02d", minutes, seconds) -end - -function util.color(hex) -- supports 'rrggbb', 'rgb', 'rrggbbaa', 'rgba', 'ww', 'w' - local function h(i,j) - return j and tonumber("0x"..hex:sub(i,j)) / 255 or tonumber("0x"..hex:sub(i,i)) / 15 - end - - hex = hex:gsub("#","") - return #hex == 6 and {r = h(1,2), g = h(3,4), b = h(5,6)} - or #hex == 3 and {r = h(1), g = h(2), b = h(3)} - or #hex == 8 and {r = h(1,2), g = h(3,4), b = h(5,6), a = h(7,8)} - or #hex == 4 and {r = h(1), g = h(2), b = h(3), a = h(4)} - or #hex == 2 and {r = h(1,2), g = h(1,2), b = h(1,2)} - or #hex == 1 and {r = h(1), g = h(1), b = h(1)} - or {r=1, g=1, b=1} -end - -function util.premul_color(color) - local r = color.r or color[1] - local g = color.g or color[2] - local b = color.b or color[3] - local a = color.a or color[4] or 1 - return - { - r = r and (r * a), - g = g and (g * a), - b = b and (b * a), - a = a - } -end - -function util.mix_color(c1, c2) - return - { - (c1.r or c1[1] or 0) * (c2.r or c2[1] or 0), - (c1.g or c1[2] or 0) * (c2.g or c2[2] or 0), - (c1.b or c1[3] or 0) * (c2.b or c2[3] or 0), - (c1.a or c1[4] or 1) * (c2.a or c2[4] or 1) - } -end - -function util.multiply_color(c1, n) - return - { - (c1.r or c1[1] or 0) * (n or 0), - (c1.g or c1[2] or 0) * (n or 0), - (c1.b or c1[3] or 0) * (n or 0), - (c1.a or c1[4] or 1) * (n or 1) - } -end - -function util.get_color_with_alpha(color, alpha, normalized_alpha) - local new_color = - { - r = color.r or color[1] or 0, - g = color.g or color[2] or 0, - b = color.b or color[3] or 0 - } - new_color.a = normalized_alpha and (new_color.r > 1 or new_color.g > 1 or new_color.b > 1) and alpha * 255 or alpha - return new_color -end - -function util.moveposition(position, direction, distance) - - if direction == defines.direction.north then - return {position[1], position[2] - distance} - end - - if direction == defines.direction.south then - return {position[1], position[2] + distance} - end - - if direction == defines.direction.east then - return {position[1] + distance, position[2]} - end - - if direction == defines.direction.west then - return {position[1] - distance, position[2]} - end -end - -function util.oppositedirection(direction) - if not tonumber(direction) then error(direction .. " is not a valid direction") end - return (direction + 4) % 8 -end - -function util.multiplystripes(count, stripes) - local ret = {} - for _, stripe in ipairs(stripes) do - for _ = 1, count do - ret[#ret + 1] = stripe - end - end - return ret -end - -function util.by_pixel(x,y) - return {x / 32, y / 32} -end - -function util.by_pixel_hr(x,y) - return {x / 64, y / 64} -end - -function util.foreach_sprite_definition(table_, fun_) - --for k, tab in pairs(table_) do - fun_(table_) - if table_.hr_version then - fun_(table_.hr_version) - end - --end - return table_ -end - -function util.add_shift(a, b) - if not (a and b) then - return a or b - end - return { a[1] + b[1], a[2] + b[2] } -end - -function util.add_shift_offset(offset_, table_) - return - util.foreach_sprite_definition(table_, function(tab) - tab.shift = util.add_shift(tab.shift, offset_) - end) -end - -function util.mul_shift(shift, scale) - if not (shift and scale) then - return shift - end - return {shift[1] * scale, shift[2] * scale} -end - -function util.format_number(amount, append_suffix) - local suffix = "" - if append_suffix then - local suffix_list = - { - ["T"] = 1000000000000, - ["B"] = 1000000000, - ["M"] = 1000000, - ["k"] = 1000 - } - for letter, limit in pairs (suffix_list) do - if math.abs(amount) >= limit then - amount = math.floor(amount/(limit/10))/10 - suffix = letter - break - end - end - end - local formatted, k = amount - while true do - formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') - if (k==0) then - break - end - end - return formatted..suffix -end - -function util.increment(t, k, v) - t[k] = t[k] + (v or 1) -end - -function util.conditional_return(value, data) - return value and data -end - --- Recursively merges and/or deep-copies tables. --- Entries in later tables override entries in earlier ones, unless --- both entries are themselves tables, in which case they are recursively merged. --- Non-merged tables are deep-copied, so that the result is brand new. -function util.merge(tables) - local ret = {} - for i, tab in ipairs(tables) do - for k, v in pairs(tab) do - if (type(v) == "table") then - if (type(ret[k] or false) == "table") then - ret[k] = util.merge{ret[k], v} - else - ret[k] = table.deepcopy(v) - end - else - ret[k] = v - end - end - end - return ret -end - -util.insert_safe = function(entity, item_dict) - if not (entity and entity.valid and item_dict) then return end - local items = game.item_prototypes - local insert = entity.insert - for name, count in pairs (item_dict) do - if items[name] then - insert{name = name, count = count} - else - log("Item to insert not valid: "..name) - end - end -end - -util.remove_safe = function(entity, item_dict) - if not (entity and entity.valid and item_dict) then return end - local items = game.item_prototypes - local remove = entity.remove_item - for name, count in pairs (item_dict) do - if items[name] then - remove{name = name, count = count} - else - log("Item to remove not valid: "..name) - end - end -end - -util.split_whitespace = function(string) - if not string then return {} end - - local result = {} - for w in string:gmatch("%S+") do - table.insert(result, w) - end - return result -end - -util.split = function(inputstr, sep) - local result = {} - - for str in string.gmatch(inputstr, "([^"..sep.."]+)") do - table.insert(result, str) - end - - return result -end - -util.string_starts_with = function(str, start) - return str.sub(str, 1, string.len(start)) == start -end - -util.online_players = function() - log("But why?") - return game.connected_players -end - -util.clamp = function(x, lower, upper) - return math.max(lower, math.min(upper, x)) -end - -local walkable_mask = {"item-layer", "object-layer", "player-layer", "water-tile"} - -local is_walkable = function(mask) - for k, layer in pairs (walkable_mask) do - if mask[layer] then return false end - end - return true -end - -util.get_walkable_tile = function() - for name, tile in pairs (game.tile_prototypes) do - if is_walkable(tile.collision_mask) and not tile.items_to_place_this then - return name - end - end - error("No walkable tile in prototype list") -end - --- This function takes 2 icons tables, and adds the second to the first, but applies scale, shift and tint to the entire second set. --- This allows you to manipulate the entire second icons table in the same way as you would manipulate a single icon when adding to the icons table. -function util.combine_icons(icons1, icons2, inputs, default_icon_size) - scale = inputs.scale or 1 - shift = inputs.shift or {0, 0} - tint = inputs.tint or {r = 1, g = 1, b = 1, a = 1} - - local icons = table.deepcopy(icons1) - for _,icon_to_add in pairs(icons2) do - local icon = {} - icon.icon = icon_to_add.icon - icon.icon_size = icon_to_add.icon_size or default_icon_size or error("No icon size defined for icon \n"..serpent.block(icon)) - icon.scale = scale * (icon_to_add.scale or 32.0 / icon.icon_size) - icon.icon_mipmaps = icon_to_add.icon_mipmaps - if icon_to_add.shift then - icon.shift = {icon_to_add.shift[1] * scale + shift[1], icon_to_add.shift[2] * scale + shift[2]} - else - icon.shift = shift - end - if icon_to_add.tint then - icon.tint = util.mix_color(tint, icon_to_add.tint) - else - icon.tint = tint - end - table.insert(icons,icon) - end - return icons -end - -local energy_chars = -{ - k = 10^3, - K = 10^3, - M = 10^6, - G = 10^9, - T = 10^12, - P = 10^15, - E = 10^18, - Z = 10^21, - Y = 10^24 -} - -function util.technology_icon_constant_damage(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-damage.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_speed(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-speed.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_movement_speed(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-movement-speed.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_range(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-range.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_equipment(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-equipment.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_followers(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-count.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_capacity(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-capacity.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_stack_size(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-capacity.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_productivity(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-mining-productivity.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_braking_force(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-braking-force.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end -function util.technology_icon_constant_mining(technology_icon) - local icons = - { - { - icon = technology_icon, - icon_size = 256, icon_mipmaps = 4 - }, - { - icon = "__core__/graphics/icons/technology/constants/constant-mining.png", - icon_size = 128, - icon_mipmaps = 3, - shift = {100, 100} - } - } - return icons -end - -function util.parse_energy(energy) - - local ending = energy:sub(energy:len()) - if not (ending == "J" or ending == "W") then - error(ending.. " is not a valid unit of energy") - end - - local multiplier = (ending == "W" and 1 / 60) or 1 - local magnitude = energy:sub(energy:len() - 1, energy:len() - 1) - - if tonumber(magnitude) then - return tonumber(energy:sub(1, energy:len()-1)) * multiplier - end - - multiplier = multiplier * (energy_chars[magnitude] or error(magnitude.. " is not valid magnitude")) - return tonumber(energy:sub(1, energy:len()-2)) * multiplier - -end - -function util.product_amount(product) - return product.probability * (product.amount or ((product.amount_min + product.amount_max) / 2)) -end - -function util.empty_sprite(animation_length) - return - { - filename = "__core__/graphics/empty.png", - priority = "extra-high", - width = 1, - height = 1, - frame_count = 1, - repeat_count = animation_length, - direction_count = 1 - } -end - -function util.draw_as_glow(layer) - layer.draw_as_glow = true - if layer.hr_version then - layer.hr_version.draw_as_glow = true - end - return layer -end - -function util.remove_tile_references(data, array_of_tiles_to_remove) - -- Does not handle: - -- explicit tile filters in "selection-tool" items - -- ItemPrototype::place_as_tile - -- TilePrototype::next_direction - -- TilePrototype::transition_merges_with_tile - -- general tile transitions, only removes tile names from water_tile_type_names - - if (type(array_of_tiles_to_remove) ~= "table") then - error("The second parameter of util.remove_tile_reference() is expected to be array of strings.") - end - - local tiles_to_remove = {} - for i, n in pairs(array_of_tiles_to_remove) do - tiles_to_remove[n] = true - end - - local remove_from_mapping = function(mapping) - if not mapping then - return - end - - for _, item in pairs(mapping) do - if item.tiles then - for i, t in pairs(item.tiles) do - if t and tiles_to_remove[t] then - item.tiles[i] = nil - end - end - end - end - end - - for k,e in pairs(data.raw["character"]) do - remove_from_mapping(e.footstep_particle_triggers) - remove_from_mapping(e.synced_footstep_particle_triggers) - remove_from_mapping(e.footprint_particles) - end - - for k,e in pairs(data.raw["car"]) do - remove_from_mapping(e.track_particle_triggers) - end - - if data.raw["fire"] then - for k, fire in pairs(data.raw["fire"]) do - if fire.burnt_patch_alpha_variations then - for i, v in pairs(fire.burnt_patch_alpha_variations) do - if v and v.tile and tiles_to_remove[v.tile] then - fire.burnt_patch_alpha_variations[i] = nil - end - end - end - end - end - - if water_tile_type_names then - for i = #water_tile_type_names, 1, -1 do - if tiles_to_remove[water_tile_type_names[i]] then - table.remove(water_tile_type_names, i) - end - end - end -end - -local remove = table.remove -util.remove_from_list = function(list, value) - for k, v in pairs (list) do - if v == value then - remove(list, k) - return true - end - end - return false -end - -util.list_to_map = function(list) - local map = {} - --Here I am trusting you not to give a list of junk. - for k, value in pairs(list) do - map[value] = true - end - return map -end - -return util \ No newline at end of file diff --git a/fle/env/factorio_server.py b/fle/env/factorio_server.py index 05f60c4c4..bccb1549a 100644 --- a/fle/env/factorio_server.py +++ b/fle/env/factorio_server.py @@ -66,7 +66,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): class FactorioServer: - def __init__(self, address, tcp_port = PLATFORM_CONFIG.rcon_port, cache_scripts=True): + def __init__(self, address, tcp_port = PLATFORM_CONFIG.rcon_port, cache_scripts=True, server_docker_config=None): self.rcon_client, self.address = self.connect_to_server(address, tcp_port) self.cache_scripts = cache_scripts self.tool_hook_registry = ToolHookRegistry() @@ -78,6 +78,7 @@ def __init__(self, address, tcp_port = PLATFORM_CONFIG.rcon_port, cache_scripts= **self.lua_script_manager.lib_scripts, **self.lua_script_manager.tool_scripts, } + self.server_docker_config = server_docker_config def ensure_rcon_client(self): if not self.rcon_client: diff --git a/fle/env/run_envs.py b/fle/env/run_envs.py index 9621be788..a12c92327 100644 --- a/fle/env/run_envs.py +++ b/fle/env/run_envs.py @@ -44,6 +44,7 @@ def __init__(self): self.server_config_dir = Path(__file__).parent.resolve() / "factorio" / "config" self.mode = Mode.SAVE_BASED.value self.temp_playing_dir = "/opt/factorio/temp/currently-playing" + self.name_prefix = "factorio_" with open(self.server_config_dir / "rconpw", "r") as f: self.factorio_password = f.read().strip() @@ -165,7 +166,7 @@ async def get_scenario2map_ctr(self, instance_index: int): "Platform": self.docker_platform, } return await self.docker.containers.run( - config=config, name=f"conv_factorio_{instance_index}" + config=config, name=f"conv_{self.config.name_prefix}{instance_index}" ) async def get_server_ctr(self, instance_index: int): @@ -185,7 +186,7 @@ async def get_server_ctr(self, instance_index: int): config["Cmd"] = [self.config.scenario_name] print(config) return await self.docker.containers.create_or_replace( - config=config, name=f"factorio_{instance_index}" + config=config, name=f"{self.config.name_prefix}{instance_index}" ) async def get_containers_to_run(self): @@ -208,7 +209,7 @@ async def start(self): if self.dry_run: for i in range(self.num): - name = f"factorio_{i}" + name = f"{self.config.name_prefix}{i}" print(f"\nContainer name: {name}") print(f"Port mappings: {self._get_ports(i)}") print(f"Volume mounts: {self._get_volumes(i)}") @@ -236,20 +237,20 @@ async def start(self): async def stop(self): # Stop & remove any container whose name starts with "factorio_" - ctrs = await self.docker.containers.list(filters={"name": ["/factorio_"]}) + ctrs = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) tasks_stop = [ctr.stop() for ctr in ctrs] tasks_delete = [ctr.delete() for ctr in ctrs] await asyncio.gather(*tasks_stop) await asyncio.gather(*tasks_delete) async def restart(self): - ctrs = await self.docker.containers.list(filters={"name": ["/factorio_"]}) + ctrs = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) tasks = [ctr.restart() for ctr in ctrs] await asyncio.gather(*tasks) async def hot_reload_scenario(self): # Sync scenario files into the server's temp directory for hot-reload - containers = await self.docker.containers.list(filters={"name": ["/factorio_"]}) + containers = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) async def sync_container(ctr): cmd = ( @@ -278,9 +279,8 @@ async def sync_container(ctr): async def get_local_container_ips(self) -> tuple[List[str], List[int], List[int]]: """Get IP addresses of running Factorio containers in the local Docker setup.""" # Get container IDs for factorio containers - containers = await self.docker.containers.list(filters={"name": ["/factorio_"]}) + containers = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) container_ids = [ctr.id for ctr in containers] - print(container_ids) if not container_ids or container_ids[0] == "": print("No running Factorio containers found") @@ -289,15 +289,12 @@ async def get_local_container_ips(self) -> tuple[List[str], List[int], List[int] ips = [] udp_ports = [] tcp_ports = [] - for container in containers: - # Get container details in JSON format - container_info = await container.show() - # print(container_info) - - # Get host ports for UDP game port + tasks = [ctr.show() for ctr in containers] + container_infos = await asyncio.gather(*tasks) + + for container_info in container_infos: ports = container_info["NetworkSettings"]["Ports"] - # Find the UDP port mapping for port, bindings in ports.items(): if "/udp" in port and bindings: udp_port = bindings[0]["HostPort"] From c938a7c6d92169fd6e8241ecb756337ab1fa57d0 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 31 Jul 2025 16:59:34 +0530 Subject: [PATCH 32/53] restructure --- fle/agents/agent_abc.py | 2 +- fle/agents/data/run_trace.py | 2 +- .../formatters/conversation_formatter_abc.py | 2 +- .../formatters/recursive_report_formatter.py | 2 +- fle/agents/gym_agent.py | 2 +- fle/cluster/remote/factorio_server_login.py | 2 +- fle/commons/db_client.py | 2 +- fle/commons/models/__init__.py | 2 +- fle/commons/models/program.py | 2 +- fle/commons/runner.py | 298 ++++++ fle/env/__init__.py | 10 +- fle/env/a2a_namespace.py | 2 +- fle/env/{gym_env => }/environment.py | 23 +- ...{factorio_server.py => factorio_client.py} | 55 +- fle/env/{ => game}/entities.py | 0 .../factorio/config/map-gen-settings.json | 0 .../factorio/config/map-settings.json | 0 fle/env/{ => game}/factorio/config/rconpw | 0 .../factorio/config/server-settings.json | 0 fle/env/{ => game}/factorio/mods/README.md | 0 fle/env/{ => game}/factorio/mods/alerts.lua | 0 .../factorio/mods/build_checkerboard.lua | 0 fle/env/{ => game}/factorio/mods/checksum.lua | 0 .../factorio/mods/clear_entities.lua | 0 .../factorio/mods/clear_inventory.lua | 0 .../factorio/mods/connection_points.lua | 0 fle/env/{ => game}/factorio/mods/enemies.lua | 0 .../{ => game}/factorio/mods/initialise.lua | 0 .../factorio/mods/initialise_inventory.lua | 0 .../factorio/mods/priority_queue.lua | 0 .../factorio/mods/production_score.lua | 0 .../mods/recipe_fluid_connection_mappings.lua | 0 fle/env/{ => game}/factorio/mods/reset.lua | 0 .../factorio/mods/reset_position.lua | 0 .../{ => game}/factorio/mods/serialize.lua | 0 fle/env/{ => game}/factorio/mods/util.lua | 0 .../default_lab_scenario/blueprint.zip | Bin .../default_lab_scenario/control2.lua | 0 .../default_lab_scenario/description.json | 0 .../default_lab_scenario/freeplay.lua | 0 .../scenarios/default_lab_scenario/info.json | 0 .../locale/en/freeplay.cfg | 0 .../locale/en/level-01-locale.cfg | 0 .../default_lab_scenario/paperclips.lua | 0 .../default_lab_scenario/planet1.png | Bin .../scenarios/default_lab_scenario/script.dat | Bin .../factorio/scenarios/open_world/README.md | 0 .../models => env/game}/game_state.py | 0 fle/env/{ => game}/game_types.py | 0 fle/env/{ => game}/instance.py | 14 +- fle/env/{ => game}/lua_manager.py | 2 +- fle/env/{ => game}/namespace.py | 2 +- fle/env/{factorio => game}/tools/__init__.py | 0 .../admin/clear_collision_boxes/client.py | 0 .../admin/clear_collision_boxes/server.lua | 0 .../tools/admin/clear_entities/client.py | 0 .../tools/admin/clear_entities/server.lua | 0 .../admin/extend_collision_boxes/client.py | 2 +- .../admin/extend_collision_boxes/server.lua | 0 .../admin/get_factory_centroid/client.py | 2 +- .../admin/get_factory_centroid/server.lua | 0 .../admin/get_factory_centroid/server2.lua | 0 .../tools/admin/get_messages/server.lua | 0 .../tools/admin/get_path/client.py | 2 +- .../tools/admin/get_path/server.lua | 0 .../admin/get_production_stats/client.py | 0 .../admin/get_production_stats/server.lua | 0 .../tools/admin/inspect_entities/deprecated | 0 .../tools/admin/inspect_entities/server.lua | 0 .../tools/admin/load_blueprint/client.py | 2 +- .../tools/admin/load_blueprint/server.lua | 0 .../tools/admin/load_entity_state/client.py | 0 .../tools/admin/load_entity_state/server.lua | 0 .../tools/admin/load_research_state/client.py | 0 .../admin/load_research_state/server.lua | 0 .../tools/admin/message_utils.py | 0 .../tools/admin/production_stats/client.py | 0 .../tools/admin/production_stats/server.lua | 0 .../admin/regenerate_resources/client.py | 0 .../admin/regenerate_resources/server.lua | 0 .../tools/admin/render/agent.md | 0 .../tools/admin/render/client.py | 0 .../layers/connection_layers_renderer.py | 2 +- .../render/layers/entities_layer_renderer.py | 2 +- .../render/layers/grid_layer_renderer.py | 0 .../admin/render/layers/layer_renderer.py | 0 .../render/layers/marker_layers_renderer.py | 2 +- .../render/layers/natural_layer_renderer.py | 2 +- .../render/layers/resource_layer_renderer.py | 0 .../render/layers/water_layer_renderer.py | 0 .../tools/admin/render/rendered_image.py | 0 .../tools/admin/render/renderer.py | 2 +- .../tools/admin/render/server.lua | 0 .../admin/render/utils/colour_manager.py | 2 +- .../admin/render/utils/connection_renderer.py | 0 .../render/utils/electricity_renderer.py | 2 +- .../admin/render/utils/entity_categoriser.py | 0 .../admin/render/utils/image_calculator.py | 2 +- .../admin/render/utils/legend_renderer.py | 2 +- .../admin/render/utils/natural_renderer.py | 0 .../tools/admin/render/utils/render_config.py | 2 +- .../admin/render/utils/shape_renderer.py | 2 +- .../tools/admin/render/utils/tile_renderer.py | 0 .../tools/admin/render_message/client.py | 0 .../tools/admin/render_message/server.lua | 0 .../tools/admin/request_path/client.py | 2 +- .../tools/admin/request_path/server.lua | 0 .../tools/admin/save_blueprint/client.py | 2 +- .../tools/admin/save_blueprint/server.lua | 0 .../tools/admin/save_entity_state/client.py | 0 .../tools/admin/save_entity_state/server.lua | 0 .../tools/admin/save_research_state/client.py | 0 .../admin/save_research_state/server.lua | 0 fle/env/{factorio => game}/tools/agent.md | 0 .../tools/agent/can_place_entity/agent.md | 0 .../tools/agent/can_place_entity/client.py | 0 .../tools/agent/can_place_entity/server.lua | 0 .../tools/agent/connect_entities/agent.md | 0 .../tools/agent/connect_entities/client.py | 0 .../connect_entities/groupable_entities.py | 0 .../agent/connect_entities/path_result.py | 0 .../tools/agent/connect_entities/resolver.py | 2 +- .../resolvers/fluid_connection_resolver.py | 0 .../resolvers/power_connection_resolver.py | 2 +- .../transport_connection_resolver.py | 2 +- .../tools/agent/connect_entities/server.lua | 0 .../tools/agent/craft_item/agent.md | 0 .../tools/agent/craft_item/client.py | 0 .../tools/agent/craft_item/server.lua | 0 .../tools/agent/extract_item/agent.md | 0 .../tools/agent/extract_item/client.py | 2 +- .../tools/agent/extract_item/server.lua | 0 .../agent/get_connection_amount/agent.md | 0 .../agent/get_connection_amount/client.py | 2 +- .../agent/get_connection_amount/server.lua | 0 .../tools/agent/get_entities/agent.md | 0 .../tools/agent/get_entities/client.py | 2 +- .../tools/agent/get_entities/server.lua | 0 .../tools/agent/get_entity/agent.md | 0 .../tools/agent/get_entity/client.py | 2 +- .../tools/agent/get_entity/server.lua | 0 .../tools/agent/get_prototype_recipe/agent.md | 0 .../agent/get_prototype_recipe/client.py | 0 .../agent/get_prototype_recipe/server.lua | 0 .../agent/get_research_progress/agent.md | 0 .../agent/get_research_progress/client.py | 0 .../agent/get_research_progress/server.lua | 0 .../tools/agent/get_resource_patch/agent.md | 0 .../tools/agent/get_resource_patch/client.py | 2 +- .../tools/agent/get_resource_patch/server.lua | 0 .../tools/agent/harvest_resource/agent.md | 0 .../tools/agent/harvest_resource/client.py | 2 +- .../tools/agent/harvest_resource/server.lua | 0 .../tools/agent/insert_item/agent.md | 0 .../tools/agent/insert_item/client.py | 2 +- .../tools/agent/insert_item/server.lua | 0 .../tools/agent/inspect_inventory/agent.md | 0 .../tools/agent/inspect_inventory/client.py | 0 .../tools/agent/inspect_inventory/server.lua | 0 .../tools/agent/launch_rocket/agent.md | 0 .../tools/agent/launch_rocket/client.py | 2 +- .../tools/agent/launch_rocket/server.lua | 0 .../tools/agent/move_to/agent.md | 0 .../tools/agent/move_to/client.py | 4 +- .../tools/agent/move_to/server.lua | 0 .../tools/agent/nearest/agent.md | 0 .../tools/agent/nearest/client.py | 2 +- .../tools/agent/nearest/server.lua | 0 .../tools/agent/nearest_buildable/agent.md | 0 .../tools/agent/nearest_buildable/client.py | 0 .../tools/agent/nearest_buildable/server.lua | 0 .../tools/agent/pickup_entity/agent.md | 0 .../tools/agent/pickup_entity/client.py | 0 .../tools/agent/pickup_entity/server.lua | 0 .../tools/agent/place_entity/agent.md | 0 .../tools/agent/place_entity/client.py | 2 +- .../tools/agent/place_entity/server.lua | 0 .../tools/agent/place_entity_next_to/agent.md | 0 .../agent/place_entity_next_to/client.py | 2 +- .../agent/place_entity_next_to/server.lua | 0 .../tools/agent/print/agent.md | 0 .../tools/agent/print/client.py | 2 +- .../tools/agent/print/server.lua | 0 .../tools/agent/rotate_entity/agent.md | 0 .../tools/agent/rotate_entity/client.py | 2 +- .../tools/agent/rotate_entity/server.lua | 0 .../tools/agent/score/client.py | 0 .../tools/agent/score/server.lua | 0 .../tools/agent/send_message/agent.md | 0 .../tools/agent/send_message/client.py | 0 .../tools/agent/send_message/server.lua | 0 .../tools/agent/set_entity_recipe/agent.md | 0 .../tools/agent/set_entity_recipe/client.py | 2 +- .../tools/agent/set_entity_recipe/server.lua | 0 .../tools/agent/set_research/agent.md | 0 .../tools/agent/set_research/client.py | 0 .../tools/agent/set_research/server.lua | 0 .../tools/agent/shift_entity/client.py | 2 +- .../tools/agent/sleep/agent.md | 0 .../tools/agent/sleep/client.py | 0 .../tools/agent/sleep/server.lua | 0 .../{factorio => game}/tools/controller.py | 15 +- fle/env/{factorio => game}/tools/init.py | 4 +- fle/env/{factorio => game}/tools/tool.py | 8 +- fle/env/game_session.py | 67 ++ fle/env/gym_env/action.py | 2 +- fle/env/gym_env/config.py | 2 +- fle/env/gym_env/example_usage.py | 4 +- fle/env/gym_env/observation.py | 2 +- fle/env/gym_env/observation_formatter.py | 4 +- fle/env/gym_env/run_eval.py | 10 +- fle/env/protocols/mcp/repository.py | 2 +- fle/env/protocols/mcp/tools.py | 4 +- fle/env/protocols/mcp/version_control.py | 2 +- fle/env/{gym_env => }/registry.py | 13 +- fle/{eval => env}/tasks/__init__.py | 2 +- fle/{eval => env}/tasks/default_task.py | 2 +- fle/{eval => env}/tasks/task_abc.py | 2 +- .../lab_play/advanced_circuit_throughput.json | 0 .../automation_science_pack_throughput.json | 0 .../lab_play/battery_throughput.json | 0 .../chemical_science_pack_throughput.json | 0 .../lab_play/crude_oil_throughput.json | 0 .../electronic_circuit_throughput.json | 0 .../lab_play/engine_unit_throughput.json | 0 .../lab_play/inserter_throughput.json | 0 .../lab_play/iron_gear_wheel_throughput.json | 0 .../lab_play/iron_ore_throughput.json | 0 .../lab_play/iron_plate_throughput.json | 0 .../logistics_science_pack_throughput.json | 0 .../low_density_structure_throughput.json | 0 .../military_science_pack_throughput.json | 0 .../lab_play/petroleum_gas_throughput.json | 0 .../lab_play/piercing_round_throughput.json | 0 .../lab_play/plastic_bar_throughput.json | 0 .../lab_play/processing_unit_throughput.json | 0 .../production_science_pack_throughput.json | 0 .../lab_play/steel_plate_throughput.json | 0 .../lab_play/stone_wall_throughput.json | 0 .../lab_play/sufuric_acid_throughput.json | 0 .../lab_play/sulfur_throughput.json | 0 .../utility_science_pack_throughput.json | 0 .../iron_plate_throughput_distrust.json | 0 .../iron_plate_throughput_free.json | 0 .../iron_plate_throughput_impostor.json | 0 .../iron_gear_wheel_throughput_unbounded.json | 0 .../task_definitions/unbounded/open_play.json | 0 fle/{eval => env}/tasks/task_factory.py | 9 +- fle/{eval => env}/tasks/throughput_task.py | 2 +- .../tasks/unbounded_throughput_task.py | 4 +- fle/env/utils/camera.py | 2 +- fle/eval/algorithms/beam/beam_search.py | 18 +- .../algorithms/beam/beam_search_milestones.py | 6 +- fle/eval/algorithms/beam/run_milestones.py | 4 +- fle/eval/algorithms/beam/run_parallel.py | 4 +- .../independent/simple_evaluator.py | 6 +- .../independent/trajectory_runner.py | 6 +- fle/eval/algorithms/mcts/__main__chunked.py | 4 +- .../algorithms/mcts/__main__objective_mcts.py | 4 +- .../algorithms/mcts/blueprints_to_programs.py | 2 +- fle/eval/algorithms/mcts/chunked_mcts.py | 4 +- fle/eval/algorithms/mcts/evaluator.py | 8 +- fle/eval/algorithms/mcts/instance_group.py | 2 +- fle/eval/algorithms/mcts/main.py | 2 +- fle/eval/algorithms/mcts/mcts.py | 4 +- fle/eval/algorithms/mcts/mcts_factory.py | 2 +- fle/eval/algorithms/mcts/objective_mcts.py | 4 +- fle/eval/algorithms/mcts/parallel_mcts.py | 2 +- .../algorithms/mcts/parallel_mcts_config.py | 2 +- .../algorithms/mcts/parallel_planning_mcts.py | 4 +- .../mcts/parallel_supervised_config.py | 2 +- fle/eval/algorithms/mcts/plan_sampler.py | 2 +- fle/eval/algorithms/mcts/planning_mcts.py | 2 +- fle/eval/algorithms/mcts/planning_models.py | 2 +- fle/eval/algorithms/mcts/results.py | 2 +- .../algorithms/mcts/samplers/beam_sampler.py | 2 +- .../samplers/blueprint_scenario_sampler.py | 2 +- .../algorithms/mcts/samplers/db_sampler.py | 2 +- .../dynamic_reward_weighted_sampler.py | 2 +- .../mcts/samplers/kld_achievement_sampler.py | 2 +- .../mcts/supervised_task_executor_abc.py | 6 +- fle/eval/entrypoints/beam_run.py | 2 +- fle/eval/entrypoints/independent_run.py | 2 +- fle/eval/entrypoints/independent_run_a2a.py | 2 +- .../entrypoints/independent_run_vision.py | 2 +- fle/eval/entrypoints/mcts_planning_agent.py | 4 +- fle/eval/open/plots/run_results.py | 2 +- fle/eval/open/plots/run_visualiser.py | 2 +- fle/eval/open/plots/simple_run_visualiser.py | 2 +- .../plots/simple_run_visualiser_with_ticks.py | 2 +- .../plots/visualise_production_figures.py | 2 +- fle/services/db_client.py | 864 ++++++++++++++++++ .../docker_manager.py} | 34 +- fle/{env/utils => services}/rcon.py | 0 fle/{env/gym_env => }/trajectory_runner.py | 7 +- tests/actions/_test_inspect_entities.py | 2 +- tests/actions/test_can_place.py | 2 +- .../actions/test_connect_entities_dry_run.py | 2 +- tests/actions/test_extract_item.py | 2 +- tests/actions/test_get_entity.py | 2 +- tests/actions/test_harvest_resource.py | 2 +- tests/actions/test_insert_item.py | 2 +- tests/actions/test_inspect_inventory.py | 2 +- tests/actions/test_move_to.py | 2 +- tests/actions/test_nearest.py | 2 +- tests/actions/test_nearest_buildable.py | 2 +- tests/actions/test_pickup_entity.py | 2 +- tests/actions/test_place.py | 2 +- tests/actions/test_place_entity_next_to.py | 2 +- .../actions/test_place_next_to_and_rotate.py | 2 +- tests/actions/test_render.py | 2 +- tests/actions/test_request_path.py | 2 +- tests/actions/test_rotate.py | 2 +- tests/actions/test_set_entity_recipe.py | 2 +- tests/benchmarks/test_demo_video.py | 2 +- tests/benchmarks/test_elapsed_ticks.py | 2 +- .../test_blueprint_based_policies.py | 2 +- tests/blueprints/test_save_load.py | 2 +- tests/complex/test_edge_cases.py | 2 +- tests/complex/test_slow.py | 2 +- tests/connect/test_connect_pipes.py | 2 +- tests/connect/test_connect_poles.py | 2 +- tests/connect/test_connect_transport_belts.py | 2 +- .../connect/test_connect_underground_belts.py | 2 +- .../connect/test_connect_underground_pipes.py | 2 +- tests/connect/test_connect_walls.py | 2 +- tests/connect/test_fluid_handlers.py | 2 +- tests/entities/test_fluid_processors.py | 2 +- tests/entities/test_rockets.py | 2 +- tests/eval/test_game_state.py | 2 +- tests/eval/test_save_load_python_namespace.py | 2 +- ...test_auto_fueling_iron_smelting_factory.py | 2 +- tests/functional/test_auto_refilling_coal.py | 2 +- tests/functional/test_chemical_plants.py | 2 +- tests/functional/test_defence.py | 2 +- tests/functional/test_electricity_unit.py | 2 +- tests/functional/test_full_oil_chain.py | 2 +- .../test_multiple_drills_multiple_furnaces.py | 2 +- tests/functional/test_objectives.py | 2 +- tests/functional/test_research.py | 6 +- tests/functional/test_small_iron_factory.py | 2 +- tests/gym_env/test_observation.py | 2 +- tests/gym_env/test_registry.py | 2 +- tests/multiagent/test_agent_instructions.py | 2 +- tests/multiagent/test_messages.py | 4 +- tests/test_inventory_deduction.py | 2 +- tests/test_production_stats.py | 2 +- tests/test_rcon_utils.py | 2 +- tests/test_warnings.py | 2 +- 349 files changed, 1534 insertions(+), 281 deletions(-) create mode 100644 fle/commons/runner.py rename fle/env/{gym_env => }/environment.py (98%) rename fle/env/{factorio_server.py => factorio_client.py} (77%) rename fle/env/{ => game}/entities.py (100%) rename fle/env/{ => game}/factorio/config/map-gen-settings.json (100%) rename fle/env/{ => game}/factorio/config/map-settings.json (100%) rename fle/env/{ => game}/factorio/config/rconpw (100%) rename fle/env/{ => game}/factorio/config/server-settings.json (100%) rename fle/env/{ => game}/factorio/mods/README.md (100%) rename fle/env/{ => game}/factorio/mods/alerts.lua (100%) rename fle/env/{ => game}/factorio/mods/build_checkerboard.lua (100%) rename fle/env/{ => game}/factorio/mods/checksum.lua (100%) rename fle/env/{ => game}/factorio/mods/clear_entities.lua (100%) rename fle/env/{ => game}/factorio/mods/clear_inventory.lua (100%) rename fle/env/{ => game}/factorio/mods/connection_points.lua (100%) rename fle/env/{ => game}/factorio/mods/enemies.lua (100%) rename fle/env/{ => game}/factorio/mods/initialise.lua (100%) rename fle/env/{ => game}/factorio/mods/initialise_inventory.lua (100%) rename fle/env/{ => game}/factorio/mods/priority_queue.lua (100%) rename fle/env/{ => game}/factorio/mods/production_score.lua (100%) rename fle/env/{ => game}/factorio/mods/recipe_fluid_connection_mappings.lua (100%) rename fle/env/{ => game}/factorio/mods/reset.lua (100%) rename fle/env/{ => game}/factorio/mods/reset_position.lua (100%) rename fle/env/{ => game}/factorio/mods/serialize.lua (100%) rename fle/env/{ => game}/factorio/mods/util.lua (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/blueprint.zip (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/control2.lua (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/description.json (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/freeplay.lua (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/info.json (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/locale/en/freeplay.cfg (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/paperclips.lua (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/planet1.png (100%) rename fle/env/{ => game}/factorio/scenarios/default_lab_scenario/script.dat (100%) rename fle/env/{ => game}/factorio/scenarios/open_world/README.md (100%) rename fle/{commons/models => env/game}/game_state.py (100%) rename fle/env/{ => game}/game_types.py (100%) rename fle/env/{ => game}/instance.py (98%) rename fle/env/{ => game}/lua_manager.py (99%) rename fle/env/{ => game}/namespace.py (99%) rename fle/env/{factorio => game}/tools/__init__.py (100%) rename fle/env/{factorio => game}/tools/admin/clear_collision_boxes/client.py (100%) rename fle/env/{factorio => game}/tools/admin/clear_collision_boxes/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/clear_entities/client.py (100%) rename fle/env/{factorio => game}/tools/admin/clear_entities/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/extend_collision_boxes/client.py (94%) rename fle/env/{factorio => game}/tools/admin/extend_collision_boxes/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/get_factory_centroid/client.py (97%) rename fle/env/{factorio => game}/tools/admin/get_factory_centroid/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/get_factory_centroid/server2.lua (100%) rename fle/env/{factorio => game}/tools/admin/get_messages/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/get_path/client.py (97%) rename fle/env/{factorio => game}/tools/admin/get_path/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/get_production_stats/client.py (100%) rename fle/env/{factorio => game}/tools/admin/get_production_stats/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/inspect_entities/deprecated (100%) rename fle/env/{factorio => game}/tools/admin/inspect_entities/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/load_blueprint/client.py (93%) rename fle/env/{factorio => game}/tools/admin/load_blueprint/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/load_entity_state/client.py (100%) rename fle/env/{factorio => game}/tools/admin/load_entity_state/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/load_research_state/client.py (100%) rename fle/env/{factorio => game}/tools/admin/load_research_state/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/message_utils.py (100%) rename fle/env/{factorio => game}/tools/admin/production_stats/client.py (100%) rename fle/env/{factorio => game}/tools/admin/production_stats/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/regenerate_resources/client.py (100%) rename fle/env/{factorio => game}/tools/admin/regenerate_resources/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/render/agent.md (100%) rename fle/env/{factorio => game}/tools/admin/render/client.py (100%) rename fle/env/{factorio => game}/tools/admin/render/layers/connection_layers_renderer.py (98%) rename fle/env/{factorio => game}/tools/admin/render/layers/entities_layer_renderer.py (98%) rename fle/env/{factorio => game}/tools/admin/render/layers/grid_layer_renderer.py (100%) rename fle/env/{factorio => game}/tools/admin/render/layers/layer_renderer.py (100%) rename fle/env/{factorio => game}/tools/admin/render/layers/marker_layers_renderer.py (98%) rename fle/env/{factorio => game}/tools/admin/render/layers/natural_layer_renderer.py (99%) rename fle/env/{factorio => game}/tools/admin/render/layers/resource_layer_renderer.py (100%) rename fle/env/{factorio => game}/tools/admin/render/layers/water_layer_renderer.py (100%) rename fle/env/{factorio => game}/tools/admin/render/rendered_image.py (100%) rename fle/env/{factorio => game}/tools/admin/render/renderer.py (99%) rename fle/env/{factorio => game}/tools/admin/render/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/render/utils/colour_manager.py (99%) rename fle/env/{factorio => game}/tools/admin/render/utils/connection_renderer.py (100%) rename fle/env/{factorio => game}/tools/admin/render/utils/electricity_renderer.py (99%) rename fle/env/{factorio => game}/tools/admin/render/utils/entity_categoriser.py (100%) rename fle/env/{factorio => game}/tools/admin/render/utils/image_calculator.py (98%) rename fle/env/{factorio => game}/tools/admin/render/utils/legend_renderer.py (99%) rename fle/env/{factorio => game}/tools/admin/render/utils/natural_renderer.py (100%) rename fle/env/{factorio => game}/tools/admin/render/utils/render_config.py (99%) rename fle/env/{factorio => game}/tools/admin/render/utils/shape_renderer.py (99%) rename fle/env/{factorio => game}/tools/admin/render/utils/tile_renderer.py (100%) rename fle/env/{factorio => game}/tools/admin/render_message/client.py (100%) rename fle/env/{factorio => game}/tools/admin/render_message/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/request_path/client.py (97%) rename fle/env/{factorio => game}/tools/admin/request_path/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/save_blueprint/client.py (93%) rename fle/env/{factorio => game}/tools/admin/save_blueprint/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/save_entity_state/client.py (100%) rename fle/env/{factorio => game}/tools/admin/save_entity_state/server.lua (100%) rename fle/env/{factorio => game}/tools/admin/save_research_state/client.py (100%) rename fle/env/{factorio => game}/tools/admin/save_research_state/server.lua (100%) rename fle/env/{factorio => game}/tools/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/can_place_entity/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/can_place_entity/client.py (100%) rename fle/env/{factorio => game}/tools/agent/can_place_entity/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/connect_entities/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/connect_entities/client.py (100%) rename fle/env/{factorio => game}/tools/agent/connect_entities/groupable_entities.py (100%) rename fle/env/{factorio => game}/tools/agent/connect_entities/path_result.py (100%) rename fle/env/{factorio => game}/tools/agent/connect_entities/resolver.py (96%) rename fle/env/{factorio => game}/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py (100%) rename fle/env/{factorio => game}/tools/agent/connect_entities/resolvers/power_connection_resolver.py (98%) rename fle/env/{factorio => game}/tools/agent/connect_entities/resolvers/transport_connection_resolver.py (99%) rename fle/env/{factorio => game}/tools/agent/connect_entities/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/craft_item/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/craft_item/client.py (100%) rename fle/env/{factorio => game}/tools/agent/craft_item/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/extract_item/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/extract_item/client.py (97%) rename fle/env/{factorio => game}/tools/agent/extract_item/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/get_connection_amount/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/get_connection_amount/client.py (95%) rename fle/env/{factorio => game}/tools/agent/get_connection_amount/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/get_entities/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/get_entities/client.py (99%) rename fle/env/{factorio => game}/tools/agent/get_entities/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/get_entity/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/get_entity/client.py (98%) rename fle/env/{factorio => game}/tools/agent/get_entity/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/get_prototype_recipe/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/get_prototype_recipe/client.py (100%) rename fle/env/{factorio => game}/tools/agent/get_prototype_recipe/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/get_research_progress/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/get_research_progress/client.py (100%) rename fle/env/{factorio => game}/tools/agent/get_research_progress/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/get_resource_patch/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/get_resource_patch/client.py (97%) rename fle/env/{factorio => game}/tools/agent/get_resource_patch/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/harvest_resource/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/harvest_resource/client.py (98%) rename fle/env/{factorio => game}/tools/agent/harvest_resource/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/insert_item/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/insert_item/client.py (98%) rename fle/env/{factorio => game}/tools/agent/insert_item/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/inspect_inventory/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/inspect_inventory/client.py (100%) rename fle/env/{factorio => game}/tools/agent/inspect_inventory/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/launch_rocket/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/launch_rocket/client.py (94%) rename fle/env/{factorio => game}/tools/agent/launch_rocket/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/move_to/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/move_to/client.py (97%) rename fle/env/{factorio => game}/tools/agent/move_to/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/nearest/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/nearest/client.py (97%) rename fle/env/{factorio => game}/tools/agent/nearest/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/nearest_buildable/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/nearest_buildable/client.py (100%) rename fle/env/{factorio => game}/tools/agent/nearest_buildable/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/pickup_entity/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/pickup_entity/client.py (100%) rename fle/env/{factorio => game}/tools/agent/pickup_entity/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/place_entity/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/place_entity/client.py (98%) rename fle/env/{factorio => game}/tools/agent/place_entity/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/place_entity_next_to/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/place_entity_next_to/client.py (98%) rename fle/env/{factorio => game}/tools/agent/place_entity_next_to/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/print/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/print/client.py (95%) rename fle/env/{factorio => game}/tools/agent/print/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/rotate_entity/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/rotate_entity/client.py (98%) rename fle/env/{factorio => game}/tools/agent/rotate_entity/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/score/client.py (100%) rename fle/env/{factorio => game}/tools/agent/score/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/send_message/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/send_message/client.py (100%) rename fle/env/{factorio => game}/tools/agent/send_message/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/set_entity_recipe/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/set_entity_recipe/client.py (98%) rename fle/env/{factorio => game}/tools/agent/set_entity_recipe/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/set_research/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/set_research/client.py (100%) rename fle/env/{factorio => game}/tools/agent/set_research/server.lua (100%) rename fle/env/{factorio => game}/tools/agent/shift_entity/client.py (98%) rename fle/env/{factorio => game}/tools/agent/sleep/agent.md (100%) rename fle/env/{factorio => game}/tools/agent/sleep/client.py (100%) rename fle/env/{factorio => game}/tools/agent/sleep/server.lua (100%) rename fle/env/{factorio => game}/tools/controller.py (95%) rename fle/env/{factorio => game}/tools/init.py (76%) rename fle/env/{factorio => game}/tools/tool.py (85%) create mode 100644 fle/env/game_session.py rename fle/env/{gym_env => }/registry.py (96%) rename fle/{eval => env}/tasks/__init__.py (96%) rename fle/{eval => env}/tasks/default_task.py (97%) rename fle/{eval => env}/tasks/task_abc.py (97%) rename fle/{eval => env}/tasks/task_definitions/lab_play/advanced_circuit_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/automation_science_pack_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/battery_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/chemical_science_pack_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/crude_oil_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/electronic_circuit_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/engine_unit_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/inserter_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/iron_gear_wheel_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/iron_ore_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/iron_plate_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/logistics_science_pack_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/low_density_structure_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/military_science_pack_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/petroleum_gas_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/piercing_round_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/plastic_bar_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/processing_unit_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/production_science_pack_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/steel_plate_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/stone_wall_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/sufuric_acid_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/sulfur_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/lab_play/utility_science_pack_throughput.json (100%) rename fle/{eval => env}/tasks/task_definitions/multiagent/iron_plate_throughput_distrust.json (100%) rename fle/{eval => env}/tasks/task_definitions/multiagent/iron_plate_throughput_free.json (100%) rename fle/{eval => env}/tasks/task_definitions/multiagent/iron_plate_throughput_impostor.json (100%) rename fle/{eval => env}/tasks/task_definitions/unbounded/iron_gear_wheel_throughput_unbounded.json (100%) rename fle/{eval => env}/tasks/task_definitions/unbounded/open_play.json (100%) rename fle/{eval => env}/tasks/task_factory.py (83%) rename fle/{eval => env}/tasks/throughput_task.py (99%) rename fle/{eval => env}/tasks/unbounded_throughput_task.py (97%) create mode 100644 fle/services/db_client.py rename fle/{env/run_envs.py => services/docker_manager.py} (88%) rename fle/{env/utils => services}/rcon.py (100%) rename fle/{env/gym_env => }/trajectory_runner.py (98%) diff --git a/fle/agents/agent_abc.py b/fle/agents/agent_abc.py index baf57d679..747a67795 100644 --- a/fle/agents/agent_abc.py +++ b/fle/agents/agent_abc.py @@ -3,7 +3,7 @@ from a2a.types import AgentCapabilities, AgentCard, AgentProvider, AgentSkill from fle.commons.models.conversation import Conversation -from fle.env.namespace import FactorioNamespace +from fle.env.game.namespace import FactorioNamespace from fle.agents.models import CompletionResult, Response from fle.agents.llm.parsing import Policy diff --git a/fle/agents/data/run_trace.py b/fle/agents/data/run_trace.py index 6d3bf9942..3b100b51c 100644 --- a/fle/agents/data/run_trace.py +++ b/fle/agents/data/run_trace.py @@ -2,7 +2,7 @@ import os from dotenv import load_dotenv -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.env import FactorioInstance # Load environment variables diff --git a/fle/agents/formatters/conversation_formatter_abc.py b/fle/agents/formatters/conversation_formatter_abc.py index b86a17b7f..28bdfd490 100644 --- a/fle/agents/formatters/conversation_formatter_abc.py +++ b/fle/agents/formatters/conversation_formatter_abc.py @@ -3,7 +3,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message -from fle.env.namespace import FactorioNamespace +from fle.env.game.namespace import FactorioNamespace PLANNING_ADDITION_PROMPT = """ Your goal is to automate an increasingly complex factory process. diff --git a/fle/agents/formatters/recursive_report_formatter.py b/fle/agents/formatters/recursive_report_formatter.py index ee4390e6f..f4c9d1d69 100644 --- a/fle/agents/formatters/recursive_report_formatter.py +++ b/fle/agents/formatters/recursive_report_formatter.py @@ -7,7 +7,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message -from fle.env.namespace import FactorioNamespace +from fle.env.game.namespace import FactorioNamespace from fle.agents.formatters.conversation_formatter_abc import ConversationFormatter diff --git a/fle/agents/gym_agent.py b/fle/agents/gym_agent.py index 93e28480c..0d7928803 100644 --- a/fle/agents/gym_agent.py +++ b/fle/agents/gym_agent.py @@ -8,7 +8,7 @@ from fle.commons.models.program import Program from fle.env.gym_env.observation_formatter import BasicObservationFormatter from fle.env.gym_env.system_prompt_formatter import SystemPromptFormatter -from fle.eval.tasks import TaskABC +from fle.env.tasks import TaskABC from fle.agents.models import CompletionResult from fle.agents.llm.parsing import Policy diff --git a/fle/cluster/remote/factorio_server_login.py b/fle/cluster/remote/factorio_server_login.py index 105f31f6c..8b36a3d2c 100644 --- a/fle/cluster/remote/factorio_server_login.py +++ b/fle/cluster/remote/factorio_server_login.py @@ -8,7 +8,7 @@ import sys from dotenv import load_dotenv -from cluster.remote.cluster_ips import get_public_ips +from fle.cluster.remote.cluster_ips import get_public_ips from fle.env import FactorioInstance """ diff --git a/fle/commons/db_client.py b/fle/commons/db_client.py index 080af71dc..a5e7c08c0 100644 --- a/fle/commons/db_client.py +++ b/fle/commons/db_client.py @@ -18,7 +18,7 @@ from tenacity import retry_if_exception_type, wait_exponential, wait_random_exponential from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.program import Program # Configure logging diff --git a/fle/commons/models/__init__.py b/fle/commons/models/__init__.py index b7f61948b..db7fefc57 100644 --- a/fle/commons/models/__init__.py +++ b/fle/commons/models/__init__.py @@ -7,7 +7,7 @@ """ # Game state and research models -from fle.commons.models.game_state import GameState, filter_serializable_vars +from fle.env.game.game_state import GameState, filter_serializable_vars from fle.commons.models.research_state import ResearchState from fle.commons.models.technology_state import TechnologyState diff --git a/fle/commons/models/program.py b/fle/commons/models/program.py index a3ff6ca96..9a659d95b 100644 --- a/fle/commons/models/program.py +++ b/fle/commons/models/program.py @@ -8,7 +8,7 @@ from fle.commons.models.achievements import ProductionFlows from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState class Program(BaseModel): diff --git a/fle/commons/runner.py b/fle/commons/runner.py new file mode 100644 index 000000000..c9b376f1e --- /dev/null +++ b/fle/commons/runner.py @@ -0,0 +1,298 @@ +from abc import ABC, abstractmethod +from typing import Any, Dict, List, Optional, Tuple, Generic, TypeVar +import time +from dataclasses import dataclass +from fle.services.db_client import DBClient +from fle.commons.models.program import Program +from fle.env.game.game_state import GameState +from fle.commons.models.conversation import Conversation +from fle.agents import CompletionResult, CompletionReason +from fle.env.tasks import TaskABC + +# Generic types for flexibility +ConfigType = TypeVar("ConfigType") +AgentType = TypeVar("AgentType") +EnvironmentType = TypeVar("EnvironmentType") +ObservationType = TypeVar("ObservationType") +ActionType = TypeVar("ActionType") + + +@dataclass +class RunnerConfig(ABC): + """Base configuration for all runners""" + + agents: List[Any] + version: int + version_description: str + exit_on_task_success: bool + task: TaskABC + + +class AbstractTrajectoryRunner(ABC, Generic[ConfigType, AgentType, EnvironmentType]): + """Abstract base class for trajectory runners""" + + def __init__( + self, + config: ConfigType, + environment: EnvironmentType, + db_client: Optional[DBClient], + process_id: int, + log_dir: Optional[str] = None, + ): + self.config = config + self.environment = environment + self.agents = config.agents + self.db_client = db_client + self.process_id = process_id + self.start_time = time.time() + self.log_dir = log_dir + + # Common state tracking + self.iteration_times = [] + self.agent_steps = [0] * len(self.agents) + + # Initialize logging + self._initialize_logging() + + # Abstract methods that must be implemented by subclasses + + @abstractmethod + async def _initialize_environment( + self, initial_state: Optional[GameState] = None + ) -> None: + """Initialize the environment with the given state""" + pass + + @abstractmethod + async def _generate_policy(self, agent_idx: int, conversation: Conversation) -> Any: + """Generate a policy/program for the given agent""" + pass + + @abstractmethod + async def _execute_step( + self, agent_idx: int, policy: Any, current_state: GameState + ) -> Tuple[ObservationType, float, bool, Dict[str, Any]]: + """Execute a single step in the environment""" + pass + + @abstractmethod + async def _create_program_from_policy( + self, policy: Any, agent_idx: int, **kwargs + ) -> Program: + """Create a Program object from a policy""" + pass + + @abstractmethod + async def _update_agent_conversation( + self, agent_idx: int, observation: ObservationType, program: Program + ) -> None: + """Update the agent's conversation with the step results""" + pass + + @abstractmethod + def _check_step_completion( + self, agent_idx: int, observation: ObservationType + ) -> Tuple[bool, bool]: + """Check if the agent step is complete and if state should be updated""" + pass + + @abstractmethod + async def _get_initial_observation(self, agent_idx: int) -> ObservationType: + """Get the initial observation for an agent""" + pass + + # Template methods - common patterns with some customization points + + async def _initialize_trajectory_state( + self, + ) -> Tuple[GameState, List[Conversation]]: + """Initialize trajectory state, handling resume or fresh start""" + current_state = None + conversations = [None] * len(self.agents) + + # Check for resume state + if self.config.version and self.db_client is not None: + current_state, conversations = await self._load_resume_state() + + # Initialize fresh if no resume state + if not current_state: + current_state = self.config.task.starting_game_state + conversations = await self._initialize_fresh_conversations() + + # Initialize environment + await self._initialize_environment(current_state) + + return current_state, conversations + + async def _load_resume_state( + self, + ) -> Tuple[Optional[GameState], List[Conversation]]: + """Load resume state from database""" + current_state = None + conversations = [None] * len(self.agents) + + for agent_idx in range(len(self.agents)): + state, conversation, parent_id, depth = ( + await self.db_client.get_resume_state( + resume_version=self.config.version, + process_id=self.process_id, + agent_idx=agent_idx, + ) + ) + if state: + current_state = state + conversations[agent_idx] = conversation + self.agent_steps[agent_idx] = depth + + return current_state, conversations + + async def _initialize_fresh_conversations(self) -> List[Conversation]: + """Initialize fresh conversations for all agents""" + conversations = [] + for agent_idx in range(len(self.agents)): + conversation = Conversation() + initial_obs = await self._get_initial_observation(agent_idx) + # Add initial system message and observation + conversation.add_system_message(self.agents[agent_idx].system_prompt) + conversation.add_user_message(self._format_initial_observation(initial_obs)) + conversations.append(conversation) + return conversations + + def _format_initial_observation(self, observation: ObservationType) -> str: + """Format initial observation for conversation - can be overridden""" + return str(observation) + + def _initialize_logging(self) -> None: + """Initialize logging system - can be overridden""" + pass + + def _log_progress(self, agent_idx: int, step: int, value: float) -> None: + """Log progress - can be overridden""" + if step % 10 == 0: + elapsed = time.time() - self.start_time + elapsed_str = f"{int(elapsed // 3600):02d}:{int((elapsed % 3600) // 60):02d}:{int(elapsed % 60):02d}" + eta = self._calculate_eta(step) + print( + f"Process {self.process_id} - Agent {agent_idx} - " + f"Step {step}/{self.config.task.trajectory_length} - " + f"Value: {value:.2f} - Elapsed: {elapsed_str} - ETA: {eta}" + ) + + def _calculate_eta(self, current_step: int) -> str: + """Calculate estimated time remaining""" + if not self.iteration_times: + return "calculating..." + + # Use recent iterations for better accuracy + recent_times = self.iteration_times[-50:] + avg_time = sum(recent_times) / len(recent_times) + remaining_steps = self.config.task.trajectory_length - current_step + seconds_remaining = avg_time * remaining_steps + + hours = int(seconds_remaining // 3600) + minutes = int((seconds_remaining % 3600) // 60) + seconds = int(seconds_remaining % 60) + + return f"{hours:02d}:{minutes:02d}:{seconds:02d}" + + # Main run method - implements the common trajectory execution pattern + + async def run(self) -> None: + """Main trajectory execution loop""" + # Initialize state + current_state, conversations = await self._initialize_trajectory_state() + max_steps = self.config.task.trajectory_length + + # Main execution loop + from itertools import product + + for _, agent_idx in product(range(max_steps), range(len(self.agents))): + if self.agent_steps[agent_idx] >= max_steps: + continue + + iteration_start = time.time() + agent_completed = False + + try: + # Agent execution loop + while not agent_completed and self.agent_steps[agent_idx] < max_steps: + # Generate policy + policy = await self._generate_policy( + agent_idx, conversations[agent_idx] + ) + self.agent_steps[agent_idx] += 1 + + if not policy: + print(f"Policy generation failed for agent {agent_idx}") + break + + # Execute step + observation, reward, done, info = await self._execute_step( + agent_idx, policy, current_state + ) + + # Create program + program = await self._create_program_from_policy( + policy, + agent_idx, + reward=reward, + observation=observation, + info=info, + ) + + # Update conversation + await self._update_agent_conversation( + agent_idx, observation, program + ) + + # Save program if database available + if self.db_client: + await self._save_program(program) + + # Log progress + self._log_step_completion(iteration_start, agent_idx, program) + + # Check completion + agent_completed, update_state = self._check_step_completion( + agent_idx, observation + ) + if update_state: + current_state = info.get("output_game_state", current_state) + + # Check for early termination + if done and self.config.exit_on_task_success: + await self._handle_early_termination(agent_idx) + return + + except Exception as e: + print( + f"Error in trajectory runner iteration {self.agent_steps[agent_idx]}: {e}" + ) + continue + + async def _save_program(self, program: Program) -> None: + """Save program to database""" + if self.db_client: + saved_program = await self.db_client.create_program(program) + program.id = saved_program.id + + def _log_step_completion( + self, iteration_start: float, agent_idx: int, program: Program + ) -> None: + """Log completion of a step""" + iteration_time = time.time() - iteration_start + self.iteration_times.append(iteration_time) + + # Keep only recent iterations + if len(self.iteration_times) > 50: + self.iteration_times = self.iteration_times[-50:] + + self._log_progress(agent_idx, self.agent_steps[agent_idx], program.value) + + async def _handle_early_termination(self, agent_idx: int) -> None: + """Handle early termination due to task success""" + completion_result = CompletionResult( + step=self.agent_steps[agent_idx], reason=CompletionReason.SUCCESS + ) + for agent in self.agents: + await agent.end(completion_result) diff --git a/fle/env/__init__.py b/fle/env/__init__.py index 2989782d4..2cd441e4f 100644 --- a/fle/env/__init__.py +++ b/fle/env/__init__.py @@ -1,9 +1,9 @@ """Factorio environment module.""" -from fle.env.entities import * # noqa -from fle.env.game_types import Prototype, Resource -from fle.env.instance import DirectionInternal, FactorioInstance -from fle.env.factorio_server import FactorioServer +from fle.env.game.entities import * # noqa +from fle.env.game.game_types import Prototype, Resource +from fle.env.game.instance import DirectionInternal, FactorioInstance +from fle.env.factorio_client import FactorioClient __all__ = [ "FactorioInstance", @@ -15,5 +15,5 @@ "EntityGroup", "Prototype", "Resource", - "FactorioServer", + "FactorioClient", ] diff --git a/fle/env/a2a_namespace.py b/fle/env/a2a_namespace.py index de119a03e..ba67632bf 100644 --- a/fle/env/a2a_namespace.py +++ b/fle/env/a2a_namespace.py @@ -4,7 +4,7 @@ from a2a.types import AgentCard, Message, Part, TextPart from fle.agents.agent_abc import create_default_agent_card -from fle.env.namespace import FactorioNamespace +from fle.env.game.namespace import FactorioNamespace from fle.env.protocols.a2a.handler import A2AProtocolHandler diff --git a/fle/env/gym_env/environment.py b/fle/env/environment.py similarity index 98% rename from fle/env/gym_env/environment.py rename to fle/env/environment.py index 2aacd9f47..667260f7d 100644 --- a/fle/env/gym_env/environment.py +++ b/fle/env/environment.py @@ -1,24 +1,21 @@ +import datetime +import pickle +import string import time +from typing import Any, Dict, Optional, Tuple + import gym import numpy as np from gym import spaces -from typing import Dict, Optional, Tuple, Any -import pickle -import datetime -import string +from fle.agents import Response, TaskResponse +from fle.commons.models.achievements import ProductionFlows +from fle.env.game.game_state import GameState from fle.env import FactorioInstance -from fle.commons.models.game_state import GameState from fle.env.gym_env.action import Action -from fle.commons.models.achievements import ProductionFlows +from fle.env.gym_env.observation import AgentMessage, GameInfo, Observation +from fle.env.tasks import TaskABC from fle.env.utils.profits import get_achievements -from fle.agents import Response, TaskResponse -from fle.env.gym_env.observation import ( - Observation, - GameInfo, - AgentMessage, -) -from fle.eval.tasks import TaskABC # need to do this since gym doesn't work with numpy>=2.0 otherwise. np.bool8 = np.dtype(np.bool) diff --git a/fle/env/factorio_server.py b/fle/env/factorio_client.py similarity index 77% rename from fle/env/factorio_server.py rename to fle/env/factorio_client.py index bccb1549a..c819812a5 100644 --- a/fle/env/factorio_server.py +++ b/fle/env/factorio_client.py @@ -7,12 +7,11 @@ from slpp import slpp as lua from typing_extensions import Any, Dict, List, Tuple -from fle.env.run_envs import PlatformConfig -from fle.env.lua_manager import LuaScriptManager, ToolHookRegistry -from fle.env.namespace import FactorioNamespace -from fle.env.utils.rcon import _lua2python +from fle.services.docker_manager import ServerSettings +from fle.env.game.lua_manager import LuaScriptManager, ToolHookRegistry +from fle.env.game.namespace import FactorioNamespace +from fle.services.rcon import _lua2python -PLATFORM_CONFIG = PlatformConfig() class FactorioTransaction: def __init__(self, server): @@ -65,12 +64,23 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.execute() -class FactorioServer: - def __init__(self, address, tcp_port = PLATFORM_CONFIG.rcon_port, cache_scripts=True, server_docker_config=None): - self.rcon_client, self.address = self.connect_to_server(address, tcp_port) +class FactorioClient: + """ + A class that represents a Factorio server and provides an interface for interacting with it. + """ + rcon_client: RCONClient + server_config: ServerSettings + cache_scripts: bool + tool_hook_registry: ToolHookRegistry + lua_script_manager: LuaScriptManager + script_dict: Dict[str, str] + + def __init__( + self, server_settings: ServerSettings, cache_scripts=True + ): + self.rcon_client = self.get_rcon_client() self.cache_scripts = cache_scripts self.tool_hook_registry = ToolHookRegistry() - # Initialize Lua script manager and script dictionary self.lua_script_manager = LuaScriptManager( self.rcon_client, self.tool_hook_registry, cache_scripts ) @@ -78,11 +88,20 @@ def __init__(self, address, tcp_port = PLATFORM_CONFIG.rcon_port, cache_scripts= **self.lua_script_manager.lib_scripts, **self.lua_script_manager.tool_scripts, } - self.server_docker_config = server_docker_config + self.rcon_password = server_settings.rcon_password + self.rcon_port = server_settings.rcon_port + self.address = server_settings.address + + def get_rcon_client(self, address=None): + return RCONClient( + address if address else self.address, + self.rcon_port, + self.rcon_password, + ) def ensure_rcon_client(self): if not self.rcon_client: - self.rcon_client = RCONClient(self.address, self.tcp_port, PLATFORM_CONFIG.factorio_password) + self.rcon_client = self.get_rcon_client() return self.rcon_client def run_rcon_print(self, command: str): @@ -100,17 +119,16 @@ def load_init_into_game(self, init_scripts): def load_tool_into_game(self, name): self.lua_script_manager.load_tool_into_game(name) - def connect_to_server(self, address, tcp_port): + def connect_to_server(self): try: - rcon_client = RCONClient(address, tcp_port, PLATFORM_CONFIG.factorio_password) - address = address + rcon_client = self.get_rcon_client() except ConnectionError as e: print(e) - rcon_client = RCONClient("localhost", tcp_port, PLATFORM_CONFIG.factorio_password) - address = "localhost" - print(f"Connected to {address} client at tcp/{tcp_port}.") - return rcon_client, address + print(f"Connected to {self.address}" + f"client at tcp/{self.rcon_port}." + f"Password: {self.rcon_password}") + return rcon_client def _get_command(self, command, parameters=[], measured=True): prefix = "/sc " if not measured else "/command " @@ -163,4 +181,3 @@ def cleanup(self): thread.join(timeout=5) # Wait up to 5 seconds for each thread except Exception as e: print(f"Error joining thread {thread.name}: {e}") - diff --git a/fle/env/entities.py b/fle/env/game/entities.py similarity index 100% rename from fle/env/entities.py rename to fle/env/game/entities.py diff --git a/fle/env/factorio/config/map-gen-settings.json b/fle/env/game/factorio/config/map-gen-settings.json similarity index 100% rename from fle/env/factorio/config/map-gen-settings.json rename to fle/env/game/factorio/config/map-gen-settings.json diff --git a/fle/env/factorio/config/map-settings.json b/fle/env/game/factorio/config/map-settings.json similarity index 100% rename from fle/env/factorio/config/map-settings.json rename to fle/env/game/factorio/config/map-settings.json diff --git a/fle/env/factorio/config/rconpw b/fle/env/game/factorio/config/rconpw similarity index 100% rename from fle/env/factorio/config/rconpw rename to fle/env/game/factorio/config/rconpw diff --git a/fle/env/factorio/config/server-settings.json b/fle/env/game/factorio/config/server-settings.json similarity index 100% rename from fle/env/factorio/config/server-settings.json rename to fle/env/game/factorio/config/server-settings.json diff --git a/fle/env/factorio/mods/README.md b/fle/env/game/factorio/mods/README.md similarity index 100% rename from fle/env/factorio/mods/README.md rename to fle/env/game/factorio/mods/README.md diff --git a/fle/env/factorio/mods/alerts.lua b/fle/env/game/factorio/mods/alerts.lua similarity index 100% rename from fle/env/factorio/mods/alerts.lua rename to fle/env/game/factorio/mods/alerts.lua diff --git a/fle/env/factorio/mods/build_checkerboard.lua b/fle/env/game/factorio/mods/build_checkerboard.lua similarity index 100% rename from fle/env/factorio/mods/build_checkerboard.lua rename to fle/env/game/factorio/mods/build_checkerboard.lua diff --git a/fle/env/factorio/mods/checksum.lua b/fle/env/game/factorio/mods/checksum.lua similarity index 100% rename from fle/env/factorio/mods/checksum.lua rename to fle/env/game/factorio/mods/checksum.lua diff --git a/fle/env/factorio/mods/clear_entities.lua b/fle/env/game/factorio/mods/clear_entities.lua similarity index 100% rename from fle/env/factorio/mods/clear_entities.lua rename to fle/env/game/factorio/mods/clear_entities.lua diff --git a/fle/env/factorio/mods/clear_inventory.lua b/fle/env/game/factorio/mods/clear_inventory.lua similarity index 100% rename from fle/env/factorio/mods/clear_inventory.lua rename to fle/env/game/factorio/mods/clear_inventory.lua diff --git a/fle/env/factorio/mods/connection_points.lua b/fle/env/game/factorio/mods/connection_points.lua similarity index 100% rename from fle/env/factorio/mods/connection_points.lua rename to fle/env/game/factorio/mods/connection_points.lua diff --git a/fle/env/factorio/mods/enemies.lua b/fle/env/game/factorio/mods/enemies.lua similarity index 100% rename from fle/env/factorio/mods/enemies.lua rename to fle/env/game/factorio/mods/enemies.lua diff --git a/fle/env/factorio/mods/initialise.lua b/fle/env/game/factorio/mods/initialise.lua similarity index 100% rename from fle/env/factorio/mods/initialise.lua rename to fle/env/game/factorio/mods/initialise.lua diff --git a/fle/env/factorio/mods/initialise_inventory.lua b/fle/env/game/factorio/mods/initialise_inventory.lua similarity index 100% rename from fle/env/factorio/mods/initialise_inventory.lua rename to fle/env/game/factorio/mods/initialise_inventory.lua diff --git a/fle/env/factorio/mods/priority_queue.lua b/fle/env/game/factorio/mods/priority_queue.lua similarity index 100% rename from fle/env/factorio/mods/priority_queue.lua rename to fle/env/game/factorio/mods/priority_queue.lua diff --git a/fle/env/factorio/mods/production_score.lua b/fle/env/game/factorio/mods/production_score.lua similarity index 100% rename from fle/env/factorio/mods/production_score.lua rename to fle/env/game/factorio/mods/production_score.lua diff --git a/fle/env/factorio/mods/recipe_fluid_connection_mappings.lua b/fle/env/game/factorio/mods/recipe_fluid_connection_mappings.lua similarity index 100% rename from fle/env/factorio/mods/recipe_fluid_connection_mappings.lua rename to fle/env/game/factorio/mods/recipe_fluid_connection_mappings.lua diff --git a/fle/env/factorio/mods/reset.lua b/fle/env/game/factorio/mods/reset.lua similarity index 100% rename from fle/env/factorio/mods/reset.lua rename to fle/env/game/factorio/mods/reset.lua diff --git a/fle/env/factorio/mods/reset_position.lua b/fle/env/game/factorio/mods/reset_position.lua similarity index 100% rename from fle/env/factorio/mods/reset_position.lua rename to fle/env/game/factorio/mods/reset_position.lua diff --git a/fle/env/factorio/mods/serialize.lua b/fle/env/game/factorio/mods/serialize.lua similarity index 100% rename from fle/env/factorio/mods/serialize.lua rename to fle/env/game/factorio/mods/serialize.lua diff --git a/fle/env/factorio/mods/util.lua b/fle/env/game/factorio/mods/util.lua similarity index 100% rename from fle/env/factorio/mods/util.lua rename to fle/env/game/factorio/mods/util.lua diff --git a/fle/env/factorio/scenarios/default_lab_scenario/blueprint.zip b/fle/env/game/factorio/scenarios/default_lab_scenario/blueprint.zip similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/blueprint.zip rename to fle/env/game/factorio/scenarios/default_lab_scenario/blueprint.zip diff --git a/fle/env/factorio/scenarios/default_lab_scenario/control2.lua b/fle/env/game/factorio/scenarios/default_lab_scenario/control2.lua similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/control2.lua rename to fle/env/game/factorio/scenarios/default_lab_scenario/control2.lua diff --git a/fle/env/factorio/scenarios/default_lab_scenario/description.json b/fle/env/game/factorio/scenarios/default_lab_scenario/description.json similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/description.json rename to fle/env/game/factorio/scenarios/default_lab_scenario/description.json diff --git a/fle/env/factorio/scenarios/default_lab_scenario/freeplay.lua b/fle/env/game/factorio/scenarios/default_lab_scenario/freeplay.lua similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/freeplay.lua rename to fle/env/game/factorio/scenarios/default_lab_scenario/freeplay.lua diff --git a/fle/env/factorio/scenarios/default_lab_scenario/info.json b/fle/env/game/factorio/scenarios/default_lab_scenario/info.json similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/info.json rename to fle/env/game/factorio/scenarios/default_lab_scenario/info.json diff --git a/fle/env/factorio/scenarios/default_lab_scenario/locale/en/freeplay.cfg b/fle/env/game/factorio/scenarios/default_lab_scenario/locale/en/freeplay.cfg similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/locale/en/freeplay.cfg rename to fle/env/game/factorio/scenarios/default_lab_scenario/locale/en/freeplay.cfg diff --git a/fle/env/factorio/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg b/fle/env/game/factorio/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg rename to fle/env/game/factorio/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg diff --git a/fle/env/factorio/scenarios/default_lab_scenario/paperclips.lua b/fle/env/game/factorio/scenarios/default_lab_scenario/paperclips.lua similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/paperclips.lua rename to fle/env/game/factorio/scenarios/default_lab_scenario/paperclips.lua diff --git a/fle/env/factorio/scenarios/default_lab_scenario/planet1.png b/fle/env/game/factorio/scenarios/default_lab_scenario/planet1.png similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/planet1.png rename to fle/env/game/factorio/scenarios/default_lab_scenario/planet1.png diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script.dat b/fle/env/game/factorio/scenarios/default_lab_scenario/script.dat similarity index 100% rename from fle/env/factorio/scenarios/default_lab_scenario/script.dat rename to fle/env/game/factorio/scenarios/default_lab_scenario/script.dat diff --git a/fle/env/factorio/scenarios/open_world/README.md b/fle/env/game/factorio/scenarios/open_world/README.md similarity index 100% rename from fle/env/factorio/scenarios/open_world/README.md rename to fle/env/game/factorio/scenarios/open_world/README.md diff --git a/fle/commons/models/game_state.py b/fle/env/game/game_state.py similarity index 100% rename from fle/commons/models/game_state.py rename to fle/env/game/game_state.py diff --git a/fle/env/game_types.py b/fle/env/game/game_types.py similarity index 100% rename from fle/env/game_types.py rename to fle/env/game/game_types.py diff --git a/fle/env/instance.py b/fle/env/game/instance.py similarity index 98% rename from fle/env/instance.py rename to fle/env/game/instance.py index 80a864c3f..9afb11d4b 100644 --- a/fle/env/instance.py +++ b/fle/env/game/instance.py @@ -16,14 +16,14 @@ from dotenv import load_dotenv from typing_extensions import Any, Dict, List, Optional -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.research_state import ResearchState -from fle.env.namespace import FactorioNamespace +from fle.env.game.namespace import FactorioNamespace from fle.env.utils.controller_loader.system_prompt_generator import ( SystemPromptGenerator, ) -from fle.env.factorio_server import FactorioServer -from fle.env.utils.rcon import _lua2python +from fle.env.factorio_client import FactorioClient +from fle.services.rcon import _lua2python CHUNK_SIZE = 32 @@ -68,7 +68,7 @@ class FactorioInstance: namespace_class = FactorioNamespace _cleanup_registered = False # Only register cleanup once per process namespaces: List[FactorioNamespace] - server: FactorioServer + server: FactorioClient def __init__( self, @@ -86,7 +86,7 @@ def __init__( self.num_agents = num_agents self.persistent_vars = {} self.tcp_port = tcp_port - self.server = FactorioServer(address, tcp_port, cache_scripts) + self.server = FactorioClient(address, tcp_port, cache_scripts) self.all_technologies_researched = all_technologies_researched self.fast = fast self._speed = 1 @@ -492,4 +492,4 @@ def run_snippet_file_in_factorio_env(self, file_path, clean=True): self.cleanup() def cleanup(self): - self.server.cleanup() + self.server.cleanup() \ No newline at end of file diff --git a/fle/env/lua_manager.py b/fle/env/game/lua_manager.py similarity index 99% rename from fle/env/lua_manager.py rename to fle/env/game/lua_manager.py index ef5e702ad..2282b58bb 100644 --- a/fle/env/lua_manager.py +++ b/fle/env/game/lua_manager.py @@ -6,7 +6,7 @@ from lupa.lua54 import LuaRuntime from contextlib import contextmanager from factorio_rcon import RCONClient -from fle.env.utils.rcon import ( +from fle.services.rcon import ( _get_dir, _get_mods_dir, _get_lib_names, diff --git a/fle/env/namespace.py b/fle/env/game/namespace.py similarity index 99% rename from fle/env/namespace.py rename to fle/env/game/namespace.py index 0b7a6c1d0..bdb819973 100644 --- a/fle/env/namespace.py +++ b/fle/env/game/namespace.py @@ -14,7 +14,7 @@ from fle.commons.models.serializable_function import SerializableFunction from fle.env import entities as ent -from fle.env.entities import Entity +from fle.env.game.entities import Entity from fle.env.exceptions.hinting_name_error import get_value_type_str from fle.env.game_types import ( Prototype, diff --git a/fle/env/factorio/tools/__init__.py b/fle/env/game/tools/__init__.py similarity index 100% rename from fle/env/factorio/tools/__init__.py rename to fle/env/game/tools/__init__.py diff --git a/fle/env/factorio/tools/admin/clear_collision_boxes/client.py b/fle/env/game/tools/admin/clear_collision_boxes/client.py similarity index 100% rename from fle/env/factorio/tools/admin/clear_collision_boxes/client.py rename to fle/env/game/tools/admin/clear_collision_boxes/client.py diff --git a/fle/env/factorio/tools/admin/clear_collision_boxes/server.lua b/fle/env/game/tools/admin/clear_collision_boxes/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/clear_collision_boxes/server.lua rename to fle/env/game/tools/admin/clear_collision_boxes/server.lua diff --git a/fle/env/factorio/tools/admin/clear_entities/client.py b/fle/env/game/tools/admin/clear_entities/client.py similarity index 100% rename from fle/env/factorio/tools/admin/clear_entities/client.py rename to fle/env/game/tools/admin/clear_entities/client.py diff --git a/fle/env/factorio/tools/admin/clear_entities/server.lua b/fle/env/game/tools/admin/clear_entities/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/clear_entities/server.lua rename to fle/env/game/tools/admin/clear_entities/server.lua diff --git a/fle/env/factorio/tools/admin/extend_collision_boxes/client.py b/fle/env/game/tools/admin/extend_collision_boxes/client.py similarity index 94% rename from fle/env/factorio/tools/admin/extend_collision_boxes/client.py rename to fle/env/game/tools/admin/extend_collision_boxes/client.py index 1037773fe..08f3dc3ee 100644 --- a/fle/env/factorio/tools/admin/extend_collision_boxes/client.py +++ b/fle/env/game/tools/admin/extend_collision_boxes/client.py @@ -1,4 +1,4 @@ -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/admin/extend_collision_boxes/server.lua b/fle/env/game/tools/admin/extend_collision_boxes/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/extend_collision_boxes/server.lua rename to fle/env/game/tools/admin/extend_collision_boxes/server.lua diff --git a/fle/env/factorio/tools/admin/get_factory_centroid/client.py b/fle/env/game/tools/admin/get_factory_centroid/client.py similarity index 97% rename from fle/env/factorio/tools/admin/get_factory_centroid/client.py rename to fle/env/game/tools/admin/get_factory_centroid/client.py index 4c05d9ba9..e649e47b2 100644 --- a/fle/env/factorio/tools/admin/get_factory_centroid/client.py +++ b/fle/env/game/tools/admin/get_factory_centroid/client.py @@ -1,4 +1,4 @@ -from fle.env.entities import BoundingBox, Position +from fle.env.game.entities import BoundingBox, Position from fle.env.factorio.tools import Tool from fle.env.utils.camera import Camera diff --git a/fle/env/factorio/tools/admin/get_factory_centroid/server.lua b/fle/env/game/tools/admin/get_factory_centroid/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/get_factory_centroid/server.lua rename to fle/env/game/tools/admin/get_factory_centroid/server.lua diff --git a/fle/env/factorio/tools/admin/get_factory_centroid/server2.lua b/fle/env/game/tools/admin/get_factory_centroid/server2.lua similarity index 100% rename from fle/env/factorio/tools/admin/get_factory_centroid/server2.lua rename to fle/env/game/tools/admin/get_factory_centroid/server2.lua diff --git a/fle/env/factorio/tools/admin/get_messages/server.lua b/fle/env/game/tools/admin/get_messages/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/get_messages/server.lua rename to fle/env/game/tools/admin/get_messages/server.lua diff --git a/fle/env/factorio/tools/admin/get_path/client.py b/fle/env/game/tools/admin/get_path/client.py similarity index 97% rename from fle/env/factorio/tools/admin/get_path/client.py rename to fle/env/game/tools/admin/get_path/client.py index 4569389eb..e6c8a1cd5 100644 --- a/fle/env/factorio/tools/admin/get_path/client.py +++ b/fle/env/game/tools/admin/get_path/client.py @@ -1,7 +1,7 @@ import json from typing import List -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/admin/get_path/server.lua b/fle/env/game/tools/admin/get_path/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/get_path/server.lua rename to fle/env/game/tools/admin/get_path/server.lua diff --git a/fle/env/factorio/tools/admin/get_production_stats/client.py b/fle/env/game/tools/admin/get_production_stats/client.py similarity index 100% rename from fle/env/factorio/tools/admin/get_production_stats/client.py rename to fle/env/game/tools/admin/get_production_stats/client.py diff --git a/fle/env/factorio/tools/admin/get_production_stats/server.lua b/fle/env/game/tools/admin/get_production_stats/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/get_production_stats/server.lua rename to fle/env/game/tools/admin/get_production_stats/server.lua diff --git a/fle/env/factorio/tools/admin/inspect_entities/deprecated b/fle/env/game/tools/admin/inspect_entities/deprecated similarity index 100% rename from fle/env/factorio/tools/admin/inspect_entities/deprecated rename to fle/env/game/tools/admin/inspect_entities/deprecated diff --git a/fle/env/factorio/tools/admin/inspect_entities/server.lua b/fle/env/game/tools/admin/inspect_entities/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/inspect_entities/server.lua rename to fle/env/game/tools/admin/inspect_entities/server.lua diff --git a/fle/env/factorio/tools/admin/load_blueprint/client.py b/fle/env/game/tools/admin/load_blueprint/client.py similarity index 93% rename from fle/env/factorio/tools/admin/load_blueprint/client.py rename to fle/env/game/tools/admin/load_blueprint/client.py index f6d63d3f4..69f010ab2 100644 --- a/fle/env/factorio/tools/admin/load_blueprint/client.py +++ b/fle/env/game/tools/admin/load_blueprint/client.py @@ -1,4 +1,4 @@ -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/admin/load_blueprint/server.lua b/fle/env/game/tools/admin/load_blueprint/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/load_blueprint/server.lua rename to fle/env/game/tools/admin/load_blueprint/server.lua diff --git a/fle/env/factorio/tools/admin/load_entity_state/client.py b/fle/env/game/tools/admin/load_entity_state/client.py similarity index 100% rename from fle/env/factorio/tools/admin/load_entity_state/client.py rename to fle/env/game/tools/admin/load_entity_state/client.py diff --git a/fle/env/factorio/tools/admin/load_entity_state/server.lua b/fle/env/game/tools/admin/load_entity_state/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/load_entity_state/server.lua rename to fle/env/game/tools/admin/load_entity_state/server.lua diff --git a/fle/env/factorio/tools/admin/load_research_state/client.py b/fle/env/game/tools/admin/load_research_state/client.py similarity index 100% rename from fle/env/factorio/tools/admin/load_research_state/client.py rename to fle/env/game/tools/admin/load_research_state/client.py diff --git a/fle/env/factorio/tools/admin/load_research_state/server.lua b/fle/env/game/tools/admin/load_research_state/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/load_research_state/server.lua rename to fle/env/game/tools/admin/load_research_state/server.lua diff --git a/fle/env/factorio/tools/admin/message_utils.py b/fle/env/game/tools/admin/message_utils.py similarity index 100% rename from fle/env/factorio/tools/admin/message_utils.py rename to fle/env/game/tools/admin/message_utils.py diff --git a/fle/env/factorio/tools/admin/production_stats/client.py b/fle/env/game/tools/admin/production_stats/client.py similarity index 100% rename from fle/env/factorio/tools/admin/production_stats/client.py rename to fle/env/game/tools/admin/production_stats/client.py diff --git a/fle/env/factorio/tools/admin/production_stats/server.lua b/fle/env/game/tools/admin/production_stats/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/production_stats/server.lua rename to fle/env/game/tools/admin/production_stats/server.lua diff --git a/fle/env/factorio/tools/admin/regenerate_resources/client.py b/fle/env/game/tools/admin/regenerate_resources/client.py similarity index 100% rename from fle/env/factorio/tools/admin/regenerate_resources/client.py rename to fle/env/game/tools/admin/regenerate_resources/client.py diff --git a/fle/env/factorio/tools/admin/regenerate_resources/server.lua b/fle/env/game/tools/admin/regenerate_resources/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/regenerate_resources/server.lua rename to fle/env/game/tools/admin/regenerate_resources/server.lua diff --git a/fle/env/factorio/tools/admin/render/agent.md b/fle/env/game/tools/admin/render/agent.md similarity index 100% rename from fle/env/factorio/tools/admin/render/agent.md rename to fle/env/game/tools/admin/render/agent.md diff --git a/fle/env/factorio/tools/admin/render/client.py b/fle/env/game/tools/admin/render/client.py similarity index 100% rename from fle/env/factorio/tools/admin/render/client.py rename to fle/env/game/tools/admin/render/client.py diff --git a/fle/env/factorio/tools/admin/render/layers/connection_layers_renderer.py b/fle/env/game/tools/admin/render/layers/connection_layers_renderer.py similarity index 98% rename from fle/env/factorio/tools/admin/render/layers/connection_layers_renderer.py rename to fle/env/game/tools/admin/render/layers/connection_layers_renderer.py index ed70a5fb0..a799069e3 100644 --- a/fle/env/factorio/tools/admin/render/layers/connection_layers_renderer.py +++ b/fle/env/game/tools/admin/render/layers/connection_layers_renderer.py @@ -1,7 +1,7 @@ from typing import Dict, Callable, List from PIL import ImageDraw -from fle.env.entities import Entity, UndergroundBelt, Pipe +from fle.env.game.entities import Entity, UndergroundBelt, Pipe from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer diff --git a/fle/env/factorio/tools/admin/render/layers/entities_layer_renderer.py b/fle/env/game/tools/admin/render/layers/entities_layer_renderer.py similarity index 98% rename from fle/env/factorio/tools/admin/render/layers/entities_layer_renderer.py rename to fle/env/game/tools/admin/render/layers/entities_layer_renderer.py index 6fcf1d440..5f03ad9b9 100644 --- a/fle/env/factorio/tools/admin/render/layers/entities_layer_renderer.py +++ b/fle/env/game/tools/admin/render/layers/entities_layer_renderer.py @@ -1,7 +1,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.entities import Direction, EntityStatus +from fle.env.game.entities import Direction, EntityStatus from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer diff --git a/fle/env/factorio/tools/admin/render/layers/grid_layer_renderer.py b/fle/env/game/tools/admin/render/layers/grid_layer_renderer.py similarity index 100% rename from fle/env/factorio/tools/admin/render/layers/grid_layer_renderer.py rename to fle/env/game/tools/admin/render/layers/grid_layer_renderer.py diff --git a/fle/env/factorio/tools/admin/render/layers/layer_renderer.py b/fle/env/game/tools/admin/render/layers/layer_renderer.py similarity index 100% rename from fle/env/factorio/tools/admin/render/layers/layer_renderer.py rename to fle/env/game/tools/admin/render/layers/layer_renderer.py diff --git a/fle/env/factorio/tools/admin/render/layers/marker_layers_renderer.py b/fle/env/game/tools/admin/render/layers/marker_layers_renderer.py similarity index 98% rename from fle/env/factorio/tools/admin/render/layers/marker_layers_renderer.py rename to fle/env/game/tools/admin/render/layers/marker_layers_renderer.py index 86bda0f78..e35d6a0fa 100644 --- a/fle/env/factorio/tools/admin/render/layers/marker_layers_renderer.py +++ b/fle/env/game/tools/admin/render/layers/marker_layers_renderer.py @@ -1,7 +1,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.entities import Position, Layer +from fle.env.game.entities import Position, Layer from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer diff --git a/fle/env/factorio/tools/admin/render/layers/natural_layer_renderer.py b/fle/env/game/tools/admin/render/layers/natural_layer_renderer.py similarity index 99% rename from fle/env/factorio/tools/admin/render/layers/natural_layer_renderer.py rename to fle/env/game/tools/admin/render/layers/natural_layer_renderer.py index 9bedb9e26..c98061056 100644 --- a/fle/env/factorio/tools/admin/render/layers/natural_layer_renderer.py +++ b/fle/env/game/tools/admin/render/layers/natural_layer_renderer.py @@ -3,7 +3,7 @@ from typing import Dict, Callable, List from PIL import ImageDraw -from fle.env.entities import Layer +from fle.env.game.entities import Layer from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer diff --git a/fle/env/factorio/tools/admin/render/layers/resource_layer_renderer.py b/fle/env/game/tools/admin/render/layers/resource_layer_renderer.py similarity index 100% rename from fle/env/factorio/tools/admin/render/layers/resource_layer_renderer.py rename to fle/env/game/tools/admin/render/layers/resource_layer_renderer.py diff --git a/fle/env/factorio/tools/admin/render/layers/water_layer_renderer.py b/fle/env/game/tools/admin/render/layers/water_layer_renderer.py similarity index 100% rename from fle/env/factorio/tools/admin/render/layers/water_layer_renderer.py rename to fle/env/game/tools/admin/render/layers/water_layer_renderer.py diff --git a/fle/env/factorio/tools/admin/render/rendered_image.py b/fle/env/game/tools/admin/render/rendered_image.py similarity index 100% rename from fle/env/factorio/tools/admin/render/rendered_image.py rename to fle/env/game/tools/admin/render/rendered_image.py diff --git a/fle/env/factorio/tools/admin/render/renderer.py b/fle/env/game/tools/admin/render/renderer.py similarity index 99% rename from fle/env/factorio/tools/admin/render/renderer.py rename to fle/env/game/tools/admin/render/renderer.py index f5d3054f7..1f43c119b 100644 --- a/fle/env/factorio/tools/admin/render/renderer.py +++ b/fle/env/game/tools/admin/render/renderer.py @@ -1,7 +1,7 @@ from PIL import Image, ImageDraw, ImageFont from typing import List, Dict, Optional -from fle.env.entities import Entity, Position, BoundingBox, Layer, EntityStatus +from fle.env.game.entities import Entity, Position, BoundingBox, Layer, EntityStatus from fle.env.factorio.tools.admin.render.layers.connection_layers_renderer import ( ConnectionsLayerRenderer, ) diff --git a/fle/env/factorio/tools/admin/render/server.lua b/fle/env/game/tools/admin/render/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/render/server.lua rename to fle/env/game/tools/admin/render/server.lua diff --git a/fle/env/factorio/tools/admin/render/utils/colour_manager.py b/fle/env/game/tools/admin/render/utils/colour_manager.py similarity index 99% rename from fle/env/factorio/tools/admin/render/utils/colour_manager.py rename to fle/env/game/tools/admin/render/utils/colour_manager.py index 2e8d2e58b..d03601562 100644 --- a/fle/env/factorio/tools/admin/render/utils/colour_manager.py +++ b/fle/env/game/tools/admin/render/utils/colour_manager.py @@ -2,7 +2,7 @@ from typing import Dict, List, Tuple from collections import defaultdict -from fle.env.entities import Entity, EntityStatus +from fle.env.game.entities import Entity, EntityStatus from fle.env.game_types import prototype_by_name from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig from fle.env.factorio.tools.admin.render.utils.entity_categoriser import EntityCategoriser diff --git a/fle/env/factorio/tools/admin/render/utils/connection_renderer.py b/fle/env/game/tools/admin/render/utils/connection_renderer.py similarity index 100% rename from fle/env/factorio/tools/admin/render/utils/connection_renderer.py rename to fle/env/game/tools/admin/render/utils/connection_renderer.py diff --git a/fle/env/factorio/tools/admin/render/utils/electricity_renderer.py b/fle/env/game/tools/admin/render/utils/electricity_renderer.py similarity index 99% rename from fle/env/factorio/tools/admin/render/utils/electricity_renderer.py rename to fle/env/game/tools/admin/render/utils/electricity_renderer.py index 37c0bde2d..7607ec9cd 100644 --- a/fle/env/factorio/tools/admin/render/utils/electricity_renderer.py +++ b/fle/env/game/tools/admin/render/utils/electricity_renderer.py @@ -2,7 +2,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.entities import Layer +from fle.env.game.entities import Layer from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer diff --git a/fle/env/factorio/tools/admin/render/utils/entity_categoriser.py b/fle/env/game/tools/admin/render/utils/entity_categoriser.py similarity index 100% rename from fle/env/factorio/tools/admin/render/utils/entity_categoriser.py rename to fle/env/game/tools/admin/render/utils/entity_categoriser.py diff --git a/fle/env/factorio/tools/admin/render/utils/image_calculator.py b/fle/env/game/tools/admin/render/utils/image_calculator.py similarity index 98% rename from fle/env/factorio/tools/admin/render/utils/image_calculator.py rename to fle/env/game/tools/admin/render/utils/image_calculator.py index b3e0d4e5f..97ba0ded8 100644 --- a/fle/env/factorio/tools/admin/render/utils/image_calculator.py +++ b/fle/env/game/tools/admin/render/utils/image_calculator.py @@ -1,6 +1,6 @@ from typing import List, Optional, Dict, Callable -from fle.env.entities import Entity, Position, BoundingBox, Direction +from fle.env.game.entities import Entity, Position, BoundingBox, Direction from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig diff --git a/fle/env/factorio/tools/admin/render/utils/legend_renderer.py b/fle/env/game/tools/admin/render/utils/legend_renderer.py similarity index 99% rename from fle/env/factorio/tools/admin/render/utils/legend_renderer.py rename to fle/env/game/tools/admin/render/utils/legend_renderer.py index 88672311d..3443fba55 100644 --- a/fle/env/factorio/tools/admin/render/utils/legend_renderer.py +++ b/fle/env/game/tools/admin/render/utils/legend_renderer.py @@ -2,7 +2,7 @@ from PIL import ImageDraw, ImageFont, Image import math -from fle.env.entities import EntityStatus +from fle.env.game.entities import EntityStatus from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig from fle.env.factorio.tools.admin.render.utils.colour_manager import ColourManager from fle.env.factorio.tools.admin.render.utils.entity_categoriser import EntityCategoriser diff --git a/fle/env/factorio/tools/admin/render/utils/natural_renderer.py b/fle/env/game/tools/admin/render/utils/natural_renderer.py similarity index 100% rename from fle/env/factorio/tools/admin/render/utils/natural_renderer.py rename to fle/env/game/tools/admin/render/utils/natural_renderer.py diff --git a/fle/env/factorio/tools/admin/render/utils/render_config.py b/fle/env/game/tools/admin/render/utils/render_config.py similarity index 99% rename from fle/env/factorio/tools/admin/render/utils/render_config.py rename to fle/env/game/tools/admin/render/utils/render_config.py index 8c3523407..b458f55b4 100644 --- a/fle/env/factorio/tools/admin/render/utils/render_config.py +++ b/fle/env/game/tools/admin/render/utils/render_config.py @@ -1,5 +1,5 @@ from typing import Dict, Optional, Tuple, Any -from fle.env.entities import EntityStatus +from fle.env.game.entities import EntityStatus class RenderConfig: diff --git a/fle/env/factorio/tools/admin/render/utils/shape_renderer.py b/fle/env/game/tools/admin/render/utils/shape_renderer.py similarity index 99% rename from fle/env/factorio/tools/admin/render/utils/shape_renderer.py rename to fle/env/game/tools/admin/render/utils/shape_renderer.py index 5ec2302b8..4b6d46744 100644 --- a/fle/env/factorio/tools/admin/render/utils/shape_renderer.py +++ b/fle/env/game/tools/admin/render/utils/shape_renderer.py @@ -2,7 +2,7 @@ from typing import Tuple, Optional from PIL import ImageDraw -from fle.env.entities import Direction, EntityStatus +from fle.env.game.entities import Direction, EntityStatus from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig diff --git a/fle/env/factorio/tools/admin/render/utils/tile_renderer.py b/fle/env/game/tools/admin/render/utils/tile_renderer.py similarity index 100% rename from fle/env/factorio/tools/admin/render/utils/tile_renderer.py rename to fle/env/game/tools/admin/render/utils/tile_renderer.py diff --git a/fle/env/factorio/tools/admin/render_message/client.py b/fle/env/game/tools/admin/render_message/client.py similarity index 100% rename from fle/env/factorio/tools/admin/render_message/client.py rename to fle/env/game/tools/admin/render_message/client.py diff --git a/fle/env/factorio/tools/admin/render_message/server.lua b/fle/env/game/tools/admin/render_message/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/render_message/server.lua rename to fle/env/game/tools/admin/render_message/server.lua diff --git a/fle/env/factorio/tools/admin/request_path/client.py b/fle/env/game/tools/admin/request_path/client.py similarity index 97% rename from fle/env/factorio/tools/admin/request_path/client.py rename to fle/env/game/tools/admin/request_path/client.py index ca53d3601..0fe28cbff 100644 --- a/fle/env/factorio/tools/admin/request_path/client.py +++ b/fle/env/game/tools/admin/request_path/client.py @@ -1,4 +1,4 @@ -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/admin/request_path/server.lua b/fle/env/game/tools/admin/request_path/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/request_path/server.lua rename to fle/env/game/tools/admin/request_path/server.lua diff --git a/fle/env/factorio/tools/admin/save_blueprint/client.py b/fle/env/game/tools/admin/save_blueprint/client.py similarity index 93% rename from fle/env/factorio/tools/admin/save_blueprint/client.py rename to fle/env/game/tools/admin/save_blueprint/client.py index a435dec12..a3c0a6bab 100644 --- a/fle/env/factorio/tools/admin/save_blueprint/client.py +++ b/fle/env/game/tools/admin/save_blueprint/client.py @@ -1,6 +1,6 @@ from typing import Tuple -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/admin/save_blueprint/server.lua b/fle/env/game/tools/admin/save_blueprint/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/save_blueprint/server.lua rename to fle/env/game/tools/admin/save_blueprint/server.lua diff --git a/fle/env/factorio/tools/admin/save_entity_state/client.py b/fle/env/game/tools/admin/save_entity_state/client.py similarity index 100% rename from fle/env/factorio/tools/admin/save_entity_state/client.py rename to fle/env/game/tools/admin/save_entity_state/client.py diff --git a/fle/env/factorio/tools/admin/save_entity_state/server.lua b/fle/env/game/tools/admin/save_entity_state/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/save_entity_state/server.lua rename to fle/env/game/tools/admin/save_entity_state/server.lua diff --git a/fle/env/factorio/tools/admin/save_research_state/client.py b/fle/env/game/tools/admin/save_research_state/client.py similarity index 100% rename from fle/env/factorio/tools/admin/save_research_state/client.py rename to fle/env/game/tools/admin/save_research_state/client.py diff --git a/fle/env/factorio/tools/admin/save_research_state/server.lua b/fle/env/game/tools/admin/save_research_state/server.lua similarity index 100% rename from fle/env/factorio/tools/admin/save_research_state/server.lua rename to fle/env/game/tools/admin/save_research_state/server.lua diff --git a/fle/env/factorio/tools/agent.md b/fle/env/game/tools/agent.md similarity index 100% rename from fle/env/factorio/tools/agent.md rename to fle/env/game/tools/agent.md diff --git a/fle/env/factorio/tools/agent/can_place_entity/agent.md b/fle/env/game/tools/agent/can_place_entity/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/can_place_entity/agent.md rename to fle/env/game/tools/agent/can_place_entity/agent.md diff --git a/fle/env/factorio/tools/agent/can_place_entity/client.py b/fle/env/game/tools/agent/can_place_entity/client.py similarity index 100% rename from fle/env/factorio/tools/agent/can_place_entity/client.py rename to fle/env/game/tools/agent/can_place_entity/client.py diff --git a/fle/env/factorio/tools/agent/can_place_entity/server.lua b/fle/env/game/tools/agent/can_place_entity/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/can_place_entity/server.lua rename to fle/env/game/tools/agent/can_place_entity/server.lua diff --git a/fle/env/factorio/tools/agent/connect_entities/agent.md b/fle/env/game/tools/agent/connect_entities/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/connect_entities/agent.md rename to fle/env/game/tools/agent/connect_entities/agent.md diff --git a/fle/env/factorio/tools/agent/connect_entities/client.py b/fle/env/game/tools/agent/connect_entities/client.py similarity index 100% rename from fle/env/factorio/tools/agent/connect_entities/client.py rename to fle/env/game/tools/agent/connect_entities/client.py diff --git a/fle/env/factorio/tools/agent/connect_entities/groupable_entities.py b/fle/env/game/tools/agent/connect_entities/groupable_entities.py similarity index 100% rename from fle/env/factorio/tools/agent/connect_entities/groupable_entities.py rename to fle/env/game/tools/agent/connect_entities/groupable_entities.py diff --git a/fle/env/factorio/tools/agent/connect_entities/path_result.py b/fle/env/game/tools/agent/connect_entities/path_result.py similarity index 100% rename from fle/env/factorio/tools/agent/connect_entities/path_result.py rename to fle/env/game/tools/agent/connect_entities/path_result.py diff --git a/fle/env/factorio/tools/agent/connect_entities/resolver.py b/fle/env/game/tools/agent/connect_entities/resolver.py similarity index 96% rename from fle/env/factorio/tools/agent/connect_entities/resolver.py rename to fle/env/game/tools/agent/connect_entities/resolver.py index 09baa0e84..18c140424 100644 --- a/fle/env/factorio/tools/agent/connect_entities/resolver.py +++ b/fle/env/game/tools/agent/connect_entities/resolver.py @@ -2,7 +2,7 @@ from enum import Enum, auto from typing import Union, List, Optional, Protocol, Tuple -from fle.env.entities import Position, Entity +from fle.env.game.entities import Position, Entity @dataclass diff --git a/fle/env/factorio/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py b/fle/env/game/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py similarity index 100% rename from fle/env/factorio/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py rename to fle/env/game/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py diff --git a/fle/env/factorio/tools/agent/connect_entities/resolvers/power_connection_resolver.py b/fle/env/game/tools/agent/connect_entities/resolvers/power_connection_resolver.py similarity index 98% rename from fle/env/factorio/tools/agent/connect_entities/resolvers/power_connection_resolver.py rename to fle/env/game/tools/agent/connect_entities/resolvers/power_connection_resolver.py index 8a2b4664b..cee10babc 100644 --- a/fle/env/factorio/tools/agent/connect_entities/resolvers/power_connection_resolver.py +++ b/fle/env/game/tools/agent/connect_entities/resolvers/power_connection_resolver.py @@ -1,5 +1,5 @@ from typing import Union, Tuple, List, Set -from fle.env.entities import Position, Entity, ElectricityGroup +from fle.env.game.entities import Position, Entity, ElectricityGroup from fle.env.factorio.tools.agent.connect_entities.resolver import Resolver diff --git a/fle/env/factorio/tools/agent/connect_entities/resolvers/transport_connection_resolver.py b/fle/env/game/tools/agent/connect_entities/resolvers/transport_connection_resolver.py similarity index 99% rename from fle/env/factorio/tools/agent/connect_entities/resolvers/transport_connection_resolver.py rename to fle/env/game/tools/agent/connect_entities/resolvers/transport_connection_resolver.py index c376ec7bf..d4fec42df 100644 --- a/fle/env/factorio/tools/agent/connect_entities/resolvers/transport_connection_resolver.py +++ b/fle/env/game/tools/agent/connect_entities/resolvers/transport_connection_resolver.py @@ -1,6 +1,6 @@ import math from typing import Union, Tuple, List -from fle.env.entities import ( +from fle.env.game.entities import ( Position, Entity, BeltGroup, diff --git a/fle/env/factorio/tools/agent/connect_entities/server.lua b/fle/env/game/tools/agent/connect_entities/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/connect_entities/server.lua rename to fle/env/game/tools/agent/connect_entities/server.lua diff --git a/fle/env/factorio/tools/agent/craft_item/agent.md b/fle/env/game/tools/agent/craft_item/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/craft_item/agent.md rename to fle/env/game/tools/agent/craft_item/agent.md diff --git a/fle/env/factorio/tools/agent/craft_item/client.py b/fle/env/game/tools/agent/craft_item/client.py similarity index 100% rename from fle/env/factorio/tools/agent/craft_item/client.py rename to fle/env/game/tools/agent/craft_item/client.py diff --git a/fle/env/factorio/tools/agent/craft_item/server.lua b/fle/env/game/tools/agent/craft_item/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/craft_item/server.lua rename to fle/env/game/tools/agent/craft_item/server.lua diff --git a/fle/env/factorio/tools/agent/extract_item/agent.md b/fle/env/game/tools/agent/extract_item/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/extract_item/agent.md rename to fle/env/game/tools/agent/extract_item/agent.md diff --git a/fle/env/factorio/tools/agent/extract_item/client.py b/fle/env/game/tools/agent/extract_item/client.py similarity index 97% rename from fle/env/factorio/tools/agent/extract_item/client.py rename to fle/env/game/tools/agent/extract_item/client.py index d36f55862..277635acb 100644 --- a/fle/env/factorio/tools/agent/extract_item/client.py +++ b/fle/env/game/tools/agent/extract_item/client.py @@ -1,6 +1,6 @@ from typing import Union -from fle.env.entities import Position, Entity +from fle.env.game.entities import Position, Entity from fle.env.game_types import Prototype from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/agent/extract_item/server.lua b/fle/env/game/tools/agent/extract_item/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/extract_item/server.lua rename to fle/env/game/tools/agent/extract_item/server.lua diff --git a/fle/env/factorio/tools/agent/get_connection_amount/agent.md b/fle/env/game/tools/agent/get_connection_amount/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/get_connection_amount/agent.md rename to fle/env/game/tools/agent/get_connection_amount/agent.md diff --git a/fle/env/factorio/tools/agent/get_connection_amount/client.py b/fle/env/game/tools/agent/get_connection_amount/client.py similarity index 95% rename from fle/env/factorio/tools/agent/get_connection_amount/client.py rename to fle/env/game/tools/agent/get_connection_amount/client.py index c174e8359..a1dc9730f 100644 --- a/fle/env/factorio/tools/agent/get_connection_amount/client.py +++ b/fle/env/game/tools/agent/get_connection_amount/client.py @@ -1,6 +1,6 @@ from typing import Union -from fle.env.entities import Entity, Position, EntityGroup +from fle.env.game.entities import Entity, Position, EntityGroup from fle.env.game_types import Prototype from fle.env.factorio.tools.agent.connect_entities.client import ConnectEntities from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/agent/get_connection_amount/server.lua b/fle/env/game/tools/agent/get_connection_amount/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/get_connection_amount/server.lua rename to fle/env/game/tools/agent/get_connection_amount/server.lua diff --git a/fle/env/factorio/tools/agent/get_entities/agent.md b/fle/env/game/tools/agent/get_entities/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/get_entities/agent.md rename to fle/env/game/tools/agent/get_entities/agent.md diff --git a/fle/env/factorio/tools/agent/get_entities/client.py b/fle/env/game/tools/agent/get_entities/client.py similarity index 99% rename from fle/env/factorio/tools/agent/get_entities/client.py rename to fle/env/game/tools/agent/get_entities/client.py index c35c28c4c..49c27328a 100644 --- a/fle/env/factorio/tools/agent/get_entities/client.py +++ b/fle/env/game/tools/agent/get_entities/client.py @@ -1,6 +1,6 @@ from time import sleep from typing import List, Set, Union -from fle.env.entities import Position, Entity, EntityGroup +from fle.env.game.entities import Position, Entity, EntityGroup from fle.env.game_types import Prototype from fle.env.factorio.tools.agent.connect_entities.groupable_entities import ( agglomerate_groupable_entities, diff --git a/fle/env/factorio/tools/agent/get_entities/server.lua b/fle/env/game/tools/agent/get_entities/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/get_entities/server.lua rename to fle/env/game/tools/agent/get_entities/server.lua diff --git a/fle/env/factorio/tools/agent/get_entity/agent.md b/fle/env/game/tools/agent/get_entity/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/get_entity/agent.md rename to fle/env/game/tools/agent/get_entity/agent.md diff --git a/fle/env/factorio/tools/agent/get_entity/client.py b/fle/env/game/tools/agent/get_entity/client.py similarity index 98% rename from fle/env/factorio/tools/agent/get_entity/client.py rename to fle/env/game/tools/agent/get_entity/client.py index a8879e95b..2089b8116 100644 --- a/fle/env/factorio/tools/agent/get_entity/client.py +++ b/fle/env/game/tools/agent/get_entity/client.py @@ -1,6 +1,6 @@ from time import sleep -from fle.env.entities import Position, Entity +from fle.env.game.entities import Position, Entity from fle.env.game_types import Prototype from fle.env.factorio.tools.agent.get_entities.client import GetEntities diff --git a/fle/env/factorio/tools/agent/get_entity/server.lua b/fle/env/game/tools/agent/get_entity/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/get_entity/server.lua rename to fle/env/game/tools/agent/get_entity/server.lua diff --git a/fle/env/factorio/tools/agent/get_prototype_recipe/agent.md b/fle/env/game/tools/agent/get_prototype_recipe/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/get_prototype_recipe/agent.md rename to fle/env/game/tools/agent/get_prototype_recipe/agent.md diff --git a/fle/env/factorio/tools/agent/get_prototype_recipe/client.py b/fle/env/game/tools/agent/get_prototype_recipe/client.py similarity index 100% rename from fle/env/factorio/tools/agent/get_prototype_recipe/client.py rename to fle/env/game/tools/agent/get_prototype_recipe/client.py diff --git a/fle/env/factorio/tools/agent/get_prototype_recipe/server.lua b/fle/env/game/tools/agent/get_prototype_recipe/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/get_prototype_recipe/server.lua rename to fle/env/game/tools/agent/get_prototype_recipe/server.lua diff --git a/fle/env/factorio/tools/agent/get_research_progress/agent.md b/fle/env/game/tools/agent/get_research_progress/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/get_research_progress/agent.md rename to fle/env/game/tools/agent/get_research_progress/agent.md diff --git a/fle/env/factorio/tools/agent/get_research_progress/client.py b/fle/env/game/tools/agent/get_research_progress/client.py similarity index 100% rename from fle/env/factorio/tools/agent/get_research_progress/client.py rename to fle/env/game/tools/agent/get_research_progress/client.py diff --git a/fle/env/factorio/tools/agent/get_research_progress/server.lua b/fle/env/game/tools/agent/get_research_progress/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/get_research_progress/server.lua rename to fle/env/game/tools/agent/get_research_progress/server.lua diff --git a/fle/env/factorio/tools/agent/get_resource_patch/agent.md b/fle/env/game/tools/agent/get_resource_patch/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/get_resource_patch/agent.md rename to fle/env/game/tools/agent/get_resource_patch/agent.md diff --git a/fle/env/factorio/tools/agent/get_resource_patch/client.py b/fle/env/game/tools/agent/get_resource_patch/client.py similarity index 97% rename from fle/env/factorio/tools/agent/get_resource_patch/client.py rename to fle/env/game/tools/agent/get_resource_patch/client.py index ad63002c2..74177a1ab 100644 --- a/fle/env/factorio/tools/agent/get_resource_patch/client.py +++ b/fle/env/game/tools/agent/get_resource_patch/client.py @@ -1,6 +1,6 @@ from typing import Optional -from fle.env.entities import Position, ResourcePatch, BoundingBox +from fle.env.game.entities import Position, ResourcePatch, BoundingBox from fle.env.game_types import Resource from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/agent/get_resource_patch/server.lua b/fle/env/game/tools/agent/get_resource_patch/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/get_resource_patch/server.lua rename to fle/env/game/tools/agent/get_resource_patch/server.lua diff --git a/fle/env/factorio/tools/agent/harvest_resource/agent.md b/fle/env/game/tools/agent/harvest_resource/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/harvest_resource/agent.md rename to fle/env/game/tools/agent/harvest_resource/agent.md diff --git a/fle/env/factorio/tools/agent/harvest_resource/client.py b/fle/env/game/tools/agent/harvest_resource/client.py similarity index 98% rename from fle/env/factorio/tools/agent/harvest_resource/client.py rename to fle/env/game/tools/agent/harvest_resource/client.py index 6c13c4128..d7cd15c29 100644 --- a/fle/env/factorio/tools/agent/harvest_resource/client.py +++ b/fle/env/game/tools/agent/harvest_resource/client.py @@ -1,6 +1,6 @@ from time import sleep -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Resource from fle.env.factorio.tools.agent.get_entity.client import GetEntity from fle.env.factorio.tools.agent.move_to.client import MoveTo diff --git a/fle/env/factorio/tools/agent/harvest_resource/server.lua b/fle/env/game/tools/agent/harvest_resource/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/harvest_resource/server.lua rename to fle/env/game/tools/agent/harvest_resource/server.lua diff --git a/fle/env/factorio/tools/agent/insert_item/agent.md b/fle/env/game/tools/agent/insert_item/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/insert_item/agent.md rename to fle/env/game/tools/agent/insert_item/agent.md diff --git a/fle/env/factorio/tools/agent/insert_item/client.py b/fle/env/game/tools/agent/insert_item/client.py similarity index 98% rename from fle/env/factorio/tools/agent/insert_item/client.py rename to fle/env/game/tools/agent/insert_item/client.py index 8a271836b..b7c53cf36 100644 --- a/fle/env/factorio/tools/agent/insert_item/client.py +++ b/fle/env/game/tools/agent/insert_item/client.py @@ -1,7 +1,7 @@ from time import sleep from typing import Union -from fle.env.entities import Entity, EntityGroup, Position, BeltGroup, PipeGroup +from fle.env.game.entities import Entity, EntityGroup, Position, BeltGroup, PipeGroup from fle.env.game_types import Prototype from fle.env.factorio.tools.agent.get_entities.client import GetEntities from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/agent/insert_item/server.lua b/fle/env/game/tools/agent/insert_item/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/insert_item/server.lua rename to fle/env/game/tools/agent/insert_item/server.lua diff --git a/fle/env/factorio/tools/agent/inspect_inventory/agent.md b/fle/env/game/tools/agent/inspect_inventory/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/inspect_inventory/agent.md rename to fle/env/game/tools/agent/inspect_inventory/agent.md diff --git a/fle/env/factorio/tools/agent/inspect_inventory/client.py b/fle/env/game/tools/agent/inspect_inventory/client.py similarity index 100% rename from fle/env/factorio/tools/agent/inspect_inventory/client.py rename to fle/env/game/tools/agent/inspect_inventory/client.py diff --git a/fle/env/factorio/tools/agent/inspect_inventory/server.lua b/fle/env/game/tools/agent/inspect_inventory/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/inspect_inventory/server.lua rename to fle/env/game/tools/agent/inspect_inventory/server.lua diff --git a/fle/env/factorio/tools/agent/launch_rocket/agent.md b/fle/env/game/tools/agent/launch_rocket/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/launch_rocket/agent.md rename to fle/env/game/tools/agent/launch_rocket/agent.md diff --git a/fle/env/factorio/tools/agent/launch_rocket/client.py b/fle/env/game/tools/agent/launch_rocket/client.py similarity index 94% rename from fle/env/factorio/tools/agent/launch_rocket/client.py rename to fle/env/game/tools/agent/launch_rocket/client.py index 869d99efb..a393ec65e 100644 --- a/fle/env/factorio/tools/agent/launch_rocket/client.py +++ b/fle/env/game/tools/agent/launch_rocket/client.py @@ -1,6 +1,6 @@ from typing import Union, cast -from fle.env.entities import Position, RocketSilo +from fle.env.game.entities import Position, RocketSilo from fle.env.game_types import Prototype from fle.env.factorio.tools.agent.get_entity.client import GetEntity from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/agent/launch_rocket/server.lua b/fle/env/game/tools/agent/launch_rocket/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/launch_rocket/server.lua rename to fle/env/game/tools/agent/launch_rocket/server.lua diff --git a/fle/env/factorio/tools/agent/move_to/agent.md b/fle/env/game/tools/agent/move_to/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/move_to/agent.md rename to fle/env/game/tools/agent/move_to/agent.md diff --git a/fle/env/factorio/tools/agent/move_to/client.py b/fle/env/game/tools/agent/move_to/client.py similarity index 97% rename from fle/env/factorio/tools/agent/move_to/client.py rename to fle/env/game/tools/agent/move_to/client.py index 427ac8f20..fdf046532 100644 --- a/fle/env/factorio/tools/agent/move_to/client.py +++ b/fle/env/game/tools/agent/move_to/client.py @@ -1,8 +1,8 @@ import math from time import sleep -from fle.env.entities import Position -from fle.env.instance import NONE +from fle.env.game.entities import Position +from fle.env.game.instance import NONE from fle.env.game_types import Prototype from fle.env.factorio.tools.admin.get_path.client import GetPath from fle.env.factorio.tools.admin.request_path.client import RequestPath diff --git a/fle/env/factorio/tools/agent/move_to/server.lua b/fle/env/game/tools/agent/move_to/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/move_to/server.lua rename to fle/env/game/tools/agent/move_to/server.lua diff --git a/fle/env/factorio/tools/agent/nearest/agent.md b/fle/env/game/tools/agent/nearest/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/nearest/agent.md rename to fle/env/game/tools/agent/nearest/agent.md diff --git a/fle/env/factorio/tools/agent/nearest/client.py b/fle/env/game/tools/agent/nearest/client.py similarity index 97% rename from fle/env/factorio/tools/agent/nearest/client.py rename to fle/env/game/tools/agent/nearest/client.py index 98ec2460e..a3b7d17e2 100644 --- a/fle/env/factorio/tools/agent/nearest/client.py +++ b/fle/env/game/tools/agent/nearest/client.py @@ -1,6 +1,6 @@ from typing import Union -from fle.env.entities import Position, ResourcePatch +from fle.env.game.entities import Position, ResourcePatch from fle.env.game_types import Prototype, Resource from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/agent/nearest/server.lua b/fle/env/game/tools/agent/nearest/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/nearest/server.lua rename to fle/env/game/tools/agent/nearest/server.lua diff --git a/fle/env/factorio/tools/agent/nearest_buildable/agent.md b/fle/env/game/tools/agent/nearest_buildable/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/nearest_buildable/agent.md rename to fle/env/game/tools/agent/nearest_buildable/agent.md diff --git a/fle/env/factorio/tools/agent/nearest_buildable/client.py b/fle/env/game/tools/agent/nearest_buildable/client.py similarity index 100% rename from fle/env/factorio/tools/agent/nearest_buildable/client.py rename to fle/env/game/tools/agent/nearest_buildable/client.py diff --git a/fle/env/factorio/tools/agent/nearest_buildable/server.lua b/fle/env/game/tools/agent/nearest_buildable/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/nearest_buildable/server.lua rename to fle/env/game/tools/agent/nearest_buildable/server.lua diff --git a/fle/env/factorio/tools/agent/pickup_entity/agent.md b/fle/env/game/tools/agent/pickup_entity/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/pickup_entity/agent.md rename to fle/env/game/tools/agent/pickup_entity/agent.md diff --git a/fle/env/factorio/tools/agent/pickup_entity/client.py b/fle/env/game/tools/agent/pickup_entity/client.py similarity index 100% rename from fle/env/factorio/tools/agent/pickup_entity/client.py rename to fle/env/game/tools/agent/pickup_entity/client.py diff --git a/fle/env/factorio/tools/agent/pickup_entity/server.lua b/fle/env/game/tools/agent/pickup_entity/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/pickup_entity/server.lua rename to fle/env/game/tools/agent/pickup_entity/server.lua diff --git a/fle/env/factorio/tools/agent/place_entity/agent.md b/fle/env/game/tools/agent/place_entity/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/place_entity/agent.md rename to fle/env/game/tools/agent/place_entity/agent.md diff --git a/fle/env/factorio/tools/agent/place_entity/client.py b/fle/env/game/tools/agent/place_entity/client.py similarity index 98% rename from fle/env/factorio/tools/agent/place_entity/client.py rename to fle/env/game/tools/agent/place_entity/client.py index 1fedfd9a7..bdcfe6c95 100644 --- a/fle/env/factorio/tools/agent/place_entity/client.py +++ b/fle/env/game/tools/agent/place_entity/client.py @@ -1,6 +1,6 @@ from time import sleep -from fle.env.entities import Position, Entity +from fle.env.game.entities import Position, Entity from fle.env import DirectionInternal, Direction from fle.env.game_types import Prototype from fle.env.factorio.tools.agent.get_entity.client import GetEntity diff --git a/fle/env/factorio/tools/agent/place_entity/server.lua b/fle/env/game/tools/agent/place_entity/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/place_entity/server.lua rename to fle/env/game/tools/agent/place_entity/server.lua diff --git a/fle/env/factorio/tools/agent/place_entity_next_to/agent.md b/fle/env/game/tools/agent/place_entity_next_to/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/place_entity_next_to/agent.md rename to fle/env/game/tools/agent/place_entity_next_to/agent.md diff --git a/fle/env/factorio/tools/agent/place_entity_next_to/client.py b/fle/env/game/tools/agent/place_entity_next_to/client.py similarity index 98% rename from fle/env/factorio/tools/agent/place_entity_next_to/client.py rename to fle/env/game/tools/agent/place_entity_next_to/client.py index 57192d5b4..5964908ea 100644 --- a/fle/env/factorio/tools/agent/place_entity_next_to/client.py +++ b/fle/env/game/tools/agent/place_entity_next_to/client.py @@ -1,4 +1,4 @@ -from fle.env.entities import Position, Entity +from fle.env.game.entities import Position, Entity from fle.env import DirectionInternal from fle.env.game_types import Prototype from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/agent/place_entity_next_to/server.lua b/fle/env/game/tools/agent/place_entity_next_to/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/place_entity_next_to/server.lua rename to fle/env/game/tools/agent/place_entity_next_to/server.lua diff --git a/fle/env/factorio/tools/agent/print/agent.md b/fle/env/game/tools/agent/print/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/print/agent.md rename to fle/env/game/tools/agent/print/agent.md diff --git a/fle/env/factorio/tools/agent/print/client.py b/fle/env/game/tools/agent/print/client.py similarity index 95% rename from fle/env/factorio/tools/agent/print/client.py rename to fle/env/game/tools/agent/print/client.py index c0c28a95c..185e8b5c6 100644 --- a/fle/env/factorio/tools/agent/print/client.py +++ b/fle/env/game/tools/agent/print/client.py @@ -1,4 +1,4 @@ -from fle.env.entities import Entity, Inventory, Position +from fle.env.game.entities import Entity, Inventory, Position from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/agent/print/server.lua b/fle/env/game/tools/agent/print/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/print/server.lua rename to fle/env/game/tools/agent/print/server.lua diff --git a/fle/env/factorio/tools/agent/rotate_entity/agent.md b/fle/env/game/tools/agent/rotate_entity/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/rotate_entity/agent.md rename to fle/env/game/tools/agent/rotate_entity/agent.md diff --git a/fle/env/factorio/tools/agent/rotate_entity/client.py b/fle/env/game/tools/agent/rotate_entity/client.py similarity index 98% rename from fle/env/factorio/tools/agent/rotate_entity/client.py rename to fle/env/game/tools/agent/rotate_entity/client.py index 87b340e95..ff34cfb30 100644 --- a/fle/env/factorio/tools/agent/rotate_entity/client.py +++ b/fle/env/game/tools/agent/rotate_entity/client.py @@ -1,4 +1,4 @@ -from fle.env.entities import ( +from fle.env.game.entities import ( Entity, Direction as DirectionA, AssemblingMachine, diff --git a/fle/env/factorio/tools/agent/rotate_entity/server.lua b/fle/env/game/tools/agent/rotate_entity/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/rotate_entity/server.lua rename to fle/env/game/tools/agent/rotate_entity/server.lua diff --git a/fle/env/factorio/tools/agent/score/client.py b/fle/env/game/tools/agent/score/client.py similarity index 100% rename from fle/env/factorio/tools/agent/score/client.py rename to fle/env/game/tools/agent/score/client.py diff --git a/fle/env/factorio/tools/agent/score/server.lua b/fle/env/game/tools/agent/score/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/score/server.lua rename to fle/env/game/tools/agent/score/server.lua diff --git a/fle/env/factorio/tools/agent/send_message/agent.md b/fle/env/game/tools/agent/send_message/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/send_message/agent.md rename to fle/env/game/tools/agent/send_message/agent.md diff --git a/fle/env/factorio/tools/agent/send_message/client.py b/fle/env/game/tools/agent/send_message/client.py similarity index 100% rename from fle/env/factorio/tools/agent/send_message/client.py rename to fle/env/game/tools/agent/send_message/client.py diff --git a/fle/env/factorio/tools/agent/send_message/server.lua b/fle/env/game/tools/agent/send_message/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/send_message/server.lua rename to fle/env/game/tools/agent/send_message/server.lua diff --git a/fle/env/factorio/tools/agent/set_entity_recipe/agent.md b/fle/env/game/tools/agent/set_entity_recipe/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/set_entity_recipe/agent.md rename to fle/env/game/tools/agent/set_entity_recipe/agent.md diff --git a/fle/env/factorio/tools/agent/set_entity_recipe/client.py b/fle/env/game/tools/agent/set_entity_recipe/client.py similarity index 98% rename from fle/env/factorio/tools/agent/set_entity_recipe/client.py rename to fle/env/game/tools/agent/set_entity_recipe/client.py index a991cbed5..c14f06ac9 100644 --- a/fle/env/factorio/tools/agent/set_entity_recipe/client.py +++ b/fle/env/game/tools/agent/set_entity_recipe/client.py @@ -1,6 +1,6 @@ from typing import Union -from fle.env.entities import Entity +from fle.env.game.entities import Entity from fle.env.game_types import Prototype, RecipeName from fle.env.factorio.tools import Tool diff --git a/fle/env/factorio/tools/agent/set_entity_recipe/server.lua b/fle/env/game/tools/agent/set_entity_recipe/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/set_entity_recipe/server.lua rename to fle/env/game/tools/agent/set_entity_recipe/server.lua diff --git a/fle/env/factorio/tools/agent/set_research/agent.md b/fle/env/game/tools/agent/set_research/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/set_research/agent.md rename to fle/env/game/tools/agent/set_research/agent.md diff --git a/fle/env/factorio/tools/agent/set_research/client.py b/fle/env/game/tools/agent/set_research/client.py similarity index 100% rename from fle/env/factorio/tools/agent/set_research/client.py rename to fle/env/game/tools/agent/set_research/client.py diff --git a/fle/env/factorio/tools/agent/set_research/server.lua b/fle/env/game/tools/agent/set_research/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/set_research/server.lua rename to fle/env/game/tools/agent/set_research/server.lua diff --git a/fle/env/factorio/tools/agent/shift_entity/client.py b/fle/env/game/tools/agent/shift_entity/client.py similarity index 98% rename from fle/env/factorio/tools/agent/shift_entity/client.py rename to fle/env/game/tools/agent/shift_entity/client.py index 5b64013e3..e57eb1adb 100644 --- a/fle/env/factorio/tools/agent/shift_entity/client.py +++ b/fle/env/game/tools/agent/shift_entity/client.py @@ -1,6 +1,6 @@ from typing import Union -from fle.env.entities import Entity, Direction +from fle.env.game.entities import Entity, Direction from fle.env.game_types import prototype_by_name from fle.env import DirectionInternal as DirectionA from fle.env.factorio.tools.agent.can_place_entity.client import CanPlaceEntity diff --git a/fle/env/factorio/tools/agent/sleep/agent.md b/fle/env/game/tools/agent/sleep/agent.md similarity index 100% rename from fle/env/factorio/tools/agent/sleep/agent.md rename to fle/env/game/tools/agent/sleep/agent.md diff --git a/fle/env/factorio/tools/agent/sleep/client.py b/fle/env/game/tools/agent/sleep/client.py similarity index 100% rename from fle/env/factorio/tools/agent/sleep/client.py rename to fle/env/game/tools/agent/sleep/client.py diff --git a/fle/env/factorio/tools/agent/sleep/server.lua b/fle/env/game/tools/agent/sleep/server.lua similarity index 100% rename from fle/env/factorio/tools/agent/sleep/server.lua rename to fle/env/game/tools/agent/sleep/server.lua diff --git a/fle/env/factorio/tools/controller.py b/fle/env/game/tools/controller.py similarity index 95% rename from fle/env/factorio/tools/controller.py rename to fle/env/game/tools/controller.py index ab3de6f18..ad057e014 100644 --- a/fle/env/factorio/tools/controller.py +++ b/fle/env/game/tools/controller.py @@ -1,13 +1,14 @@ import time from timeit import default_timer as timer -from typing import List, Tuple, Dict, Any +from typing import Any, Dict, List, Tuple -from slpp import slpp as lua, ParseError +from slpp import ParseError +from slpp import slpp as lua -from fle.env.entities import Direction -from fle.env.namespace import FactorioNamespace -from fle.env.instance import FactorioServer -from fle.env.utils.rcon import _lua2python +from fle.env.game.entities import Direction +from fle.env.game.instance import FactorioClient +from fle.env.game.namespace import FactorioNamespace +from fle.services.rcon import _lua2python COMMAND = "/silent-command" @@ -15,7 +16,7 @@ class Controller: def __init__( self, - factorio_server: "FactorioServer", + factorio_server: "FactorioClient", game_state: "FactorioNamespace", *args, **kwargs, diff --git a/fle/env/factorio/tools/init.py b/fle/env/game/tools/init.py similarity index 76% rename from fle/env/factorio/tools/init.py rename to fle/env/game/tools/init.py index 9e705e3c1..7f270ca07 100644 --- a/fle/env/factorio/tools/init.py +++ b/fle/env/game/tools/init.py @@ -1,11 +1,11 @@ from fle.env.factorio.tools.controller import Controller -from fle.env.instance import FactorioInstance, FactorioServer +from fle.env.game.instance import FactorioInstance, FactorioClient class Init(Controller): def __init__( self, - factorio_server: "FactorioServer", + factorio_server: "FactorioClient", game_state: "FactorioInstance", *args, **kwargs, diff --git a/fle/env/factorio/tools/tool.py b/fle/env/game/tools/tool.py similarity index 85% rename from fle/env/factorio/tools/tool.py rename to fle/env/game/tools/tool.py index e11a54489..39ccb0ab3 100644 --- a/fle/env/factorio/tools/tool.py +++ b/fle/env/game/tools/tool.py @@ -1,15 +1,15 @@ from typing import Tuple, Union -from fle.env.entities import Position, Entity -from fle.env.namespace import FactorioNamespace -from fle.env.instance import FactorioServer +from fle.env.game.entities import Position, Entity +from fle.env.game.namespace import FactorioNamespace +from fle.env.game.instance import FactorioClient from fle.env.factorio.tools.controller import Controller class Tool(Controller): def __init__( self, - factorio_server: "FactorioServer", + factorio_server: "FactorioClient", game_state: "FactorioNamespace", *args, **kwargs, diff --git a/fle/env/game_session.py b/fle/env/game_session.py new file mode 100644 index 000000000..df408f9fb --- /dev/null +++ b/fle/env/game_session.py @@ -0,0 +1,67 @@ +from typing import List + +# from fle.env.run_envs import get_local_container_ips +from fle.env.game.instance import FactorioInstance +from fle.env.game.game_state import GameState +from fle.commons.models.program import Program + +class FactorioGameSession: + instance: FactorioInstance + game_state: GameState + programs: List[Program] + + def __init__(self, instance: FactorioInstance, game_state: GameState): + self.instance = instance + self.game_state = game_state + + async def create_factorio_instance( + instance_id: int, num_agents: int = 1, agent_cards: Optional[List[AgentCard]] = None + ) -> FactorioInstance: + """Create and asynchronously initialize a single Factorio instance""" + ips, udp_ports, tcp_ports = get_local_container_ips() + + common_kwargs = { + "address": ips[instance_id], + "tcp_port": tcp_ports[instance_id], + "bounding_box": 200, + "fast": True, + "cache_scripts": True, + "inventory": {}, + "all_technologies_researched": True, + "num_agents": num_agents, + } + + if num_agents > 1: + instance = await A2AFactorioInstance.create( + **common_kwargs, agent_cards=agent_cards + ) + else: + instance = FactorioInstance(**common_kwargs) + + instance.set_speed(10) + return instance + + +async def run_trajectory(process_id: int, config: EvalConfig): + """Entry point for running a single trajectory""" + db_client = await create_db_client() + instance = await create_factorio_instance( + process_id, len(config.agents), config.agent_cards + ) + evaluator = SimpleFactorioEvaluator( + db_client=db_client, instance=instance, value_accrual_time=1, error_penalty=0 + ) + task = config.task + task.setup(instance) + + runner = TrajectoryRunner(config.agents, db_client, evaluator, config, process_id) + + await runner.run() + await db_client.cleanup() + +async def get_next_version() -> int: + """Get next available version number""" + db_client = await create_db_client() + version = await db_client.get_largest_version() + await db_client.cleanup() + return version + 1 diff --git a/fle/env/gym_env/action.py b/fle/env/gym_env/action.py index 049835042..719c992d1 100644 --- a/fle/env/gym_env/action.py +++ b/fle/env/gym_env/action.py @@ -1,7 +1,7 @@ from dataclasses import dataclass from typing import Optional, Dict, Any -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState @dataclass diff --git a/fle/env/gym_env/config.py b/fle/env/gym_env/config.py index 300277181..bc730e343 100644 --- a/fle/env/gym_env/config.py +++ b/fle/env/gym_env/config.py @@ -3,7 +3,7 @@ from fle.agents.gym_agent import GymAgent from fle.env.gym_env.observation_formatter import BasicObservationFormatter -from fle.eval.tasks import TaskABC +from fle.env.tasks import TaskABC from a2a.types import AgentCard diff --git a/fle/env/gym_env/example_usage.py b/fle/env/gym_env/example_usage.py index 4470cdae3..54f64351a 100644 --- a/fle/env/gym_env/example_usage.py +++ b/fle/env/gym_env/example_usage.py @@ -25,9 +25,9 @@ # Add the project root to the path so we can import our modules sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..")) -from fle.env.gym_env.registry import list_available_environments, get_environment_info +from fle.env.registry import list_available_environments, get_environment_info from fle.env.gym_env.action import Action -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState def print_environment_list(env_ids: List[str], detailed: bool = False): diff --git a/fle/env/gym_env/observation.py b/fle/env/gym_env/observation.py index 14fc716ac..04e6a8527 100644 --- a/fle/env/gym_env/observation.py +++ b/fle/env/gym_env/observation.py @@ -6,7 +6,7 @@ from fle.commons.models.research_state import ResearchState from fle.commons.models.achievements import ProductionFlows from fle.agents import TaskResponse -from fle.env.entities import Inventory +from fle.env.game.entities import Inventory @dataclass diff --git a/fle/env/gym_env/observation_formatter.py b/fle/env/gym_env/observation_formatter.py index e5ee82235..4e968c62a 100644 --- a/fle/env/gym_env/observation_formatter.py +++ b/fle/env/gym_env/observation_formatter.py @@ -1,9 +1,9 @@ -from dataclasses import dataclass import pickle import re +from dataclasses import dataclass +from typing import Any, Dict, List, Optional from fle.env.gym_env.observation import Observation -from typing import Any, Dict, List, Optional @dataclass diff --git a/fle/env/gym_env/run_eval.py b/fle/env/gym_env/run_eval.py index cf0c2934b..cc99d106a 100644 --- a/fle/env/gym_env/run_eval.py +++ b/fle/env/gym_env/run_eval.py @@ -7,16 +7,16 @@ import gym from dotenv import load_dotenv + +from fle.agents.gym_agent import GymAgent +from fle.commons.cluster_ips import get_local_container_ips from fle.env.gym_env.config import GymEvalConfig, GymRunConfig from fle.env.gym_env.observation_formatter import BasicObservationFormatter from fle.env.gym_env.system_prompt_formatter import SystemPromptFormatter -from fle.env.gym_env.registry import get_environment_info, list_available_environments from fle.env.gym_env.trajectory_runner import GymTrajectoryRunner - -from fle.agents.gym_agent import GymAgent -from fle.commons.cluster_ips import get_local_container_ips -from fle.commons.db_client import create_db_client +from fle.env.registry import get_environment_info, list_available_environments from fle.eval.algorithms.independent import get_next_version +from fle.services.db_client import create_db_client load_dotenv() diff --git a/fle/env/protocols/mcp/repository.py b/fle/env/protocols/mcp/repository.py index 9e4a91f8b..e65157bd0 100644 --- a/fle/env/protocols/mcp/repository.py +++ b/fle/env/protocols/mcp/repository.py @@ -6,7 +6,7 @@ from dulwich.repo import Repo from fle.env import FactorioInstance -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState class FactorioMCPRepository: diff --git a/fle/env/protocols/mcp/tools.py b/fle/env/protocols/mcp/tools.py index 9e82a8803..c148a1709 100644 --- a/fle/env/protocols/mcp/tools.py +++ b/fle/env/protocols/mcp/tools.py @@ -3,8 +3,8 @@ from typing import Dict from mcp.server.fastmcp import Image -from fle.env.entities import Position -from fle.commons.models.game_state import GameState +from fle.env.game.entities import Position +from fle.env.game.game_state import GameState from fle.env.utils.controller_loader.system_prompt_generator import ( SystemPromptGenerator, ) diff --git a/fle/env/protocols/mcp/version_control.py b/fle/env/protocols/mcp/version_control.py index 1e3aba838..07de33e15 100644 --- a/fle/env/protocols/mcp/version_control.py +++ b/fle/env/protocols/mcp/version_control.py @@ -9,7 +9,7 @@ """ from mcp.server.fastmcp import Context -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.env.protocols.mcp import mcp from fle.env.protocols.mcp.init import state diff --git a/fle/env/gym_env/registry.py b/fle/env/registry.py similarity index 96% rename from fle/env/gym_env/registry.py rename to fle/env/registry.py index 17eda9f8f..d99d4c445 100644 --- a/fle/env/gym_env/registry.py +++ b/fle/env/registry.py @@ -1,12 +1,13 @@ -import gym import json -from typing import Dict, List, Optional, Any from dataclasses import dataclass +from typing import Any, Dict, List, Optional + +import gym -from fle.env.gym_env.environment import FactorioGymEnv -from fle.eval.tasks import TaskFactory -from fle.env import FactorioInstance from fle.commons.cluster_ips import get_local_container_ips +from fle.env import FactorioInstance +from fle.env.environment import FactorioGymEnv +from fle.env.tasks import TaskFactory @dataclass @@ -29,7 +30,7 @@ class FactorioGymRegistry: def __init__(self): self._environments: Dict[str, GymEnvironmentSpec] = {} # Use the same path construction as TaskFactory for consistency - from fle.eval.tasks.task_factory import TASK_FOLDER + from fle.env.tasks.task_factory import TASK_FOLDER self._task_definitions_path = TASK_FOLDER self._discovered = False diff --git a/fle/eval/tasks/__init__.py b/fle/env/tasks/__init__.py similarity index 96% rename from fle/eval/tasks/__init__.py rename to fle/env/tasks/__init__.py index e9fc2ca00..69295b31c 100644 --- a/fle/eval/tasks/__init__.py +++ b/fle/env/tasks/__init__.py @@ -10,7 +10,7 @@ - Custom task definitions loaded from JSON files Example usage: - from fle.eval.tasks import TaskFactory, ThroughputTask + from fle.env.tasks import TaskFactory, ThroughputTask # Create task from JSON definition task = TaskFactory.create_task("iron_plate_throughput_16.json") diff --git a/fle/eval/tasks/default_task.py b/fle/env/tasks/default_task.py similarity index 97% rename from fle/eval/tasks/default_task.py rename to fle/env/tasks/default_task.py index 55c076c4e..7299a0791 100644 --- a/fle/eval/tasks/default_task.py +++ b/fle/env/tasks/default_task.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Optional from fle.env import FactorioInstance -from fle.eval.tasks import TaskABC +from fle.env.tasks import TaskABC from fle.agents import TaskResponse diff --git a/fle/eval/tasks/task_abc.py b/fle/env/tasks/task_abc.py similarity index 97% rename from fle/eval/tasks/task_abc.py rename to fle/env/tasks/task_abc.py index e1a4a8a80..d6a144fc4 100644 --- a/fle/eval/tasks/task_abc.py +++ b/fle/env/tasks/task_abc.py @@ -2,7 +2,7 @@ from fle.env import Inventory from fle.env import FactorioInstance from fle.agents import TaskResponse -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState class TaskABC: diff --git a/fle/eval/tasks/task_definitions/lab_play/advanced_circuit_throughput.json b/fle/env/tasks/task_definitions/lab_play/advanced_circuit_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/advanced_circuit_throughput.json rename to fle/env/tasks/task_definitions/lab_play/advanced_circuit_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/automation_science_pack_throughput.json b/fle/env/tasks/task_definitions/lab_play/automation_science_pack_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/automation_science_pack_throughput.json rename to fle/env/tasks/task_definitions/lab_play/automation_science_pack_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/battery_throughput.json b/fle/env/tasks/task_definitions/lab_play/battery_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/battery_throughput.json rename to fle/env/tasks/task_definitions/lab_play/battery_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/chemical_science_pack_throughput.json b/fle/env/tasks/task_definitions/lab_play/chemical_science_pack_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/chemical_science_pack_throughput.json rename to fle/env/tasks/task_definitions/lab_play/chemical_science_pack_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/crude_oil_throughput.json b/fle/env/tasks/task_definitions/lab_play/crude_oil_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/crude_oil_throughput.json rename to fle/env/tasks/task_definitions/lab_play/crude_oil_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/electronic_circuit_throughput.json b/fle/env/tasks/task_definitions/lab_play/electronic_circuit_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/electronic_circuit_throughput.json rename to fle/env/tasks/task_definitions/lab_play/electronic_circuit_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/engine_unit_throughput.json b/fle/env/tasks/task_definitions/lab_play/engine_unit_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/engine_unit_throughput.json rename to fle/env/tasks/task_definitions/lab_play/engine_unit_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/inserter_throughput.json b/fle/env/tasks/task_definitions/lab_play/inserter_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/inserter_throughput.json rename to fle/env/tasks/task_definitions/lab_play/inserter_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/iron_gear_wheel_throughput.json b/fle/env/tasks/task_definitions/lab_play/iron_gear_wheel_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/iron_gear_wheel_throughput.json rename to fle/env/tasks/task_definitions/lab_play/iron_gear_wheel_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/iron_ore_throughput.json b/fle/env/tasks/task_definitions/lab_play/iron_ore_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/iron_ore_throughput.json rename to fle/env/tasks/task_definitions/lab_play/iron_ore_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/iron_plate_throughput.json b/fle/env/tasks/task_definitions/lab_play/iron_plate_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/iron_plate_throughput.json rename to fle/env/tasks/task_definitions/lab_play/iron_plate_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/logistics_science_pack_throughput.json b/fle/env/tasks/task_definitions/lab_play/logistics_science_pack_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/logistics_science_pack_throughput.json rename to fle/env/tasks/task_definitions/lab_play/logistics_science_pack_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/low_density_structure_throughput.json b/fle/env/tasks/task_definitions/lab_play/low_density_structure_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/low_density_structure_throughput.json rename to fle/env/tasks/task_definitions/lab_play/low_density_structure_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/military_science_pack_throughput.json b/fle/env/tasks/task_definitions/lab_play/military_science_pack_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/military_science_pack_throughput.json rename to fle/env/tasks/task_definitions/lab_play/military_science_pack_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/petroleum_gas_throughput.json b/fle/env/tasks/task_definitions/lab_play/petroleum_gas_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/petroleum_gas_throughput.json rename to fle/env/tasks/task_definitions/lab_play/petroleum_gas_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/piercing_round_throughput.json b/fle/env/tasks/task_definitions/lab_play/piercing_round_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/piercing_round_throughput.json rename to fle/env/tasks/task_definitions/lab_play/piercing_round_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/plastic_bar_throughput.json b/fle/env/tasks/task_definitions/lab_play/plastic_bar_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/plastic_bar_throughput.json rename to fle/env/tasks/task_definitions/lab_play/plastic_bar_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/processing_unit_throughput.json b/fle/env/tasks/task_definitions/lab_play/processing_unit_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/processing_unit_throughput.json rename to fle/env/tasks/task_definitions/lab_play/processing_unit_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/production_science_pack_throughput.json b/fle/env/tasks/task_definitions/lab_play/production_science_pack_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/production_science_pack_throughput.json rename to fle/env/tasks/task_definitions/lab_play/production_science_pack_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/steel_plate_throughput.json b/fle/env/tasks/task_definitions/lab_play/steel_plate_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/steel_plate_throughput.json rename to fle/env/tasks/task_definitions/lab_play/steel_plate_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/stone_wall_throughput.json b/fle/env/tasks/task_definitions/lab_play/stone_wall_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/stone_wall_throughput.json rename to fle/env/tasks/task_definitions/lab_play/stone_wall_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/sufuric_acid_throughput.json b/fle/env/tasks/task_definitions/lab_play/sufuric_acid_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/sufuric_acid_throughput.json rename to fle/env/tasks/task_definitions/lab_play/sufuric_acid_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/sulfur_throughput.json b/fle/env/tasks/task_definitions/lab_play/sulfur_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/sulfur_throughput.json rename to fle/env/tasks/task_definitions/lab_play/sulfur_throughput.json diff --git a/fle/eval/tasks/task_definitions/lab_play/utility_science_pack_throughput.json b/fle/env/tasks/task_definitions/lab_play/utility_science_pack_throughput.json similarity index 100% rename from fle/eval/tasks/task_definitions/lab_play/utility_science_pack_throughput.json rename to fle/env/tasks/task_definitions/lab_play/utility_science_pack_throughput.json diff --git a/fle/eval/tasks/task_definitions/multiagent/iron_plate_throughput_distrust.json b/fle/env/tasks/task_definitions/multiagent/iron_plate_throughput_distrust.json similarity index 100% rename from fle/eval/tasks/task_definitions/multiagent/iron_plate_throughput_distrust.json rename to fle/env/tasks/task_definitions/multiagent/iron_plate_throughput_distrust.json diff --git a/fle/eval/tasks/task_definitions/multiagent/iron_plate_throughput_free.json b/fle/env/tasks/task_definitions/multiagent/iron_plate_throughput_free.json similarity index 100% rename from fle/eval/tasks/task_definitions/multiagent/iron_plate_throughput_free.json rename to fle/env/tasks/task_definitions/multiagent/iron_plate_throughput_free.json diff --git a/fle/eval/tasks/task_definitions/multiagent/iron_plate_throughput_impostor.json b/fle/env/tasks/task_definitions/multiagent/iron_plate_throughput_impostor.json similarity index 100% rename from fle/eval/tasks/task_definitions/multiagent/iron_plate_throughput_impostor.json rename to fle/env/tasks/task_definitions/multiagent/iron_plate_throughput_impostor.json diff --git a/fle/eval/tasks/task_definitions/unbounded/iron_gear_wheel_throughput_unbounded.json b/fle/env/tasks/task_definitions/unbounded/iron_gear_wheel_throughput_unbounded.json similarity index 100% rename from fle/eval/tasks/task_definitions/unbounded/iron_gear_wheel_throughput_unbounded.json rename to fle/env/tasks/task_definitions/unbounded/iron_gear_wheel_throughput_unbounded.json diff --git a/fle/eval/tasks/task_definitions/unbounded/open_play.json b/fle/env/tasks/task_definitions/unbounded/open_play.json similarity index 100% rename from fle/eval/tasks/task_definitions/unbounded/open_play.json rename to fle/env/tasks/task_definitions/unbounded/open_play.json diff --git a/fle/eval/tasks/task_factory.py b/fle/env/tasks/task_factory.py similarity index 83% rename from fle/eval/tasks/task_factory.py rename to fle/env/tasks/task_factory.py index a0befaed5..9a880adab 100644 --- a/fle/eval/tasks/task_factory.py +++ b/fle/env/tasks/task_factory.py @@ -1,10 +1,9 @@ -from fle.eval.tasks import ThroughputTask -from fle.eval.tasks import DefaultTask -from fle.eval.tasks import TaskABC -from fle.eval.tasks import UnboundedThroughputTask -from pathlib import Path import json import os +from pathlib import Path + +from fle.env.tasks import (DefaultTask, TaskABC, ThroughputTask, + UnboundedThroughputTask) TASK_FOLDER = Path(os.path.dirname(__file__), "task_definitions") diff --git a/fle/eval/tasks/throughput_task.py b/fle/env/tasks/throughput_task.py similarity index 99% rename from fle/eval/tasks/throughput_task.py rename to fle/env/tasks/throughput_task.py index 48fd645ae..51b05c890 100644 --- a/fle/eval/tasks/throughput_task.py +++ b/fle/env/tasks/throughput_task.py @@ -1,7 +1,7 @@ from typing import Any, Dict, List, Optional from fle.env import Entity from fle.env import FactorioInstance -from fle.eval.tasks import TaskABC +from fle.env.tasks import TaskABC from fle.env.utils.achievements import eval_program_with_achievements from fle.agents import TaskResponse diff --git a/fle/eval/tasks/unbounded_throughput_task.py b/fle/env/tasks/unbounded_throughput_task.py similarity index 97% rename from fle/eval/tasks/unbounded_throughput_task.py rename to fle/env/tasks/unbounded_throughput_task.py index 8e4fd0aae..a2068abfb 100644 --- a/fle/eval/tasks/unbounded_throughput_task.py +++ b/fle/env/tasks/unbounded_throughput_task.py @@ -1,8 +1,8 @@ from typing import Any, Dict, List, Optional from fle.env import Entity from fle.env import FactorioInstance -from fle.eval.tasks import TaskABC -from fle.eval.tasks import LAB_PLAY_POPULATED_STARTING_INVENTORY, CRAFTING_STATISTICS +from fle.env.tasks import TaskABC +from fle.env.tasks import LAB_PLAY_POPULATED_STARTING_INVENTORY, CRAFTING_STATISTICS from fle.env.utils.achievements import eval_program_with_achievements from fle.agents import TaskResponse diff --git a/fle/env/utils/camera.py b/fle/env/utils/camera.py index 73ad1f1cb..85ba20543 100644 --- a/fle/env/utils/camera.py +++ b/fle/env/utils/camera.py @@ -1,5 +1,5 @@ from pydantic import BaseModel -from fle.env.entities import Position, BoundingBox +from fle.env.game.entities import Position, BoundingBox class Camera(BaseModel): diff --git a/fle/eval/algorithms/beam/beam_search.py b/fle/eval/algorithms/beam/beam_search.py index 7c6ad26f7..d538f8d54 100644 --- a/fle/eval/algorithms/beam/beam_search.py +++ b/fle/eval/algorithms/beam/beam_search.py @@ -12,9 +12,9 @@ from rich.console import Console from fle.agents.formatters import ConversationFormatter, DefaultFormatter -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program @@ -319,20 +319,6 @@ def __init__( # Create beam groups self.beam_groups = self._create_beam_groups(instances) - # async def _monitor_progress(self): - # """Monitor for hanging operations""" - # while True: - # try: - # await asyncio.sleep(30) # Check every 30 seconds - # current_time = time.time() - # if current_time - self._last_progress_time > self._progress_timeout: - # print(f"Operation potentially hanging - no progress for {self._progress_timeout} seconds") - # self._hanging_detector.set() - # return - # except Exception as e: - # print(f"Error in progress monitor: {e}") - # return - async def _verify_version_compatibility(self): """Verify that resuming version is compatible with current configuration""" metadata = await self.db_client.get_version_metadata(self.version) diff --git a/fle/eval/algorithms/beam/beam_search_milestones.py b/fle/eval/algorithms/beam/beam_search_milestones.py index 5fd3e19d2..cf093cea4 100644 --- a/fle/eval/algorithms/beam/beam_search_milestones.py +++ b/fle/eval/algorithms/beam/beam_search_milestones.py @@ -13,14 +13,14 @@ ) from tenacity import retry, wait_exponential -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program from fle.env import FactorioInstance -from fle.eval.tasks import ThroughputTask +from fle.env.tasks import ThroughputTask from ..mcts import get_mining_setup diff --git a/fle/eval/algorithms/beam/run_milestones.py b/fle/eval/algorithms/beam/run_milestones.py index f4e7c7017..557c9acef 100644 --- a/fle/eval/algorithms/beam/run_milestones.py +++ b/fle/eval/algorithms/beam/run_milestones.py @@ -11,11 +11,11 @@ from dotenv import load_dotenv from fle.agents.formatters import RecursiveReportFormatter -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.env import FactorioInstance, GameState from fle.eval.algorithms.beam import MilestonesBeamSearchExecutor from fle.eval.algorithms.mcts import SupervisedExecutorConfig -from fle.eval.tasks.throughput_task import ( +from fle.env.tasks.throughput_task import ( LAB_PLAY_POPULATED_STARTING_INVENTORY, ThroughputTask, ) diff --git a/fle/eval/algorithms/beam/run_parallel.py b/fle/eval/algorithms/beam/run_parallel.py index 901f18067..5e09c67d6 100644 --- a/fle/eval/algorithms/beam/run_parallel.py +++ b/fle/eval/algorithms/beam/run_parallel.py @@ -12,8 +12,8 @@ from fle.agents.formatters import RecursiveReportFormatter from fle.agents.llm.api_factory import APIFactory from fle.commons.cluster_ips import get_local_container_ips -from fle.commons.db_client import create_db_client -from fle.commons.models.game_state import GameState +from fle.services.db_client import create_db_client +from fle.env.game.game_state import GameState from fle.env import FactorioInstance from fle.eval.algorithms.beam import ParallelBeamConfig, ParallelBeamSearch from fle.eval.algorithms.beam.run import MANUAL, OBSERVATION_SPACE, SYSTEM_PROMPT diff --git a/fle/eval/algorithms/independent/simple_evaluator.py b/fle/eval/algorithms/independent/simple_evaluator.py index edb2ebb1d..91fc78ab3 100644 --- a/fle/eval/algorithms/independent/simple_evaluator.py +++ b/fle/eval/algorithms/independent/simple_evaluator.py @@ -2,11 +2,11 @@ import copy from typing import List, Tuple, Union, Dict -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.achievements import ProductionFlows -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.program import Program -from fle.env.entities import Entity, EntityGroup +from fle.env.game.entities import Entity, EntityGroup from fle.env import FactorioInstance from fle.env.utils.profits import get_achievements diff --git a/fle/eval/algorithms/independent/trajectory_runner.py b/fle/eval/algorithms/independent/trajectory_runner.py index e8f6f0503..3981dc435 100644 --- a/fle/eval/algorithms/independent/trajectory_runner.py +++ b/fle/eval/algorithms/independent/trajectory_runner.py @@ -10,7 +10,7 @@ from fle.agents import CompletionResult, CompletionReason from fle.agents.agent_abc import AgentABC -from fle.commons.db_client import DBClient, create_db_client +from fle.services.db_client import DBClient, create_db_client from fle.eval.algorithms.independent.simple_evaluator import SimpleFactorioEvaluator from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message @@ -20,12 +20,12 @@ from fle.agents.llm.metrics import timing_tracker, log_metrics # from fle.commons.models.response import EnvironmentResponse -from fle.env.namespace import FactorioNamespace +from fle.env.game.namespace import FactorioNamespace from fle.env.protocols.a2a.handler import A2AMessage from a2a.types import AgentCard from fle.agents import Response -from fle.eval.tasks import TaskABC +from fle.env.tasks import TaskABC load_dotenv() diff --git a/fle/eval/algorithms/mcts/__main__chunked.py b/fle/eval/algorithms/mcts/__main__chunked.py index 83a711762..97083df06 100644 --- a/fle/eval/algorithms/mcts/__main__chunked.py +++ b/fle/eval/algorithms/mcts/__main__chunked.py @@ -22,9 +22,9 @@ from fle.agents.llm.api_factory import APIFactory from fle.commons.cluster_ips import get_local_container_ips -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.message import Message from fle.commons.models.program import Program from fle.env import FactorioInstance diff --git a/fle/eval/algorithms/mcts/__main__objective_mcts.py b/fle/eval/algorithms/mcts/__main__objective_mcts.py index 5d415f5b6..891c4a6dd 100644 --- a/fle/eval/algorithms/mcts/__main__objective_mcts.py +++ b/fle/eval/algorithms/mcts/__main__objective_mcts.py @@ -15,9 +15,9 @@ ) from fle.agents.llm.api_factory import APIFactory from fle.commons.cluster_ips import get_local_container_ips -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.message import Message from fle.commons.models.program import Program from fle.env import FactorioInstance diff --git a/fle/eval/algorithms/mcts/blueprints_to_programs.py b/fle/eval/algorithms/mcts/blueprints_to_programs.py index 560c9112a..e205f7bfd 100644 --- a/fle/eval/algorithms/mcts/blueprints_to_programs.py +++ b/fle/eval/algorithms/mcts/blueprints_to_programs.py @@ -8,7 +8,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.program import Program from fle.env import FactorioInstance diff --git a/fle/eval/algorithms/mcts/chunked_mcts.py b/fle/eval/algorithms/mcts/chunked_mcts.py index e49a397e6..fee4c7a6b 100644 --- a/fle/eval/algorithms/mcts/chunked_mcts.py +++ b/fle/eval/algorithms/mcts/chunked_mcts.py @@ -6,11 +6,11 @@ from typing import Optional, Tuple, Union from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program -from fle.env.entities import Entity, EntityGroup +from fle.env.game.entities import Entity, EntityGroup from .mcts import MCTS diff --git a/fle/eval/algorithms/mcts/evaluator.py b/fle/eval/algorithms/mcts/evaluator.py index 22b5af3da..3ba587609 100644 --- a/fle/eval/algorithms/mcts/evaluator.py +++ b/fle/eval/algorithms/mcts/evaluator.py @@ -4,11 +4,11 @@ from fle.env.utils.profits import get_achievements -from fle.commons.db_client import DBClient -from fle.commons.models.game_state import GameState +from fle.services.db_client import DBClient +from fle.env.game.game_state import GameState from fle.commons.models.program import Program -from fle.env.entities import Entity, EntityGroup -from fle.env.instance import FactorioInstance +from fle.env.game.entities import Entity, EntityGroup +from fle.env.game.instance import FactorioInstance from fle.eval.algorithms.mcts.logger import FactorioLogger diff --git a/fle/eval/algorithms/mcts/instance_group.py b/fle/eval/algorithms/mcts/instance_group.py index 0ea596d68..299f8055d 100644 --- a/fle/eval/algorithms/mcts/instance_group.py +++ b/fle/eval/algorithms/mcts/instance_group.py @@ -3,7 +3,7 @@ from fle.eval.evaluator import Evaluator from fle.eval.algorithms.mcts.mcts import MCTS -from fle.env.instance import FactorioInstance +from fle.env.game.instance import FactorioInstance @dataclass diff --git a/fle/eval/algorithms/mcts/main.py b/fle/eval/algorithms/mcts/main.py index 499309418..c7bc6fc5d 100644 --- a/fle/eval/algorithms/mcts/main.py +++ b/fle/eval/algorithms/mcts/main.py @@ -2,7 +2,7 @@ import asyncio from dotenv import load_dotenv from fle.commons.cluster_ips import get_local_container_ips -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.eval.algorithms.mcts import MCTSFactory from fle.eval.open.plots.run_results import RunResults from fle.env import FactorioInstance diff --git a/fle/eval/algorithms/mcts/mcts.py b/fle/eval/algorithms/mcts/mcts.py index ac5d31ce8..3fb827d22 100644 --- a/fle/eval/algorithms/mcts/mcts.py +++ b/fle/eval/algorithms/mcts/mcts.py @@ -14,9 +14,9 @@ from fle.agents.llm.parsing import PythonParser from tenacity import retry, retry_if_exception_type, wait_exponential -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/mcts/mcts_factory.py b/fle/eval/algorithms/mcts/mcts_factory.py index 1f96b3d60..c0d0904a9 100644 --- a/fle/eval/algorithms/mcts/mcts_factory.py +++ b/fle/eval/algorithms/mcts/mcts_factory.py @@ -10,7 +10,7 @@ from dataclasses import asdict from .samplers import BeamSampler -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.agents.llm.api_factory import APIFactory diff --git a/fle/eval/algorithms/mcts/objective_mcts.py b/fle/eval/algorithms/mcts/objective_mcts.py index 0f9aa9829..cc421be5a 100644 --- a/fle/eval/algorithms/mcts/objective_mcts.py +++ b/fle/eval/algorithms/mcts/objective_mcts.py @@ -11,9 +11,9 @@ from fle.agents.llm.api_factory import APIFactory from tenacity import retry, retry_if_exception_type, wait_exponential -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/mcts/parallel_mcts.py b/fle/eval/algorithms/mcts/parallel_mcts.py index e9b1c082a..9c566acc9 100644 --- a/fle/eval/algorithms/mcts/parallel_mcts.py +++ b/fle/eval/algorithms/mcts/parallel_mcts.py @@ -9,7 +9,7 @@ from fle.eval.evaluator import Evaluator from rich.console import Console -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.env import FactorioInstance # Configure logging diff --git a/fle/eval/algorithms/mcts/parallel_mcts_config.py b/fle/eval/algorithms/mcts/parallel_mcts_config.py index 214f4be80..706cecf5e 100644 --- a/fle/eval/algorithms/mcts/parallel_mcts_config.py +++ b/fle/eval/algorithms/mcts/parallel_mcts_config.py @@ -1,6 +1,6 @@ from typing import Any, Dict, Type -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from .mcts import MCTS from .samplers import DBSampler diff --git a/fle/eval/algorithms/mcts/parallel_planning_mcts.py b/fle/eval/algorithms/mcts/parallel_planning_mcts.py index 1915c0120..8b5c95f14 100644 --- a/fle/eval/algorithms/mcts/parallel_planning_mcts.py +++ b/fle/eval/algorithms/mcts/parallel_planning_mcts.py @@ -13,9 +13,9 @@ ConversationFormatter, StructurePreservingFormatter, ) -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/mcts/parallel_supervised_config.py b/fle/eval/algorithms/mcts/parallel_supervised_config.py index 224752ee7..b67ebf3ba 100644 --- a/fle/eval/algorithms/mcts/parallel_supervised_config.py +++ b/fle/eval/algorithms/mcts/parallel_supervised_config.py @@ -1,6 +1,6 @@ from typing import Any, Dict -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState class SupervisedExecutorConfig: diff --git a/fle/eval/algorithms/mcts/plan_sampler.py b/fle/eval/algorithms/mcts/plan_sampler.py index 5642121ce..7d459b512 100644 --- a/fle/eval/algorithms/mcts/plan_sampler.py +++ b/fle/eval/algorithms/mcts/plan_sampler.py @@ -3,7 +3,7 @@ from typing import Any, Tuple, Optional from fle.agents.llm.api_factory import APIFactory -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.env import FactorioInstance diff --git a/fle/eval/algorithms/mcts/planning_mcts.py b/fle/eval/algorithms/mcts/planning_mcts.py index 5bcdc962c..0f39d1412 100644 --- a/fle/eval/algorithms/mcts/planning_mcts.py +++ b/fle/eval/algorithms/mcts/planning_mcts.py @@ -15,7 +15,7 @@ from tenacity import retry, wait_exponential from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/mcts/planning_models.py b/fle/eval/algorithms/mcts/planning_models.py index a9229e6ef..a57b20597 100644 --- a/fle/eval/algorithms/mcts/planning_models.py +++ b/fle/eval/algorithms/mcts/planning_models.py @@ -4,7 +4,7 @@ from pydantic import BaseModel, Field from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/mcts/results.py b/fle/eval/algorithms/mcts/results.py index ee073ac94..b6519b5f8 100644 --- a/fle/eval/algorithms/mcts/results.py +++ b/fle/eval/algorithms/mcts/results.py @@ -1,7 +1,7 @@ import os import asyncio from dotenv import load_dotenv -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from eval.open.plots.run_results import RunResults import questionary import neptune diff --git a/fle/eval/algorithms/mcts/samplers/beam_sampler.py b/fle/eval/algorithms/mcts/samplers/beam_sampler.py index ddd65ac29..fcc797a81 100644 --- a/fle/eval/algorithms/mcts/samplers/beam_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/beam_sampler.py @@ -6,7 +6,7 @@ from psycopg2.extras import DictCursor from tenacity import retry_if_exception_type, wait_exponential -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.eval.algorithms.mcts.samplers.db_sampler import DBSampler from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/mcts/samplers/blueprint_scenario_sampler.py b/fle/eval/algorithms/mcts/samplers/blueprint_scenario_sampler.py index ff77cb324..cff89c7b6 100644 --- a/fle/eval/algorithms/mcts/samplers/blueprint_scenario_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/blueprint_scenario_sampler.py @@ -5,7 +5,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.program import Program from fle.env import FactorioInstance diff --git a/fle/eval/algorithms/mcts/samplers/db_sampler.py b/fle/eval/algorithms/mcts/samplers/db_sampler.py index f33657ef5..ab523d627 100644 --- a/fle/eval/algorithms/mcts/samplers/db_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/db_sampler.py @@ -3,7 +3,7 @@ from psycopg2.extras import DictCursor -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/mcts/samplers/dynamic_reward_weighted_sampler.py b/fle/eval/algorithms/mcts/samplers/dynamic_reward_weighted_sampler.py index 6211de1df..7cfed6200 100644 --- a/fle/eval/algorithms/mcts/samplers/dynamic_reward_weighted_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/dynamic_reward_weighted_sampler.py @@ -8,7 +8,7 @@ from psycopg2.extras import DictCursor from tenacity import retry_if_exception_type, wait_exponential -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.program import Program from .db_sampler import DBSampler diff --git a/fle/eval/algorithms/mcts/samplers/kld_achievement_sampler.py b/fle/eval/algorithms/mcts/samplers/kld_achievement_sampler.py index f7b6cd3a8..dcbba2d91 100644 --- a/fle/eval/algorithms/mcts/samplers/kld_achievement_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/kld_achievement_sampler.py @@ -8,7 +8,7 @@ from psycopg2.extras import DictCursor from tenacity import retry_if_exception_type, wait_exponential -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models.program import Program from .db_sampler import DBSampler diff --git a/fle/eval/algorithms/mcts/supervised_task_executor_abc.py b/fle/eval/algorithms/mcts/supervised_task_executor_abc.py index cc641fce9..c009d1157 100644 --- a/fle/eval/algorithms/mcts/supervised_task_executor_abc.py +++ b/fle/eval/algorithms/mcts/supervised_task_executor_abc.py @@ -11,7 +11,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.generation_parameters import GenerationParameters from fle.agents.formatters.conversation_formatter_abc import DefaultFormatter -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.eval.evaluator import Evaluator from fle.eval.algorithms.mcts.grouped_logger import GroupedFactorioLogger from fle.eval.algorithms.mcts.parallel_supervised_config import SupervisedExecutorConfig @@ -22,10 +22,10 @@ LanguageOutput, InitialPlanOutput, ) -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.program import Program from fle.env import FactorioInstance -from fle.eval.tasks import TaskABC +from fle.env.tasks import TaskABC from abc import ABC, abstractmethod diff --git a/fle/eval/entrypoints/beam_run.py b/fle/eval/entrypoints/beam_run.py index 0aea8a9d5..9e27d8d0e 100644 --- a/fle/eval/entrypoints/beam_run.py +++ b/fle/eval/entrypoints/beam_run.py @@ -9,7 +9,7 @@ from fle.agents.formatters import RecursiveFormatter from fle.agents.llm.api_factory import APIFactory -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from fle.commons.models import GameState from fle.env import FactorioInstance diff --git a/fle/eval/entrypoints/independent_run.py b/fle/eval/entrypoints/independent_run.py index 99b445c8f..5cec7dcaa 100644 --- a/fle/eval/entrypoints/independent_run.py +++ b/fle/eval/entrypoints/independent_run.py @@ -9,7 +9,7 @@ create_factorio_instance, EvalConfig, ) -from fle.eval.tasks import TaskFactory +from fle.env.tasks import TaskFactory from pathlib import Path import json from dataclasses import dataclass diff --git a/fle/eval/entrypoints/independent_run_a2a.py b/fle/eval/entrypoints/independent_run_a2a.py index d24910042..60511e4a4 100644 --- a/fle/eval/entrypoints/independent_run_a2a.py +++ b/fle/eval/entrypoints/independent_run_a2a.py @@ -9,7 +9,7 @@ create_factorio_instance, EvalConfig, ) -from fle.eval.tasks.task_factory import TaskFactory +from fle.env.tasks.task_factory import TaskFactory from pathlib import Path import json from dataclasses import dataclass diff --git a/fle/eval/entrypoints/independent_run_vision.py b/fle/eval/entrypoints/independent_run_vision.py index ae4f655b8..256d044ef 100644 --- a/fle/eval/entrypoints/independent_run_vision.py +++ b/fle/eval/entrypoints/independent_run_vision.py @@ -9,7 +9,7 @@ create_factorio_instance, EvalConfig, ) -from fle.eval.tasks.task_factory import TaskFactory +from fle.env.tasks.task_factory import TaskFactory from fle.commons.cluster_ips import get_local_container_ips from pathlib import Path import json diff --git a/fle/eval/entrypoints/mcts_planning_agent.py b/fle/eval/entrypoints/mcts_planning_agent.py index a19659ca9..87c1bc68e 100644 --- a/fle/eval/entrypoints/mcts_planning_agent.py +++ b/fle/eval/entrypoints/mcts_planning_agent.py @@ -26,8 +26,8 @@ from dotenv import load_dotenv from rich import print -from fle.commons.db_client import DBClient -from fle.commons.models.game_state import GameState +from fle.services.db_client import DBClient +from fle.env.game.game_state import GameState from fle.env import FactorioInstance load_dotenv() diff --git a/fle/eval/open/plots/run_results.py b/fle/eval/open/plots/run_results.py index fcf7c0d1a..f7509ee5c 100644 --- a/fle/eval/open/plots/run_results.py +++ b/fle/eval/open/plots/run_results.py @@ -8,7 +8,7 @@ import networkx as nx import numpy as np -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient @dataclass diff --git a/fle/eval/open/plots/run_visualiser.py b/fle/eval/open/plots/run_visualiser.py index 09d5a3ac0..b1e543c9a 100644 --- a/fle/eval/open/plots/run_visualiser.py +++ b/fle/eval/open/plots/run_visualiser.py @@ -13,7 +13,7 @@ from matplotlib.offsetbox import OffsetImage, AnnotationBbox from PIL import Image -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from eval.open.plots.run_results import RunResults load_dotenv() diff --git a/fle/eval/open/plots/simple_run_visualiser.py b/fle/eval/open/plots/simple_run_visualiser.py index da09beec3..96446eab6 100644 --- a/fle/eval/open/plots/simple_run_visualiser.py +++ b/fle/eval/open/plots/simple_run_visualiser.py @@ -14,7 +14,7 @@ from matplotlib.offsetbox import OffsetImage, AnnotationBbox import os -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from eval.open.independent_runs.value_calculator import ValueCalculator load_dotenv() diff --git a/fle/eval/open/plots/simple_run_visualiser_with_ticks.py b/fle/eval/open/plots/simple_run_visualiser_with_ticks.py index 14406a07d..326022d3e 100644 --- a/fle/eval/open/plots/simple_run_visualiser_with_ticks.py +++ b/fle/eval/open/plots/simple_run_visualiser_with_ticks.py @@ -14,7 +14,7 @@ from matplotlib.offsetbox import OffsetImage, AnnotationBbox import os -from fle.commons.db_client import PostgresDBClient +from fle.services.db_client import PostgresDBClient from eval.open.independent_runs.value_calculator import ValueCalculator load_dotenv() diff --git a/fle/eval/open/plots/visualise_production_figures.py b/fle/eval/open/plots/visualise_production_figures.py index b20fdd440..04595dec5 100644 --- a/fle/eval/open/plots/visualise_production_figures.py +++ b/fle/eval/open/plots/visualise_production_figures.py @@ -15,7 +15,7 @@ from dotenv import load_dotenv from matplotlib import image as mpimg -from fle.commons.db_client import DBClient +from fle.services.db_client import DBClient from eval.open.independent_runs.value_calculator import ValueCalculator load_dotenv() diff --git a/fle/services/db_client.py b/fle/services/db_client.py new file mode 100644 index 000000000..a5e7c08c0 --- /dev/null +++ b/fle/services/db_client.py @@ -0,0 +1,864 @@ +import json +import logging +import math +import os +import random +import sqlite3 +import statistics +import threading +from abc import ABC +from contextlib import contextmanager +from pathlib import Path +from typing import Any, Dict, List, Optional + +import psycopg2 +import tenacity +from psycopg2.extras import DictCursor +from psycopg2.pool import ThreadedConnectionPool +from tenacity import retry_if_exception_type, wait_exponential, wait_random_exponential + +from fle.commons.models.conversation import Conversation +from fle.env.game.game_state import GameState +from fle.commons.models.program import Program + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class DBClient(ABC): + def __init__( + self, + max_conversation_length: int = 20, + min_connections: int = 5, + max_connections: int = 20, + **db_config, + ): + self.max_conversation_length = max_conversation_length + # Don't store connection as instance variable + # Instead create connection pool + # self.pool = [] + # self.max_pool_size = 5 + self._pool = None + self.min_connections = min_connections + self.max_connections = max_connections + self._lock = threading.Lock() + self.db_config = db_config + + async def initialize(self): + """Initialize the connection pool""" + pass + + @contextmanager + def get_connection(self): + """Regular context manager for database connections""" + pass + + async def get_beam_heads(self, version: int, beam_width: int) -> List[Program]: + """Get the highest value programs across all depths for a given version.""" + try: + with self.get_connection() as conn: + with conn.cursor(cursor_factory=DictCursor) as cur: + # Use a CTE to get diverse set of programs + cur.execute( + """ + WITH ProgramsByDepth AS ( + SELECT DISTINCT ON (depth) * + FROM programs + WHERE version = %s + AND state_json IS NOT NULL + AND value IS NOT NULL + ORDER BY depth, value DESC + ) + SELECT * FROM ( + SELECT * FROM ProgramsByDepth + ORDER BY value DESC + LIMIT %s + ) as depth_diverse + UNION DISTINCT + SELECT * FROM ( + SELECT * FROM programs + WHERE version = %s + AND state_json IS NOT NULL + AND value IS NOT NULL + ORDER BY value DESC + LIMIT %s + ) as value_focused + ORDER BY value DESC + LIMIT %s + """, + (version, beam_width, version, beam_width * 2, beam_width), + ) + + results = cur.fetchall() + if not results: + logger.warning(f"No programs found for version {version}") + return [] + + programs = [Program.from_row(dict(row)) for row in results] + depths = [p.depth for p in programs] + logger.info( + f"Found {len(programs)} beam heads for version {version} - {depths}" + ) + return programs + except Exception as e: + logger.error(f"Error fetching beam heads: {e}", exc_info=True) + return [] + + async def version_exists(self, version: int) -> bool: + """Check if a version exists in the database""" + try: + with self.get_connection() as conn: + with conn.cursor() as cur: + cur.execute( + """ + SELECT EXISTS( + SELECT 1 FROM programs WHERE version = %s + ) + """, + (version,), + ) + return cur.fetchone()[0] + except Exception as e: + print(f"Error checking version existence: {e}") + return False + + async def get_version_metadata(self, version: int) -> dict: + """Get metadata for a specific version""" + try: + with self.get_connection() as conn: + with conn.cursor(cursor_factory=DictCursor) as cur: + cur.execute( + """ + SELECT DISTINCT version_description, model + FROM programs + WHERE version = %s + LIMIT 1 + """, + (version,), + ) + result = cur.fetchone() + return dict(result) if result else {} + except Exception as e: + print(f"Error fetching version metadata: {e}") + return {} + + @tenacity.retry( + retry=retry_if_exception_type( + (psycopg2.OperationalError, psycopg2.InterfaceError, psycopg2.DatabaseError) + ), + wait=wait_random_exponential(multiplier=1, min=4, max=10), + ) + async def create_program(self, program: Program) -> Program: + """Create a new program, now with connection management""" + try: + with self.get_connection() as conn: + with conn.cursor() as cur: + cur.execute( + """ + INSERT INTO programs (code, value, visits, parent_id, state_json, conversation_json, + completion_token_usage, prompt_token_usage, token_usage, response, + holdout_value, raw_reward, version, version_description, model, meta, + achievements_json, instance, depth, advantage, ticks, timing_metrics_json) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + RETURNING id, created_at + """, + ( + program.code, + program.value, + 0, + program.parent_id, + program.state.to_raw() if program.state else None, + json.dumps(program.conversation.dict()), + program.completion_token_usage, + program.prompt_token_usage, + program.token_usage, + program.response, + program.holdout_value, + program.raw_reward, + program.version, + program.version_description, + program.model, + json.dumps(program.meta), + json.dumps(program.achievements), + program.instance, + program.depth / 2, + program.advantage, + program.ticks, + json.dumps(program.timing_metrics) + if program.timing_metrics + else None, + ), + ) + + id, created_at = cur.fetchone() + conn.commit() + program.id = id + program.created_at = created_at + return program + except Exception as e: + conn.rollback() + print(f"Error creating program: {e}") + raise e + + async def cleanup(self): + """Clean up database resources""" + if self._pool is not None: + with self._lock: + if self._pool is not None: + try: + self._pool.closeall() + except Exception as e: + logger.error(f"Error closing connection pool: {e}") + finally: + self._pool = None + + @tenacity.retry( + retry=retry_if_exception_type( + (psycopg2.OperationalError, psycopg2.InterfaceError) + ), + wait=wait_exponential(multiplier=1, min=4, max=10), + ) + async def get_all_program_rewards(self, version: int = None) -> List[float]: + """Get all program rewards with proper connection management""" + query = """ + SELECT value + FROM programs + WHERE value IS NOT NULL + """ + if version is not None: + query += " AND version = %s" + + params = (version,) if version is not None else () + + try: + with self.get_connection() as conn: + with conn.cursor() as cur: + cur.execute(query.strip(), params) + results = cur.fetchall() + return [row[0] for row in results] + except Exception as e: + print(f"Error fetching program rewards: {e}") + return [] + + @tenacity.retry( + retry=retry_if_exception_type( + (psycopg2.OperationalError, psycopg2.InterfaceError) + ), + wait=wait_exponential(multiplier=1, min=4, max=10), + ) + async def get_largest_version(self) -> int: + query = """ + SELECT MAX(version) + FROM programs + """ + + try: + with self.get_connection() as conn: + with conn.cursor() as cur: + cur.execute(query) + result = cur.fetchone() + return result[0] if result and result[0] is not None else 0 + except Exception as e: + print(f"Error fetching largest version: {e}") + return 0 + + async def get_largest_depth_in_version(self, version): + query = f""" + SELECT MAX(depth) + FROM programs + WHERE version = {version} + """ + + try: + with self.get_connection() as conn: + with conn.cursor() as cur: + cur.execute(query) + result = cur.fetchone() + return result[0] if result and result[0] is not None else 0 + except Exception as e: + print(f"Error fetching largest depth: {e}") + return 0 + + @tenacity.retry( + retry=retry_if_exception_type( + (psycopg2.OperationalError, psycopg2.InterfaceError) + ), + wait=wait_random_exponential(multiplier=1, min=4, max=10), + ) + async def sample_parent( + self, + version=1, + compression_strength: Optional[float] = None, + adaptive_period: int = 100, + ) -> Optional[Program]: + """ + Sample parent with proper connection management and adjusted reward scaling. + + Args: + version: The version of programs to sample from + compression_strength: Fixed compression strength between 0 and 1. + If None, uses adaptive compression. Higher values mean more exploitation. Lower means more exploration. + adaptive_period: Number of steps for a full sine wave cycle when using + adaptive compression. + """ + try: + with self.get_connection() as conn: + with conn.cursor(cursor_factory=DictCursor) as cur: + # First get the current step count for adaptive compression + if compression_strength is None: + cur.execute( + f"SELECT COUNT(*) as step_count FROM programs WHERE version = {version}" + ) + step_count = cur.fetchone()["step_count"] + # Calculate adaptive compression using sine wave + # sin goes from -1 to 1, so we transform to 0 to 1 + compression_strength = ( + math.sin(2 * math.pi * step_count / adaptive_period) + 1 + ) / 2 + + cur.execute( + """ + WITH recent AS ( + SELECT id, value, conversation_json + FROM programs + WHERE version = %s + AND advantage IS NOT NULL + -- AND jsonb_array_length(conversation_json->'messages') < %s + ORDER BY created_at DESC + LIMIT 300 + ) + SELECT id, advantage + FROM recent + """, + (version), + ) + + results = cur.fetchall() + if not results: + return None + + # Get statistics of the value distribution + values = [row["advantage"] for row in results] + mean_value = statistics.mean(values) + std_value = statistics.stdev(values) if len(values) > 1 else 1.0 + + # Apply reward transformation to handle power-law distribution + def transform_reward(value): + # Z-score normalization + z_score = ( + (value - mean_value) / std_value if std_value > 0 else 0 + ) + + # Compress extreme values using tanh with current compression strength + compressed = math.tanh(z_score * compression_strength) + + # Scale back to positive values and add small epsilon + return (compressed + 1.0) / 2.0 + 1e-6 + + # Log current compression state + print( + f"Using compression strength: {compression_strength:.3f} " + f"({'adaptive' if compression_strength is None else 'fixed'})" + ) + + # Calculate transformed weights + weights = [ + (row["id"], transform_reward(row["advantage"])) + for row in results + ] + + # Normalize weights + total_weight = sum(w[1] for w in weights) + if total_weight == 0: + sampled_id = random.choice([w[0] for w in weights]) + else: + normalized_weights = [ + (id, w / total_weight) for id, w in weights + ] + sampled_id = random.choices( + [id for id, _ in normalized_weights], + weights=[w for _, w in normalized_weights], + k=1, + )[0] + + # Fetch the selected program + cur.execute( + """ + SELECT * FROM programs WHERE id = %s + """, + (sampled_id,), + ) + + row = cur.fetchone() + return Program.from_row(dict(row)) if row else None + except Exception as e: + print(f"Error sampling parent: {e}") + raise e + + async def update_program(self, program_id: int, updates: Dict[str, Any]) -> Program: + """Update program with proper connection management""" + try: + with self.get_connection() as conn: + with conn.cursor() as cur: + # Handle timing_metrics separately since it needs to be JSON serialized + timing_metrics = updates.pop("timing_metrics", None) + if timing_metrics is not None: + updates["timing_metrics_json"] = json.dumps(timing_metrics) + + set_clauses = [f"{k} = %s" for k in updates.keys()] + values = list(updates.values()) + + cur.execute( + f""" + UPDATE programs + SET {", ".join(set_clauses)} + WHERE id = %s + RETURNING * + """, + values + [program_id], + ) + + conn.commit() + row = cur.fetchone() + return Program.from_row( + dict(zip([desc[0] for desc in cur.description], row)) + ) + except Exception as e: + print(f"Error updating program: {e}") + raise e + + async def get_resume_state( + self, resume_version, process_id, agent_idx=-1 + ) -> tuple[ + Optional[GameState], Optional[Conversation], Optional[int], Optional[int] + ]: + """Get the state to resume from""" + try: + # Get most recent successful program to resume from + query = """ + SELECT * FROM programs + WHERE version = %s + AND state_json IS NOT NULL + AND value IS NOT NULL + -- AND meta->>'process_id' = %s::text + AND instance = %s + ORDER BY created_at DESC + LIMIT 1 + """ + + with self.get_connection() as conn: + with conn.cursor() as cur: + cur.execute(query, (resume_version, process_id, agent_idx)) + results = cur.fetchall() + if not results: + print(f"No valid programs found for version {resume_version} ") + return None, None, None, None + + # Choose a program to resume from + program = Program.from_row( + dict(zip([desc[0] for desc in cur.description], results[0])) + ) + return program.state, program.conversation, program.id, program.depth + + except Exception as e: + print(f"Error getting resume state: {e}") + return None, None, None, None + + +class PostgresDBClient(DBClient): + def __init__( + self, + max_conversation_length: int = 20, + min_connections: int = 5, + max_connections: int = 20, + **db_config, + ): + super().__init__( + max_conversation_length, min_connections, max_connections, **db_config + ) + + async def initialize(self): + """Initialize the connection pool""" + if self._pool is None: + async with self._lock: + if self._pool is None: # Double check pattern + self._pool = ThreadedConnectionPool( + self.min_connections, self.max_connections, **self.db_config + ) + + def _ensure_pool(self): + """Ensure connection pool exists with proper locking""" + if self._pool is None: + with self._lock: + if self._pool is None: + self._pool = ThreadedConnectionPool( + self.min_connections, self.max_connections, **self.db_config + ) + + @contextmanager + def get_connection(self): + """Regular context manager for database connections""" + self._ensure_pool() + conn = None + try: + conn = self._pool.getconn() + yield conn + finally: + if conn: + try: + self._pool.putconn(conn) + except Exception as e: + print(f"Error returning connection to pool: {e}") + try: + self._pool.putconn(conn, close=True) + except: + pass + + +class SQLliteDBClient(DBClient): + def __init__( + self, + max_conversation_length: int = 20, + min_connections: int = 5, + max_connections: int = 20, + **db_config, + ): + super().__init__( + max_conversation_length, min_connections, max_connections, **db_config + ) + self.database_file = self.db_config.get("database_file") + + async def initialize(self): + """Initialize the connection pool""" + if self._pool is None: + async with self._lock: + if self._pool is None: # Double check pattern + self._pool = ThreadedConnectionPool( + self.min_connections, self.max_connections, **self.db_config + ) + + @contextmanager + def get_connection(self): + """Context manager for SQLite database connections""" + conn = None + try: + conn = sqlite3.connect(self.database_file) + yield conn + finally: + if conn: + conn.close() + + @tenacity.retry( + retry=retry_if_exception_type( + (psycopg2.OperationalError, psycopg2.InterfaceError) + ), + wait=wait_exponential(multiplier=1, min=4, max=10), + ) + async def get_largest_version(self) -> int: + query = """ + SELECT MAX(version) + FROM programs + """ + + try: + with self.get_connection() as conn: + cur = conn.cursor() + cur.execute(query) + result = cur.fetchone() + return result[0] if result and result[0] is not None else 0 + except Exception as e: + print(f"Error fetching largest version: {e}") + return 0 + + async def get_resume_state( + self, resume_version, process_id, agent_idx=-1 + ) -> tuple[ + Optional[GameState], Optional[Conversation], Optional[int], Optional[int] + ]: + """Get the state to resume from""" + try: + # Get most recent successful program to resume from + query = """ + SELECT * FROM programs + WHERE version = ? + AND state_json IS NOT NULL + AND value IS NOT NULL + AND json_extract(meta, '$.process_id') = ? + AND instance = ? + ORDER BY created_at DESC + LIMIT 1 + """ + + with self.get_connection() as conn: + cur = conn.cursor() + cur.execute(query, (resume_version, process_id, agent_idx)) + results = cur.fetchall() + + if not results: + print(f"No valid programs found for version {resume_version}") + return None, None, None, None + resulting_program_dict = dict( + zip([desc[0] for desc in cur.description], results[0]) + ) + # make meta, state_json achievements_json and conversation_json table a dict + resulting_program_dict["meta"] = json.loads(resulting_program_dict["meta"]) + resulting_program_dict["achievements_json"] = json.loads( + resulting_program_dict["achievements_json"] + ) + resulting_program_dict["conversation_json"] = json.loads( + resulting_program_dict["conversation_json"] + ) + resulting_program_dict["state_json"] = json.loads( + resulting_program_dict["state_json"] + ) + # Choose a program to resume from + program = Program.from_row(resulting_program_dict) + return program.state, program.conversation, program.id, program.depth + + except Exception as e: + print(f"Error getting resume state: {e}") + return None, None, None, None + + @tenacity.retry( + retry=retry_if_exception_type( + (psycopg2.OperationalError, psycopg2.InterfaceError, psycopg2.DatabaseError) + ), + wait=wait_random_exponential(multiplier=1, min=4, max=10), + ) + async def create_program(self, program: Program) -> Program: + """Create a new program, now with connection management""" + try: + with self.get_connection() as conn: + cur = conn.cursor() + + # Insert the program data + cur.execute( + """ + INSERT INTO programs (code, value, visits, parent_id, state_json, conversation_json, + completion_token_usage, prompt_token_usage, token_usage, response, + holdout_value, raw_reward, version, version_description, model, meta, + achievements_json, instance, depth, advantage, ticks, timing_metrics_json) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """, + ( + program.code, + program.value, + 0, # Assuming visits is initially set to 0 + program.parent_id, + program.state.to_raw() if program.state else None, + json.dumps(program.conversation.dict()), + program.completion_token_usage, + program.prompt_token_usage, + program.token_usage, + program.response, + program.holdout_value, + program.raw_reward, + program.version, + program.version_description, + program.model, + json.dumps(program.meta), + json.dumps(program.achievements), + program.instance, + program.depth / 2, + program.advantage, + program.ticks, + json.dumps(program.timing_metrics) + if program.timing_metrics + else None, + ), + ) + + # Get the last inserted row ID + program.id = cur.lastrowid + + # Retrieve the created_at timestamp + cur.execute( + "SELECT created_at FROM programs WHERE id = ?", (program.id,) + ) + program.created_at = cur.fetchone()[0] + conn.commit() + # Return the updated program object + return program + except Exception as e: + conn.rollback() + print(f"Error creating program: {e}") + raise e + + +def create_default_sqlite_db(db_file: str) -> None: + """Create SQLite database with required schema if it doesn't exist""" + db_path = Path(db_file) + + # Create directory if it doesn't exist + db_path.parent.mkdir(parents=True, exist_ok=True) + + # Create database and table if they don't exist + conn = sqlite3.connect(db_file) + try: + cursor = conn.cursor() + + # Check if programs table exists + cursor.execute(""" + SELECT name FROM sqlite_master + WHERE type='table' AND name='programs' + """) + + if not cursor.fetchone(): + print(f"Creating SQLite database schema in {db_file}") + cursor.execute(""" + CREATE TABLE programs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + code TEXT NOT NULL, + value REAL DEFAULT 0.0, + visits INTEGER DEFAULT 0, + parent_id INTEGER, + state_json TEXT, + conversation_json TEXT NOT NULL, + completion_token_usage INTEGER, + prompt_token_usage INTEGER, + token_usage INTEGER, + response TEXT, + holdout_value REAL, + raw_reward REAL, + version INTEGER DEFAULT 1, + version_description TEXT DEFAULT '', + model TEXT DEFAULT 'gpt-4o', + meta TEXT, + achievements_json TEXT, + instance INTEGER DEFAULT -1, + depth REAL DEFAULT 0.0, + advantage REAL DEFAULT 0.0, + ticks INTEGER DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + timing_metrics_json TEXT + ) + """) + conn.commit() + print("SQLite database schema created successfully!") + + finally: + conn.close() + + +def create_default_postgres_db(**db_config) -> None: + """Create PostgreSQL database with required schema if it doesn't exist""" + conn = None + try: + conn = psycopg2.connect(**db_config) + cursor = conn.cursor() + + # Check if programs table exists + cursor.execute(""" + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'programs' + ) + """) + + table_exists = cursor.fetchone()[0] + + if not table_exists: + print("Creating PostgreSQL database schema") + cursor.execute(""" + CREATE TABLE programs ( + id SERIAL PRIMARY KEY, + code TEXT NOT NULL, + value REAL DEFAULT 0.0, + visits INTEGER DEFAULT 0, + parent_id INTEGER, + state_json TEXT, + conversation_json TEXT NOT NULL, + completion_token_usage INTEGER, + prompt_token_usage INTEGER, + token_usage INTEGER, + response TEXT, + holdout_value REAL, + raw_reward REAL, + version INTEGER DEFAULT 1, + version_description TEXT DEFAULT '', + model TEXT DEFAULT 'gpt-4o', + meta TEXT, + achievements_json TEXT, + instance INTEGER DEFAULT -1, + depth REAL DEFAULT 0.0, + advantage REAL DEFAULT 0.0, + ticks INTEGER DEFAULT 0, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + timing_metrics_json TEXT + ) + """) + conn.commit() + print("PostgreSQL database schema created successfully!") + + except Exception as e: + print(f"Error creating PostgreSQL schema: {e}") + if conn: + conn.rollback() + raise + finally: + if conn: + conn.close() + + +async def create_db_client( + max_conversation_length: int = 40, + min_connections: int = 2, + max_connections: int = 5, +) -> DBClient: + """ + Create database client based on environment configuration. + Defaults to SQLite with automatic setup. + """ + # Check for database type preference + db_type = os.getenv("FLE_DB_TYPE", "sqlite").lower() + + if db_type == "postgres": + # Use PostgreSQL if explicitly requested and configured + required_vars = [ + "SKILLS_DB_HOST", + "SKILLS_DB_PORT", + "SKILLS_DB_NAME", + "SKILLS_DB_USER", + "SKILLS_DB_PASSWORD", + ] + missing_vars = [var for var in required_vars if not os.getenv(var)] + + if missing_vars: + print( + f"Warning: PostgreSQL requested but missing environment variables: {missing_vars}" + ) + print("Falling back to SQLite...") + raise Exception(f"Missing environment variables: {missing_vars}") + db_config = { + "host": os.getenv("SKILLS_DB_HOST"), + "port": os.getenv("SKILLS_DB_PORT"), + "dbname": os.getenv("SKILLS_DB_NAME"), + "user": os.getenv("SKILLS_DB_USER"), + "password": os.getenv("SKILLS_DB_PASSWORD"), + } + create_default_postgres_db(**db_config) + + return PostgresDBClient( + max_conversation_length=max_conversation_length, + min_connections=min_connections, + max_connections=max_connections, + **db_config, + ) + elif db_type == "sqlite": + # Default to SQLite + sqlite_file = os.getenv("SQLITE_DB_FILE", ".fle/data.db") + print(f"Using SQLite database file: {sqlite_file}") + + # Auto-create SQLite database if it doesn't exist + create_default_sqlite_db(sqlite_file) + + return SQLliteDBClient( + max_conversation_length=max_conversation_length, + min_connections=min_connections, + max_connections=max_connections, + database_file=sqlite_file, + ) + else: + raise Exception(f"Invalid database type: {db_type}") diff --git a/fle/env/run_envs.py b/fle/services/docker_manager.py similarity index 88% rename from fle/env/run_envs.py rename to fle/services/docker_manager.py index a12c92327..6b379ddb7 100644 --- a/fle/env/run_envs.py +++ b/fle/services/docker_manager.py @@ -29,9 +29,10 @@ class Mode(Enum): SCENARIO = "scenario" -class PlatformConfig: +class ServerSettings: def __init__(self): self.arch = platform.machine() + self.address = "localhost" self.os_name = platform.system() self.saves_path = ROOT_DIR / ".fle" / "saves" self.screenshots_dir = ROOT_DIR / "data" / "_screenshots" @@ -72,11 +73,11 @@ def _detect_mods_path(self) -> str: ) -class FactorioClusterManager: +class FactorioHeadlessClusterManager: def __init__( self, + config: ServerSettings, docker_platform: str, - config: PlatformConfig, num_instances: int, dry_run: bool = False, ): @@ -85,6 +86,7 @@ def __init__( self.num = num_instances self.dry_run = dry_run self.docker = aiodocker.Docker() + self.docker_configs = {} async def _ensure_image(self): try: @@ -141,6 +143,7 @@ def _get_environment(self) -> list: "RCON_PORT": str(self.config.rcon_port), "SERVER_SCENARIO": self.config.scenario_name, "DLC_SPACE_AGE": "false", + "MODE": self.config.mode, } if self.config.mode == Mode.SCENARIO.value: env["PRESET"] = "default" @@ -275,6 +278,25 @@ async def sync_container(ctr): # Run all container syncs concurrently await asyncio.gather(*[sync_container(ctr) for ctr in containers]) print("Hot-reload sync complete.") + + async def attach_docker_configs(self): + """Attach docker config to FactorioClusterManager""" + containers = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) + container_ids = [ctr.id for ctr in containers] + if not container_ids or container_ids[0] == "": + print("No running Factorio containers found") + return + tasks = [ctr.show() for ctr in containers] + container_infos = await asyncio.gather(*tasks) + info_to_keep = ["Id", "Name", "State", "Image", "HostConfig", "Config", "LogPath", "RestartCount", "Mounts"] + hostconfig_to_keep = ["PortBindings", "Binds", "Memory", "RestartPolicy", "NetworkMode"] + config_to_keep = ["Env", "Entrypoint", "Cmd", "WorkingDir"] + network_to_keep = ["Ports", "IPAddress", "Gateway"] + for container_info in container_infos: + self.docker_configs[container_info["Name"]] = {k: container_info[k] for k in info_to_keep} + self.docker_configs[container_info["Name"]]["Config"] = {k: container_info["Config"][k] for k in config_to_keep} + self.docker_configs[container_info["Name"]]["NetworkSettings"] = {k: container_info["NetworkSettings"][k] for k in network_to_keep} + self.docker_configs[container_info["Name"]]["HostConfig"] = {k: container_info["HostConfig"][k] for k in hostconfig_to_keep} async def get_local_container_ips(self) -> tuple[List[str], List[int], List[int]]: """Get IP addresses of running Factorio containers in the local Docker setup.""" @@ -355,7 +377,7 @@ def parse_args(): async def main(): args = parse_args() - config = PlatformConfig() + config = ServerSettings() docker_platform = ( "linux/arm64" if config.arch in ("arm64", "aarch64") else "linux/amd64" @@ -371,7 +393,7 @@ async def main(): elif args.mode == Mode.SAVE_BASED.value: config.mode = Mode.SAVE_BASED.value - mgr = FactorioClusterManager(docker_platform, config, args.n, args.dry_run) + mgr = FactorioHeadlessClusterManager(config, docker_platform, args.n, args.dry_run) if args.command == "start": await mgr.start() elif args.command == "stop": @@ -385,8 +407,10 @@ async def main(): elif args.command == "get-ips": ips, udp_ports, tcp_ports = await mgr.get_local_container_ips() print(ips, udp_ports, tcp_ports) + await mgr.attach_docker_configs() await mgr.docker.close() + if __name__ == "__main__": asyncio.run(main()) diff --git a/fle/env/utils/rcon.py b/fle/services/rcon.py similarity index 100% rename from fle/env/utils/rcon.py rename to fle/services/rcon.py diff --git a/fle/env/gym_env/trajectory_runner.py b/fle/trajectory_runner.py similarity index 98% rename from fle/env/gym_env/trajectory_runner.py rename to fle/trajectory_runner.py index 42440fdbe..1a744549a 100644 --- a/fle/env/gym_env/trajectory_runner.py +++ b/fle/trajectory_runner.py @@ -4,16 +4,15 @@ from fle.agents import CompletionReason, CompletionResult from fle.agents.gym_agent import GymAgent -from fle.commons.db_client import DBClient from fle.commons.models.conversation import Conversation -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.commons.models.program import Program - +from fle.env.environment import FactorioGymEnv from fle.env.gym_env.action import Action from fle.env.gym_env.config import GymEvalConfig -from fle.env.gym_env.environment import FactorioGymEnv from fle.env.gym_env.observation import Observation from fle.env.gym_env.trajectory_logger import TrajectoryLogger +from fle.services.db_client import DBClient class GymTrajectoryRunner: diff --git a/tests/actions/_test_inspect_entities.py b/tests/actions/_test_inspect_entities.py index f0725a2d3..3c3ae3a68 100644 --- a/tests/actions/_test_inspect_entities.py +++ b/tests/actions/_test_inspect_entities.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Direction, Prototype, Resource +from fle.env.game.entities import Position, Direction, Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_can_place.py b/tests/actions/test_can_place.py index b4023efbe..7d4797181 100644 --- a/tests/actions/test_can_place.py +++ b/tests/actions/test_can_place.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/actions/test_connect_entities_dry_run.py b/tests/actions/test_connect_entities_dry_run.py index a52d2fb45..dc5010bdb 100644 --- a/tests/actions/test_connect_entities_dry_run.py +++ b/tests/actions/test_connect_entities_dry_run.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Prototype diff --git a/tests/actions/test_extract_item.py b/tests/actions/test_extract_item.py index f9db628ce..ca4e4b0e4 100644 --- a/tests/actions/test_extract_item.py +++ b/tests/actions/test_extract_item.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Prototype diff --git a/tests/actions/test_get_entity.py b/tests/actions/test_get_entity.py index 10433c087..50219f54f 100644 --- a/tests/actions/test_get_entity.py +++ b/tests/actions/test_get_entity.py @@ -1,7 +1,7 @@ import pytest from fle.env import DirectionInternal -from fle.env.entities import Furnace, Position +from fle.env.game.entities import Furnace, Position from fle.env.game_types import Prototype, Resource diff --git a/tests/actions/test_harvest_resource.py b/tests/actions/test_harvest_resource.py index e692cabf4..76429520f 100644 --- a/tests/actions/test_harvest_resource.py +++ b/tests/actions/test_harvest_resource.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Resource, Prototype diff --git a/tests/actions/test_insert_item.py b/tests/actions/test_insert_item.py index 83e3a3d89..d6ee18c33 100644 --- a/tests/actions/test_insert_item.py +++ b/tests/actions/test_insert_item.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, EntityStatus, Direction +from fle.env.game.entities import Position, EntityStatus, Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/actions/test_inspect_inventory.py b/tests/actions/test_inspect_inventory.py index 50cf75367..fb9b370d1 100644 --- a/tests/actions/test_inspect_inventory.py +++ b/tests/actions/test_inspect_inventory.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Prototype diff --git a/tests/actions/test_move_to.py b/tests/actions/test_move_to.py index a595e0a2e..957bae2af 100644 --- a/tests/actions/test_move_to.py +++ b/tests/actions/test_move_to.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env import FactorioInstance from fle.env.game_types import Prototype, Resource diff --git a/tests/actions/test_nearest.py b/tests/actions/test_nearest.py index 3bea50bec..66162cb85 100644 --- a/tests/actions/test_nearest.py +++ b/tests/actions/test_nearest.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Resource diff --git a/tests/actions/test_nearest_buildable.py b/tests/actions/test_nearest_buildable.py index fb82ce96b..3678e4fd9 100644 --- a/tests/actions/test_nearest_buildable.py +++ b/tests/actions/test_nearest_buildable.py @@ -2,7 +2,7 @@ from fle.commons.cluster_ips import get_local_container_ips from fle.env.game_types import Prototype, Resource -from fle.env.entities import Position, BuildingBox, Direction +from fle.env.game.entities import Position, BuildingBox, Direction from fle.env import FactorioInstance # @pytest.fixture() diff --git a/tests/actions/test_pickup_entity.py b/tests/actions/test_pickup_entity.py index 4148639c7..ff5f8ddcd 100644 --- a/tests/actions/test_pickup_entity.py +++ b/tests/actions/test_pickup_entity.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Prototype, Resource diff --git a/tests/actions/test_place.py b/tests/actions/test_place.py index ebb6504f9..5c02021ef 100644 --- a/tests/actions/test_place.py +++ b/tests/actions/test_place.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/actions/test_place_entity_next_to.py b/tests/actions/test_place_entity_next_to.py index b6e6ac400..51ccb041b 100644 --- a/tests/actions/test_place_entity_next_to.py +++ b/tests/actions/test_place_entity_next_to.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction from fle.env import DirectionInternal from fle.env.game_types import Prototype, Resource diff --git a/tests/actions/test_place_next_to_and_rotate.py b/tests/actions/test_place_next_to_and_rotate.py index dd6ce5c13..6185f61ba 100644 --- a/tests/actions/test_place_next_to_and_rotate.py +++ b/tests/actions/test_place_next_to_and_rotate.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction from fle.env.game_types import Prototype, Resource from fle.env import DirectionInternal diff --git a/tests/actions/test_render.py b/tests/actions/test_render.py index 1b0e3b6e7..e78b535ca 100644 --- a/tests/actions/test_render.py +++ b/tests/actions/test_render.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Layer +from fle.env.game.entities import Position, Layer from fle.env.game_types import Prototype diff --git a/tests/actions/test_request_path.py b/tests/actions/test_request_path.py index 78677839b..25d0439cc 100644 --- a/tests/actions/test_request_path.py +++ b/tests/actions/test_request_path.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position @pytest.fixture() diff --git a/tests/actions/test_rotate.py b/tests/actions/test_rotate.py index 3fb91a6bb..1d433f36f 100644 --- a/tests/actions/test_rotate.py +++ b/tests/actions/test_rotate.py @@ -1,7 +1,7 @@ import pytest from fle.env.game_types import Prototype, RecipeName -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction @pytest.fixture() diff --git a/tests/actions/test_set_entity_recipe.py b/tests/actions/test_set_entity_recipe.py index b6f210b69..e697d502a 100644 --- a/tests/actions/test_set_entity_recipe.py +++ b/tests/actions/test_set_entity_recipe.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Prototype diff --git a/tests/benchmarks/test_demo_video.py b/tests/benchmarks/test_demo_video.py index 8f1ebd736..2de34abb2 100644 --- a/tests/benchmarks/test_demo_video.py +++ b/tests/benchmarks/test_demo_video.py @@ -3,7 +3,7 @@ # import pytest -# from fle.env.entities import Position +# from fle.env.game.entities import Position # from fle.env import FactorioInstance # from fle.env.game_types import Prototype # from fle.env.utils.profits import eval_program_with_profits diff --git a/tests/benchmarks/test_elapsed_ticks.py b/tests/benchmarks/test_elapsed_ticks.py index 6e49f5f09..204e34cfc 100644 --- a/tests/benchmarks/test_elapsed_ticks.py +++ b/tests/benchmarks/test_elapsed_ticks.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Prototype, Resource diff --git a/tests/blueprints/test_blueprint_based_policies.py b/tests/blueprints/test_blueprint_based_policies.py index c90748403..1a221713b 100644 --- a/tests/blueprints/test_blueprint_based_policies.py +++ b/tests/blueprints/test_blueprint_based_policies.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, BoundingBox # , Direction +from fle.env.game.entities import Position, BoundingBox # , Direction from fle.env import DirectionInternal as Direction from fle.env.game_types import Prototype diff --git a/tests/blueprints/test_save_load.py b/tests/blueprints/test_save_load.py index eea7747ee..4981787bb 100644 --- a/tests/blueprints/test_save_load.py +++ b/tests/blueprints/test_save_load.py @@ -6,7 +6,7 @@ import pytest -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction from fle.env.game_types import Prototype diff --git a/tests/complex/test_edge_cases.py b/tests/complex/test_edge_cases.py index 6ae5bc5e6..d1001666a 100644 --- a/tests/complex/test_edge_cases.py +++ b/tests/complex/test_edge_cases.py @@ -1,6 +1,6 @@ import pytest from time import sleep -from fle.env.entities import Position, ResourcePatch, Direction +from fle.env.game.entities import Position, ResourcePatch, Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/complex/test_slow.py b/tests/complex/test_slow.py index 2a1aa5e36..773be9a4f 100644 --- a/tests/complex/test_slow.py +++ b/tests/complex/test_slow.py @@ -1,5 +1,5 @@ import pytest -from fle.env.instance import FactorioInstance +from fle.env.game.instance import FactorioInstance from fle.env.game_types import Resource diff --git a/tests/connect/test_connect_pipes.py b/tests/connect/test_connect_pipes.py index 21603c640..0e1719688 100644 --- a/tests/connect/test_connect_pipes.py +++ b/tests/connect/test_connect_pipes.py @@ -2,7 +2,7 @@ import pytest -from fle.env.entities import ( +from fle.env.game.entities import ( BuildingBox, Direction, Entity, diff --git a/tests/connect/test_connect_poles.py b/tests/connect/test_connect_poles.py index 1e4fba706..616686b56 100644 --- a/tests/connect/test_connect_poles.py +++ b/tests/connect/test_connect_poles.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, EntityStatus, BuildingBox, Direction +from fle.env.game.entities import Position, EntityStatus, BuildingBox, Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/connect/test_connect_transport_belts.py b/tests/connect/test_connect_transport_belts.py index 7a60a60c0..7c2b187e5 100644 --- a/tests/connect/test_connect_transport_belts.py +++ b/tests/connect/test_connect_transport_belts.py @@ -2,7 +2,7 @@ import pytest -from fle.env.entities import Position, ResourcePatch, BeltGroup, Direction +from fle.env.game.entities import Position, ResourcePatch, BeltGroup, Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/connect/test_connect_underground_belts.py b/tests/connect/test_connect_underground_belts.py index 43f2985f1..52b9e119f 100644 --- a/tests/connect/test_connect_underground_belts.py +++ b/tests/connect/test_connect_underground_belts.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, UndergroundBelt +from fle.env.game.entities import Position, UndergroundBelt from fle.env.game_types import Prototype diff --git a/tests/connect/test_connect_underground_pipes.py b/tests/connect/test_connect_underground_pipes.py index 49fa6988f..e5cbff4df 100644 --- a/tests/connect/test_connect_underground_pipes.py +++ b/tests/connect/test_connect_underground_pipes.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, UndergroundBelt +from fle.env.game.entities import Position, UndergroundBelt from fle.env.game_types import Prototype diff --git a/tests/connect/test_connect_walls.py b/tests/connect/test_connect_walls.py index e86d70db7..4cb6db761 100644 --- a/tests/connect/test_connect_walls.py +++ b/tests/connect/test_connect_walls.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env.game_types import Prototype diff --git a/tests/connect/test_fluid_handlers.py b/tests/connect/test_fluid_handlers.py index 2f35b4bb7..eba3eaa44 100644 --- a/tests/connect/test_fluid_handlers.py +++ b/tests/connect/test_fluid_handlers.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, BuildingBox +from fle.env.game.entities import Position, BuildingBox from fle.env import DirectionInternal as Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/entities/test_fluid_processors.py b/tests/entities/test_fluid_processors.py index 2d7fbb0cf..a2eaa3b1f 100644 --- a/tests/entities/test_fluid_processors.py +++ b/tests/entities/test_fluid_processors.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Direction, Entity +from fle.env.game.entities import Position, Direction, Entity from fle.env.game_types import Prototype, RecipeName, prototype_by_name, Resource diff --git a/tests/entities/test_rockets.py b/tests/entities/test_rockets.py index 75867fbfa..cf4117114 100644 --- a/tests/entities/test_rockets.py +++ b/tests/entities/test_rockets.py @@ -1,5 +1,5 @@ import json -from fle.env.entities import Position, Direction, EntityStatus +from fle.env.game.entities import Position, Direction, EntityStatus from fle.env.game_types import Resource, Prototype import pytest diff --git a/tests/eval/test_game_state.py b/tests/eval/test_game_state.py index 0ab820d77..fd0c28738 100644 --- a/tests/eval/test_game_state.py +++ b/tests/eval/test_game_state.py @@ -1,6 +1,6 @@ from pydantic import BaseModel from fle.env import FactorioInstance -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState def test_game_state(): diff --git a/tests/eval/test_save_load_python_namespace.py b/tests/eval/test_save_load_python_namespace.py index 95db4b094..8d4e4caae 100644 --- a/tests/eval/test_save_load_python_namespace.py +++ b/tests/eval/test_save_load_python_namespace.py @@ -2,7 +2,7 @@ import unittest from fle.eval.algorithms.mcts.chunked_mcts import ChunkedMCTS -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState from fle.env.game_types import Prototype from fle.env import FactorioInstance diff --git a/tests/functional/test_auto_fueling_iron_smelting_factory.py b/tests/functional/test_auto_fueling_iron_smelting_factory.py index 7c39c2f96..c9ff03353 100644 --- a/tests/functional/test_auto_fueling_iron_smelting_factory.py +++ b/tests/functional/test_auto_fueling_iron_smelting_factory.py @@ -1,7 +1,7 @@ import pytest from time import sleep -from fle.env.entities import Position +from fle.env.game.entities import Position from fle.env import DirectionInternal as Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/functional/test_auto_refilling_coal.py b/tests/functional/test_auto_refilling_coal.py index 68e0597ee..36cbb24ba 100644 --- a/tests/functional/test_auto_refilling_coal.py +++ b/tests/functional/test_auto_refilling_coal.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/functional/test_chemical_plants.py b/tests/functional/test_chemical_plants.py index 2a1ee2adf..fe7a61110 100644 --- a/tests/functional/test_chemical_plants.py +++ b/tests/functional/test_chemical_plants.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Entity +from fle.env.game.entities import Position, Entity from fle.env import DirectionInternal as Direction from fle.env.game_types import Prototype, Resource, RecipeName diff --git a/tests/functional/test_defence.py b/tests/functional/test_defence.py index bc07fcc48..338886a79 100644 --- a/tests/functional/test_defence.py +++ b/tests/functional/test_defence.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction from fle.env.game_types import Prototype diff --git a/tests/functional/test_electricity_unit.py b/tests/functional/test_electricity_unit.py index ac1df8859..ed96053c1 100644 --- a/tests/functional/test_electricity_unit.py +++ b/tests/functional/test_electricity_unit.py @@ -2,7 +2,7 @@ import pytest -from fle.env.entities import ( +from fle.env.game.entities import ( Entity, Position, ResourcePatch, diff --git a/tests/functional/test_full_oil_chain.py b/tests/functional/test_full_oil_chain.py index c11a28b06..6c27009ba 100644 --- a/tests/functional/test_full_oil_chain.py +++ b/tests/functional/test_full_oil_chain.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, BuildingBox +from fle.env.game.entities import Position, BuildingBox from fle.env.game_types import Prototype, Resource, RecipeName diff --git a/tests/functional/test_multiple_drills_multiple_furnaces.py b/tests/functional/test_multiple_drills_multiple_furnaces.py index 831157600..c4bac7497 100644 --- a/tests/functional/test_multiple_drills_multiple_furnaces.py +++ b/tests/functional/test_multiple_drills_multiple_furnaces.py @@ -1,6 +1,6 @@ import pytest -from fle.env.entities import Position, Direction, BuildingBox +from fle.env.game.entities import Position, Direction, BuildingBox from fle.env.game_types import Prototype, Resource diff --git a/tests/functional/test_objectives.py b/tests/functional/test_objectives.py index bf3d86510..5ab206653 100644 --- a/tests/functional/test_objectives.py +++ b/tests/functional/test_objectives.py @@ -3,7 +3,7 @@ import pytest -from fle.env.entities import ( +from fle.env.game.entities import ( Entity, Position, ResourcePatch, diff --git a/tests/functional/test_research.py b/tests/functional/test_research.py index 518d56603..bb0d947a3 100644 --- a/tests/functional/test_research.py +++ b/tests/functional/test_research.py @@ -1,10 +1,10 @@ import pytest from fle.commons.cluster_ips import get_local_container_ips -from fle.env.instance import FactorioInstance -from fle.env.entities import Direction +from fle.env.game.instance import FactorioInstance +from fle.env.game.entities import Direction from fle.env.game_types import Resource, Prototype, Technology -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState @pytest.fixture() diff --git a/tests/functional/test_small_iron_factory.py b/tests/functional/test_small_iron_factory.py index 7cd3139a9..208f546c8 100644 --- a/tests/functional/test_small_iron_factory.py +++ b/tests/functional/test_small_iron_factory.py @@ -1,5 +1,5 @@ import pytest -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction from fle.env.game_types import Prototype, Resource diff --git a/tests/gym_env/test_observation.py b/tests/gym_env/test_observation.py index 8da2d0cde..14bcabd61 100644 --- a/tests/gym_env/test_observation.py +++ b/tests/gym_env/test_observation.py @@ -2,7 +2,7 @@ from fle.env.gym_env.action import Action # from fle.env.gym_env.validation import validate_observation -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction from fle.env.game_types import Prototype diff --git a/tests/gym_env/test_registry.py b/tests/gym_env/test_registry.py index 96f33adfb..ef9709164 100644 --- a/tests/gym_env/test_registry.py +++ b/tests/gym_env/test_registry.py @@ -16,7 +16,7 @@ # Add the project root to the path so we can import our modules sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "..")) -from fle.env.gym_env.registry import list_available_environments, get_environment_info +from fle.env.registry import list_available_environments, get_environment_info from fle.env.gym_env.action import Action diff --git a/tests/multiagent/test_agent_instructions.py b/tests/multiagent/test_agent_instructions.py index ec0ba6d8e..2a1d17ccf 100644 --- a/tests/multiagent/test_agent_instructions.py +++ b/tests/multiagent/test_agent_instructions.py @@ -1,6 +1,6 @@ import pytest from fle.agents.basic_agent import BasicAgent -from fle.eval.tasks import UnboundedThroughputTask +from fle.env.tasks import UnboundedThroughputTask def test_basic_agent_instructions(): diff --git a/tests/multiagent/test_messages.py b/tests/multiagent/test_messages.py index 9c8006813..93ccedc7c 100644 --- a/tests/multiagent/test_messages.py +++ b/tests/multiagent/test_messages.py @@ -5,12 +5,12 @@ import pytest from fle.agents.basic_agent import BasicAgent -from fle.commons.db_client import PostgresDBClient +from fle.services.db_client import PostgresDBClient from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message from fle.env.a2a_instance import A2AFactorioInstance from fle.eval.algorithms.independent import SimpleFactorioEvaluator, TrajectoryRunner -from fle.eval.tasks.default_task import DefaultTask +from fle.env.tasks.default_task import DefaultTask @pytest.fixture diff --git a/tests/test_inventory_deduction.py b/tests/test_inventory_deduction.py index 53782b95c..b4b6434b8 100644 --- a/tests/test_inventory_deduction.py +++ b/tests/test_inventory_deduction.py @@ -1,7 +1,7 @@ import pytest from fle.env import FactorioInstance from fle.env.game_types import Prototype -from fle.env.entities import Position, Direction +from fle.env.game.entities import Position, Direction @pytest.fixture() def game(instance): diff --git a/tests/test_production_stats.py b/tests/test_production_stats.py index 9538f9cd0..218b9b2ad 100644 --- a/tests/test_production_stats.py +++ b/tests/test_production_stats.py @@ -1,7 +1,7 @@ import unittest from fle.env import FactorioInstance -from fle.env.utils.rcon import _lua2python +from fle.services.rcon import _lua2python from fle.env.game_types import Resource diff --git a/tests/test_rcon_utils.py b/tests/test_rcon_utils.py index 9ea211139..16b81b5c8 100644 --- a/tests/test_rcon_utils.py +++ b/tests/test_rcon_utils.py @@ -1,6 +1,6 @@ import pytest -from fle.env.utils.rcon import _lua2python +from fle.services.rcon import _lua2python @pytest.fixture() diff --git a/tests/test_warnings.py b/tests/test_warnings.py index 7c42ffb67..c5eea4362 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -3,7 +3,7 @@ from fle.commons.cluster_ips import get_local_container_ips from fle.env.game_types import Prototype, Resource from fle.env import FactorioInstance, Direction -from fle.commons.models.game_state import GameState +from fle.env.game.game_state import GameState def test_drop_box_chest(): From 46a0c9eb6a20169878a2b82ffbb15bd43ea533ef Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 31 Jul 2025 17:02:41 +0530 Subject: [PATCH 33/53] remove cluster directory --- fle/cluster/__init__.py | 0 fle/{cluster => services}/remote/README.md | 0 fle/{cluster => services}/remote/cluster.cloudformation.yaml | 0 fle/{cluster => services}/remote/cluster_ips.py | 0 fle/{cluster => services}/remote/factorio_server_login.py | 2 +- 5 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 fle/cluster/__init__.py rename fle/{cluster => services}/remote/README.md (100%) rename fle/{cluster => services}/remote/cluster.cloudformation.yaml (100%) rename fle/{cluster => services}/remote/cluster_ips.py (100%) rename fle/{cluster => services}/remote/factorio_server_login.py (98%) diff --git a/fle/cluster/__init__.py b/fle/cluster/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/fle/cluster/remote/README.md b/fle/services/remote/README.md similarity index 100% rename from fle/cluster/remote/README.md rename to fle/services/remote/README.md diff --git a/fle/cluster/remote/cluster.cloudformation.yaml b/fle/services/remote/cluster.cloudformation.yaml similarity index 100% rename from fle/cluster/remote/cluster.cloudformation.yaml rename to fle/services/remote/cluster.cloudformation.yaml diff --git a/fle/cluster/remote/cluster_ips.py b/fle/services/remote/cluster_ips.py similarity index 100% rename from fle/cluster/remote/cluster_ips.py rename to fle/services/remote/cluster_ips.py diff --git a/fle/cluster/remote/factorio_server_login.py b/fle/services/remote/factorio_server_login.py similarity index 98% rename from fle/cluster/remote/factorio_server_login.py rename to fle/services/remote/factorio_server_login.py index 8b36a3d2c..961f42350 100644 --- a/fle/cluster/remote/factorio_server_login.py +++ b/fle/services/remote/factorio_server_login.py @@ -8,7 +8,7 @@ import sys from dotenv import load_dotenv -from fle.cluster.remote.cluster_ips import get_public_ips +from fle.services.remote.cluster_ips import get_public_ips from fle.env import FactorioInstance """ From 8bc383e7585be575b0ed7b31cb2c8f36f9daf795 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 31 Jul 2025 17:08:07 +0530 Subject: [PATCH 34/53] client goes to game --- fle/env/{ => game}/factorio_client.py | 2 +- fle/env/game/instance.py | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) rename fle/env/{ => game}/factorio_client.py (100%) diff --git a/fle/env/factorio_client.py b/fle/env/game/factorio_client.py similarity index 100% rename from fle/env/factorio_client.py rename to fle/env/game/factorio_client.py index c819812a5..96eb35584 100644 --- a/fle/env/factorio_client.py +++ b/fle/env/game/factorio_client.py @@ -7,9 +7,9 @@ from slpp import slpp as lua from typing_extensions import Any, Dict, List, Tuple -from fle.services.docker_manager import ServerSettings from fle.env.game.lua_manager import LuaScriptManager, ToolHookRegistry from fle.env.game.namespace import FactorioNamespace +from fle.services.docker_manager import ServerSettings from fle.services.rcon import _lua2python diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index 9afb11d4b..80733777f 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -9,23 +9,20 @@ import types import uuid from concurrent.futures import TimeoutError - from pathlib import Path from timeit import default_timer as timer from dotenv import load_dotenv from typing_extensions import Any, Dict, List, Optional -from fle.env.game.game_state import GameState from fle.commons.models.research_state import ResearchState +from fle.env.game.factorio_client import FactorioClient +from fle.env.game.game_state import GameState from fle.env.game.namespace import FactorioNamespace -from fle.env.utils.controller_loader.system_prompt_generator import ( - SystemPromptGenerator, -) -from fle.env.factorio_client import FactorioClient +from fle.env.utils.controller_loader.system_prompt_generator import \ + SystemPromptGenerator from fle.services.rcon import _lua2python - CHUNK_SIZE = 32 MAX_SAMPLES = 5000 From 05a012ec657f9b6bb234bc82dbdd17da9edd54d0 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 31 Jul 2025 18:11:42 +0530 Subject: [PATCH 35/53] import path changes --- fle/__init__.py | 5 +- .../blueprint_analyzer.py | 6 +-- .../blueprint_analyzer_with_connect.py | 6 +-- .../blueprint_analyzer_with_place_next_to.py | 6 +-- .../blueprint_refactor.py | 6 +-- .../blueprints_to_policies/loop_generator.py | 2 +- .../trajectory_generator.py | 6 +-- fle/agents/data/run_trace.py | 2 +- fle/agents/data/screenshots_from_run.py | 2 +- fle/env/a2a_instance.py | 4 +- fle/env/environment.py | 2 +- fle/env/{ => game}/__init__.py | 2 +- fle/env/game/entities.py | 2 +- .../exceptions/hinting_name_error.py | 0 fle/env/game/game_types.py | 2 +- fle/env/game/instance.py | 40 +++++++------- fle/env/game/namespace.py | 7 ++- fle/env/game/tools/__init__.py | 6 +-- .../admin/clear_collision_boxes/client.py | 2 +- .../game/tools/admin/clear_entities/client.py | 2 +- .../admin/extend_collision_boxes/client.py | 2 +- .../admin/get_factory_centroid/client.py | 2 +- fle/env/game/tools/admin/get_path/client.py | 2 +- .../admin/get_production_stats/client.py | 2 +- .../game/tools/admin/load_blueprint/client.py | 2 +- .../tools/admin/load_entity_state/client.py | 2 +- .../tools/admin/load_research_state/client.py | 2 +- .../tools/admin/production_stats/client.py | 2 +- .../admin/regenerate_resources/client.py | 2 +- fle/env/game/tools/admin/render/client.py | 10 ++-- .../layers/connection_layers_renderer.py | 2 +- .../render/layers/entities_layer_renderer.py | 2 +- .../render/layers/grid_layer_renderer.py | 2 +- .../admin/render/layers/layer_renderer.py | 2 +- .../render/layers/marker_layers_renderer.py | 2 +- .../render/layers/natural_layer_renderer.py | 2 +- .../render/layers/resource_layer_renderer.py | 2 +- .../render/layers/water_layer_renderer.py | 2 +- fle/env/game/tools/admin/render/renderer.py | 30 +++++------ .../admin/render/utils/colour_manager.py | 6 +-- .../admin/render/utils/connection_renderer.py | 4 +- .../render/utils/electricity_renderer.py | 2 +- .../admin/render/utils/entity_categoriser.py | 4 +- .../admin/render/utils/image_calculator.py | 2 +- .../admin/render/utils/legend_renderer.py | 8 +-- .../admin/render/utils/natural_renderer.py | 2 +- .../admin/render/utils/shape_renderer.py | 2 +- .../tools/admin/render/utils/tile_renderer.py | 2 +- .../game/tools/admin/render_message/client.py | 2 +- .../game/tools/admin/request_path/client.py | 2 +- .../game/tools/admin/save_blueprint/client.py | 2 +- .../tools/admin/save_entity_state/client.py | 2 +- .../tools/admin/save_research_state/client.py | 2 +- .../tools/agent/can_place_entity/client.py | 6 +-- .../tools/agent/connect_entities/client.py | 38 +++++++------- .../connect_entities/groupable_entities.py | 4 +- .../resolvers/fluid_connection_resolver.py | 6 +-- .../resolvers/power_connection_resolver.py | 2 +- .../transport_connection_resolver.py | 2 +- fle/env/game/tools/agent/craft_item/client.py | 6 +-- .../game/tools/agent/extract_item/client.py | 4 +- .../agent/get_connection_amount/client.py | 6 +-- .../game/tools/agent/get_entities/client.py | 6 +-- fle/env/game/tools/agent/get_entity/client.py | 6 +-- .../agent/get_prototype_recipe/client.py | 6 +-- .../agent/get_research_progress/client.py | 6 +-- .../tools/agent/get_resource_patch/client.py | 4 +- .../tools/agent/harvest_resource/client.py | 10 ++-- .../game/tools/agent/insert_item/client.py | 6 +-- .../tools/agent/inspect_inventory/client.py | 4 +- .../game/tools/agent/launch_rocket/client.py | 6 +-- fle/env/game/tools/agent/move_to/client.py | 8 +-- fle/env/game/tools/agent/nearest/client.py | 4 +- .../tools/agent/nearest_buildable/client.py | 6 +-- .../game/tools/agent/pickup_entity/client.py | 6 +-- .../game/tools/agent/place_entity/client.py | 10 ++-- .../agent/place_entity_next_to/client.py | 6 +-- fle/env/game/tools/agent/print/client.py | 2 +- .../game/tools/agent/rotate_entity/client.py | 6 +-- fle/env/game/tools/agent/score/client.py | 2 +- .../game/tools/agent/send_message/client.py | 4 +- .../tools/agent/set_entity_recipe/client.py | 4 +- .../game/tools/agent/set_research/client.py | 4 +- .../game/tools/agent/shift_entity/client.py | 14 ++--- fle/env/game/tools/agent/sleep/client.py | 2 +- fle/env/game/tools/init.py | 2 +- fle/env/game/tools/tool.py | 2 +- fle/env/game_session.py | 52 ------------------- fle/env/protocols/mcp/repository.py | 2 +- fle/env/protocols/mcp/state.py | 2 +- fle/env/registry.py | 2 +- fle/env/tasks/default_task.py | 2 +- fle/env/tasks/task_abc.py | 4 +- fle/env/tasks/throughput_task.py | 4 +- fle/env/tasks/unbounded_throughput_task.py | 4 +- fle/eval/algorithms/beam/beam_search.py | 2 +- .../algorithms/beam/beam_search_milestones.py | 2 +- fle/eval/algorithms/beam/run_milestones.py | 2 +- fle/eval/algorithms/beam/run_parallel.py | 2 +- .../independent/simple_evaluator.py | 2 +- .../independent/trajectory_runner.py | 2 +- fle/eval/algorithms/mcts/__main__chunked.py | 2 +- .../algorithms/mcts/__main__objective_mcts.py | 2 +- .../algorithms/mcts/blueprints_to_programs.py | 2 +- fle/eval/algorithms/mcts/main.py | 2 +- fle/eval/algorithms/mcts/parallel_mcts.py | 2 +- .../algorithms/mcts/parallel_planning_mcts.py | 2 +- fle/eval/algorithms/mcts/plan_sampler.py | 2 +- .../samplers/blueprint_scenario_sampler.py | 2 +- .../mcts/supervised_task_executor_abc.py | 2 +- fle/eval/entrypoints/beam_run.py | 2 +- fle/eval/entrypoints/mcts_planning_agent.py | 2 +- fle/services/remote/factorio_server_login.py | 2 +- tests/actions/test_can_place.py | 2 +- .../actions/test_connect_entities_dry_run.py | 2 +- tests/actions/test_craft.py | 2 +- tests/actions/test_extract_item.py | 2 +- tests/actions/test_get_entities.py | 4 +- tests/actions/test_get_entity.py | 4 +- tests/actions/test_get_prototype_recipe.py | 2 +- tests/actions/test_get_research_progress.py | 4 +- tests/actions/test_get_resource_patch.py | 6 +-- tests/actions/test_harvest_resource.py | 2 +- tests/actions/test_insert_item.py | 2 +- tests/actions/test_inspect_inventory.py | 2 +- tests/actions/test_move_to.py | 4 +- tests/actions/test_nearest.py | 2 +- tests/actions/test_nearest_buildable.py | 4 +- tests/actions/test_pickup_entity.py | 2 +- tests/actions/test_place.py | 2 +- tests/actions/test_place_entity_next_to.py | 4 +- .../actions/test_place_next_to_and_rotate.py | 4 +- tests/actions/test_render.py | 2 +- tests/actions/test_rotate.py | 2 +- tests/actions/test_set_entity_recipe.py | 2 +- tests/actions/test_set_research.py | 4 +- tests/benchmarks/benchmark_api.py | 2 +- tests/benchmarks/benchmark_interpreter.py | 2 +- tests/benchmarks/test_demo_video.py | 2 +- tests/benchmarks/test_elapsed_ticks.py | 2 +- tests/benchmarks/test_script_caching.py | 2 +- .../test_blueprint_based_policies.py | 4 +- tests/blueprints/test_save_load.py | 2 +- tests/complex/test_edge_cases.py | 2 +- tests/complex/test_slow.py | 2 +- tests/conftest.py | 2 +- tests/connect/test_connect_pipes.py | 2 +- tests/connect/test_connect_poles.py | 2 +- tests/connect/test_connect_transport_belts.py | 2 +- .../connect/test_connect_underground_belts.py | 2 +- .../connect/test_connect_underground_pipes.py | 2 +- tests/connect/test_connect_walls.py | 2 +- tests/connect/test_fluid_handlers.py | 4 +- tests/entities/test_assemblers.py | 4 +- tests/entities/test_drill.py | 4 +- tests/entities/test_fluid_processors.py | 2 +- tests/entities/test_inserters.py | 4 +- tests/entities/test_nuclear_reactor.py | 4 +- tests/entities/test_power_generators.py | 4 +- tests/entities/test_rockets.py | 2 +- tests/eval/test_achievements.py | 2 +- tests/eval/test_game_state.py | 2 +- tests/eval/test_production_divergence.py | 2 +- tests/eval/test_profits.py | 2 +- tests/eval/test_save_load_python_namespace.py | 4 +- ...test_auto_fueling_iron_smelting_factory.py | 4 +- tests/functional/test_auto_refilling_coal.py | 2 +- tests/functional/test_chemical_plants.py | 4 +- tests/functional/test_defence.py | 2 +- tests/functional/test_electricity_unit.py | 4 +- tests/functional/test_factory_unit.py | 6 +-- tests/functional/test_full_oil_chain.py | 2 +- .../test_multi_drill_multi_furnace.py | 4 +- .../test_multiple_drills_multiple_furnaces.py | 2 +- tests/functional/test_objectives.py | 2 +- tests/functional/test_research.py | 2 +- tests/functional/test_small_iron_factory.py | 2 +- tests/gym_env/test_observation.py | 2 +- tests/status/test_pipes_status.py | 4 +- tests/status/test_poles_status.py | 4 +- tests/status/test_pump_status.py | 4 +- tests/test_eval.py | 2 +- tests/test_inventory_deduction.py | 4 +- tests/test_production_stats.py | 4 +- tests/test_warnings.py | 4 +- 185 files changed, 347 insertions(+), 401 deletions(-) rename fle/env/{ => game}/__init__.py (87%) rename fle/env/{ => game}/exceptions/hinting_name_error.py (100%) diff --git a/fle/__init__.py b/fle/__init__.py index 0e77ba3f7..bc4dbfb76 100644 --- a/fle/__init__.py +++ b/fle/__init__.py @@ -1,6 +1,7 @@ """Factorio Learning Environment (FLE) package.""" # Make submodules available -from fle import agents, env, eval, cluster, commons +from fle import agents, eval, commons, services +from fle.env import game -__all__ = ["agents", "env", "eval", "cluster", "commons"] +__all__ = ["agents", "game", "eval", "commons", "services"] diff --git a/fle/agents/data/blueprints_to_policies/blueprint_analyzer.py b/fle/agents/data/blueprints_to_policies/blueprint_analyzer.py index f6ba6d856..e3a2dab50 100644 --- a/fle/agents/data/blueprints_to_policies/blueprint_analyzer.py +++ b/fle/agents/data/blueprints_to_policies/blueprint_analyzer.py @@ -4,9 +4,9 @@ from typing import List, Dict from typing import Union -from fle.env import EntityGroup -from fle.env import FactorioInstance -from fle.env.game_types import prototype_by_name +from fle.env.game import EntityGroup +from fle.env.game import FactorioInstance +from fle.env.game.game_types import prototype_by_name from data.blueprints_to_policies.models.blueprint_entity import BlueprintEntity diff --git a/fle/agents/data/blueprints_to_policies/blueprint_analyzer_with_connect.py b/fle/agents/data/blueprints_to_policies/blueprint_analyzer_with_connect.py index d0b9c5c52..eb8abc275 100644 --- a/fle/agents/data/blueprints_to_policies/blueprint_analyzer_with_connect.py +++ b/fle/agents/data/blueprints_to_policies/blueprint_analyzer_with_connect.py @@ -4,9 +4,9 @@ from typing import List, Dict from typing import Union -from fle.env import EntityGroup -from fle.env import FactorioInstance -from fle.env.game_types import prototype_by_name +from fle.env.game import EntityGroup +from fle.env.game import FactorioInstance +from fle.env.game.game_types import prototype_by_name from data.blueprints_to_policies.models.blueprint_entity import BlueprintEntity diff --git a/fle/agents/data/blueprints_to_policies/blueprint_analyzer_with_place_next_to.py b/fle/agents/data/blueprints_to_policies/blueprint_analyzer_with_place_next_to.py index 0b6b804d3..98784fa24 100644 --- a/fle/agents/data/blueprints_to_policies/blueprint_analyzer_with_place_next_to.py +++ b/fle/agents/data/blueprints_to_policies/blueprint_analyzer_with_place_next_to.py @@ -5,9 +5,9 @@ from typing import List, Dict, Optional, Tuple from typing import Union -from fle.env import EntityGroup -from fle.env import FactorioInstance, Direction -from fle.env.game_types import prototype_by_name, Resource +from fle.env.game import EntityGroup +from fle.env.game import FactorioInstance, Direction +from fle.env.game.game_types import prototype_by_name, Resource @dataclass diff --git a/fle/agents/data/blueprints_to_policies/blueprint_refactor.py b/fle/agents/data/blueprints_to_policies/blueprint_refactor.py index 4219bdbed..fb3555f8e 100644 --- a/fle/agents/data/blueprints_to_policies/blueprint_refactor.py +++ b/fle/agents/data/blueprints_to_policies/blueprint_refactor.py @@ -16,10 +16,10 @@ from cluster.remote.cluster_ips import get_public_ips from data.blueprints_to_policies.processing_state import ProcessingState -from fle.env import Position -from fle.env import FactorioInstance +from fle.env.game import Position +from fle.env.game import FactorioInstance from blueprint_analyzer import BlueprintAnalyzer -from fle.env.game_types import Resource +from fle.env.game.game_types import Resource load_dotenv() diff --git a/fle/agents/data/blueprints_to_policies/loop_generator.py b/fle/agents/data/blueprints_to_policies/loop_generator.py index 15f0eb068..c626bb1d7 100644 --- a/fle/agents/data/blueprints_to_policies/loop_generator.py +++ b/fle/agents/data/blueprints_to_policies/loop_generator.py @@ -4,7 +4,7 @@ from dataclasses import dataclass from typing import List, Dict, Tuple, Set import json -from fle.env.game_types import prototype_by_name +from fle.env.game.game_types import prototype_by_name @dataclass diff --git a/fle/agents/data/blueprints_to_policies/trajectory_generator.py b/fle/agents/data/blueprints_to_policies/trajectory_generator.py index aded026c2..3c98ee9ba 100644 --- a/fle/agents/data/blueprints_to_policies/trajectory_generator.py +++ b/fle/agents/data/blueprints_to_policies/trajectory_generator.py @@ -3,9 +3,9 @@ from jinja2 import Template -from fle.env import Position, BoundingBox, EntityGroup -from fle.env import FactorioInstance, Direction -from fle.env.game_types import Resource, prototype_by_name +from fle.env.game import Position, BoundingBox, EntityGroup +from fle.env.game import FactorioInstance, Direction +from fle.env.game.game_types import Resource, prototype_by_name from enum import Enum from typing import Dict, List, Optional, Tuple, NamedTuple diff --git a/fle/agents/data/run_trace.py b/fle/agents/data/run_trace.py index 3b100b51c..13179c3e6 100644 --- a/fle/agents/data/run_trace.py +++ b/fle/agents/data/run_trace.py @@ -3,7 +3,7 @@ from dotenv import load_dotenv from fle.services.db_client import DBClient -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance # Load environment variables load_dotenv() diff --git a/fle/agents/data/screenshots_from_run.py b/fle/agents/data/screenshots_from_run.py index 0eed835f7..a078a6dda 100644 --- a/fle/agents/data/screenshots_from_run.py +++ b/fle/agents/data/screenshots_from_run.py @@ -5,7 +5,7 @@ from dotenv import load_dotenv from data.screenshots_to_mp4 import png_to_mp4 -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.commons.models.program import Program from fle.cluster.local.cluster_ips import get_local_container_ips diff --git a/fle/env/a2a_instance.py b/fle/env/a2a_instance.py index a07ecebc4..0e28afad5 100644 --- a/fle/env/a2a_instance.py +++ b/fle/env/a2a_instance.py @@ -5,7 +5,7 @@ from a2a.types import AgentCard -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.a2a_namespace import A2AFactorioNamespace from fle.env.protocols.a2a.server import ServerManager @@ -55,7 +55,7 @@ async def __aenter__(self): async def __aexit__(self, exc_type, exc_val, exc_tb): """Async context manager exit""" - self.server.cleanup() + self.client.cleanup() @classmethod def _ensure_server_running(cls): diff --git a/fle/env/environment.py b/fle/env/environment.py index 667260f7d..e315eff42 100644 --- a/fle/env/environment.py +++ b/fle/env/environment.py @@ -11,7 +11,7 @@ from fle.agents import Response, TaskResponse from fle.commons.models.achievements import ProductionFlows from fle.env.game.game_state import GameState -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.gym_env.action import Action from fle.env.gym_env.observation import AgentMessage, GameInfo, Observation from fle.env.tasks import TaskABC diff --git a/fle/env/__init__.py b/fle/env/game/__init__.py similarity index 87% rename from fle/env/__init__.py rename to fle/env/game/__init__.py index 2cd441e4f..6809d6649 100644 --- a/fle/env/__init__.py +++ b/fle/env/game/__init__.py @@ -3,7 +3,7 @@ from fle.env.game.entities import * # noqa from fle.env.game.game_types import Prototype, Resource from fle.env.game.instance import DirectionInternal, FactorioInstance -from fle.env.factorio_client import FactorioClient +from fle.env.game.factorio_client import FactorioClient __all__ = [ "FactorioInstance", diff --git a/fle/env/game/entities.py b/fle/env/game/entities.py index cda514032..578437104 100644 --- a/fle/env/game/entities.py +++ b/fle/env/game/entities.py @@ -850,7 +850,7 @@ class Lab(Entity, Electric): _width: float = 3 def __repr__(self) -> str: - from fle.env.game_types import technology_by_name + from fle.env.game.game_types import technology_by_name research_string = "" if self.research and self.research in technology_by_name: diff --git a/fle/env/exceptions/hinting_name_error.py b/fle/env/game/exceptions/hinting_name_error.py similarity index 100% rename from fle/env/exceptions/hinting_name_error.py rename to fle/env/game/exceptions/hinting_name_error.py diff --git a/fle/env/game/game_types.py b/fle/env/game/game_types.py index 69eff9e37..7bd673026 100644 --- a/fle/env/game/game_types.py +++ b/fle/env/game/game_types.py @@ -1,7 +1,7 @@ from __future__ import annotations import enum from difflib import get_close_matches -from fle.env import entities as ent +import fle.env.game.entities as ent class ResourceName(enum.Enum): diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index 80733777f..c0ee1484f 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -22,6 +22,7 @@ from fle.env.utils.controller_loader.system_prompt_generator import \ SystemPromptGenerator from fle.services.rcon import _lua2python +from fle.services.docker_manager import ServerSettings CHUNK_SIZE = 32 MAX_SAMPLES = 5000 @@ -65,15 +66,13 @@ class FactorioInstance: namespace_class = FactorioNamespace _cleanup_registered = False # Only register cleanup once per process namespaces: List[FactorioNamespace] - server: FactorioClient + client: FactorioClient def __init__( self, - address=None, + server_settings: ServerSettings, fast=False, - tcp_port=27000, inventory=None, - cache_scripts=True, all_technologies_researched=True, peaceful=True, num_agents=1, @@ -83,7 +82,6 @@ def __init__( self.num_agents = num_agents self.persistent_vars = {} self.tcp_port = tcp_port - self.server = FactorioClient(address, tcp_port, cache_scripts) self.all_technologies_researched = all_technologies_researched self.fast = fast self._speed = 1 @@ -94,7 +92,7 @@ def __init__( self.namespaces = [self.namespace_class(self, i) for i in range(num_agents)] # Register controllers with the server - self.server.register_controllers(self.namespaces) + self.client.register_controllers(self.namespaces) if inventory is None: inventory = {} @@ -105,7 +103,7 @@ def __init__( self.first_namespace.score() except Exception: # Invalidate cache if there is an error - self.server.register_controllers(self.namespaces, invalidate_cache=True) + self.client.register_controllers(self.namespaces, invalidate_cache=True) self.initialise(fast) self.initial_score, goal = self.first_namespace.score() @@ -132,14 +130,14 @@ def is_multiagent(self): return self.num_agents > 1 def set_speed(self, speed): - self.server.rcon_client.send_command(f"/sc game.speed = {speed}") + self.client.rcon_client.send_command(f"/sc game.speed = {speed}") self._speed = speed def get_speed(self): return self._speed def get_elapsed_ticks(self): - response = self.server.run_rcon_print("global.elapsed_ticks or 0") + response = self.client.run_rcon_print("global.elapsed_ticks or 0") if not response: return 0 return int(response) @@ -184,7 +182,7 @@ def _reset_static_achievement_counters(self): """ This resets the cached production flows that we track for achievements and diversity sampling. """ - with self.server.transaction() as t: + with self.client.transaction() as t: t.add_command( "/sc global.crafted_items = {}; global.harvested_items = {}", raw=True ) @@ -193,11 +191,11 @@ def _reset_elapsed_ticks(self): """ This resets the cached production flows that we track for achievements and diversity sampling. """ - with self.server.transaction() as t: + with self.client.transaction() as t: t.add_command("/sc global.elapsed_ticks = 0", raw=True) def _reset(self, inventories: List[Dict[str, Any]]): - with self.server.transaction() as t: + with self.client.transaction() as t: t.add_command( "/sc global.alerts = {}; game.reset_game_state(); global.actions.reset_production_stats(); global.actions.regenerate_resources(1)", raw=True, @@ -210,7 +208,7 @@ def _reset(self, inventories: List[Dict[str, Any]]): ) # self.server.add_command('clear_inventory', player_index) - with self.server.transaction() as t: + with self.client.transaction() as t: t.add_command("/sc global.actions.clear_walking_queue()", raw=True) for i in range(self.num_agents): player_index = i + 1 @@ -288,14 +286,14 @@ def reset(self, game_state: Optional[GameState] = None): self.initial_score = 0 # Clear renderings - with self.server.transaction() as t: + with self.client.transaction() as t: t.add_command("/sc rendering.clear()", raw=True) def set_inventory(self, inventory: Dict[str, Any], agent_idx: int = 0): - with self.server.transaction() as t: + with self.client.transaction() as t: t.add_command("clear_inventory", agent_idx + 1) - with self.server.transaction() as t: + with self.client.transaction() as t: inventory_items = {k: v for k, v in inventory.items()} inventory_items_json = json.dumps(inventory_items) player_idx = agent_idx + 1 @@ -305,7 +303,7 @@ def set_inventory(self, inventory: Dict[str, Any], agent_idx: int = 0): ) def initialise(self, fast=True): - with self.server.transaction() as t: + with self.client.transaction() as t: t.add_command("/sc global.alerts = {}", raw=True) t.add_command("/sc global.elapsed_ticks = 0", raw=True) t.add_command( @@ -330,7 +328,7 @@ def initialise(self, fast=True): if self.peaceful: init_scripts.append("enemies") - self.server.load_init_into_game(init_scripts) + self.client.load_init_into_game(init_scripts) inventories = [self.initial_inventory] * self.num_agents self._reset(inventories) @@ -339,7 +337,7 @@ def initialise(self, fast=True): def _create_agent_game_characters(self): """Create Factorio characters for all agents in the game.""" # Create characters in Factorio - with self.server.transaction() as t: + with self.client.transaction() as t: color_logic = "" if self.num_agents > 1: color_logic = "if i==1 then char.color={r=0,g=1,b=0,a=1} elseif i==2 then char.color={r=0,g=0,b=1,a=1} end;" @@ -357,7 +355,7 @@ def get_warnings(self, seconds=10): :return: """ start = timer() - lua_response = self.server.run_rcon_print(f"dump(global.get_alerts({seconds}))") + lua_response = self.client.run_rcon_print(f"dump(global.get_alerts({seconds}))") # print(lua_response) alert_dict, duration = _lua2python("alerts", lua_response, start=start) if isinstance(alert_dict, dict): @@ -489,4 +487,4 @@ def run_snippet_file_in_factorio_env(self, file_path, clean=True): self.cleanup() def cleanup(self): - self.server.cleanup() \ No newline at end of file + self.client.cleanup() \ No newline at end of file diff --git a/fle/env/game/namespace.py b/fle/env/game/namespace.py index bdb819973..a462e507f 100644 --- a/fle/env/game/namespace.py +++ b/fle/env/game/namespace.py @@ -12,11 +12,10 @@ from pydantic import BaseModel from fle.commons.models.serializable_function import SerializableFunction -from fle.env import entities as ent - +from fle.env.game.exceptions.hinting_name_error import get_value_type_str +from fle.env.game import entities as ent from fle.env.game.entities import Entity -from fle.env.exceptions.hinting_name_error import get_value_type_str -from fle.env.game_types import ( +from fle.env.game.game_types import ( Prototype, RecipeName, Resource, diff --git a/fle/env/game/tools/__init__.py b/fle/env/game/tools/__init__.py index f421299b8..2d536c5b7 100644 --- a/fle/env/game/tools/__init__.py +++ b/fle/env/game/tools/__init__.py @@ -12,9 +12,9 @@ - Init: Initialization tool for setting up the game environment """ -from fle.env.factorio.tools.controller import Controller -from fle.env.factorio.tools.init import Init -from fle.env.factorio.tools.tool import Tool +from fle.env.game.tools.controller import Controller +from fle.env.game.tools.init import Init +from fle.env.game.tools.tool import Tool # Public API - expose the main base classes __all__ = [ diff --git a/fle/env/game/tools/admin/clear_collision_boxes/client.py b/fle/env/game/tools/admin/clear_collision_boxes/client.py index ad183d70d..24c120ba3 100644 --- a/fle/env/game/tools/admin/clear_collision_boxes/client.py +++ b/fle/env/game/tools/admin/clear_collision_boxes/client.py @@ -1,4 +1,4 @@ -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class ClearCollisionBoxes(Tool): diff --git a/fle/env/game/tools/admin/clear_entities/client.py b/fle/env/game/tools/admin/clear_entities/client.py index 1edd9cf7b..abadbfc62 100644 --- a/fle/env/game/tools/admin/clear_entities/client.py +++ b/fle/env/game/tools/admin/clear_entities/client.py @@ -1,4 +1,4 @@ -from fle.env.factorio.tools.init import Init +from fle.env.game.tools.init import Init class ClearEntities(Init): diff --git a/fle/env/game/tools/admin/extend_collision_boxes/client.py b/fle/env/game/tools/admin/extend_collision_boxes/client.py index 08f3dc3ee..aa3ddbefe 100644 --- a/fle/env/game/tools/admin/extend_collision_boxes/client.py +++ b/fle/env/game/tools/admin/extend_collision_boxes/client.py @@ -1,5 +1,5 @@ from fle.env.game.entities import Position -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class ExtendCollisionBoxes(Tool): diff --git a/fle/env/game/tools/admin/get_factory_centroid/client.py b/fle/env/game/tools/admin/get_factory_centroid/client.py index e649e47b2..379f55baf 100644 --- a/fle/env/game/tools/admin/get_factory_centroid/client.py +++ b/fle/env/game/tools/admin/get_factory_centroid/client.py @@ -1,5 +1,5 @@ from fle.env.game.entities import BoundingBox, Position -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool from fle.env.utils.camera import Camera diff --git a/fle/env/game/tools/admin/get_path/client.py b/fle/env/game/tools/admin/get_path/client.py index e6c8a1cd5..73f3a3474 100644 --- a/fle/env/game/tools/admin/get_path/client.py +++ b/fle/env/game/tools/admin/get_path/client.py @@ -2,7 +2,7 @@ from typing import List from fle.env.game.entities import Position -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class GetPath(Tool): diff --git a/fle/env/game/tools/admin/get_production_stats/client.py b/fle/env/game/tools/admin/get_production_stats/client.py index 069085d71..2edc3864e 100644 --- a/fle/env/game/tools/admin/get_production_stats/client.py +++ b/fle/env/game/tools/admin/get_production_stats/client.py @@ -1,4 +1,4 @@ -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class GetProductionStats(Tool): diff --git a/fle/env/game/tools/admin/load_blueprint/client.py b/fle/env/game/tools/admin/load_blueprint/client.py index 69f010ab2..0aceb503e 100644 --- a/fle/env/game/tools/admin/load_blueprint/client.py +++ b/fle/env/game/tools/admin/load_blueprint/client.py @@ -1,5 +1,5 @@ from fle.env.game.entities import Position -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class LoadBlueprint(Tool): diff --git a/fle/env/game/tools/admin/load_entity_state/client.py b/fle/env/game/tools/admin/load_entity_state/client.py index 30a65ab7b..3efb0fef4 100644 --- a/fle/env/game/tools/admin/load_entity_state/client.py +++ b/fle/env/game/tools/admin/load_entity_state/client.py @@ -3,7 +3,7 @@ import zlib from typing import Union, List, Dict -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class LoadEntityState(Tool): diff --git a/fle/env/game/tools/admin/load_research_state/client.py b/fle/env/game/tools/admin/load_research_state/client.py index 261a8c069..45e843bb1 100644 --- a/fle/env/game/tools/admin/load_research_state/client.py +++ b/fle/env/game/tools/admin/load_research_state/client.py @@ -1,5 +1,5 @@ from fle.commons.models.research_state import ResearchState -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class LoadResearchState(Tool): diff --git a/fle/env/game/tools/admin/production_stats/client.py b/fle/env/game/tools/admin/production_stats/client.py index 59e222ca1..7ef18de8a 100644 --- a/fle/env/game/tools/admin/production_stats/client.py +++ b/fle/env/game/tools/admin/production_stats/client.py @@ -1,6 +1,6 @@ from typing import Dict -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class ProductionStats(Tool): diff --git a/fle/env/game/tools/admin/regenerate_resources/client.py b/fle/env/game/tools/admin/regenerate_resources/client.py index 21b9051b4..53067cd9c 100644 --- a/fle/env/game/tools/admin/regenerate_resources/client.py +++ b/fle/env/game/tools/admin/regenerate_resources/client.py @@ -1,4 +1,4 @@ -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class RegenerateResources(Tool): diff --git a/fle/env/game/tools/admin/render/client.py b/fle/env/game/tools/admin/render/client.py index 86ab6dcf3..0c25e0564 100644 --- a/fle/env/game/tools/admin/render/client.py +++ b/fle/env/game/tools/admin/render/client.py @@ -1,11 +1,11 @@ from typing import Optional, Dict import math -from fle.env import BoundingBox, Position, BeltGroup, PipeGroup, ElectricityGroup, Layer -from fle.env.factorio.tools.admin.render.rendered_image import RenderedImage -from fle.env.factorio.tools.admin.render.renderer import Renderer -from fle.env.factorio.tools.agent.get_entities.client import GetEntities -from fle.env.factorio.tools import Tool +from fle.env.game import BoundingBox, Position, BeltGroup, PipeGroup, ElectricityGroup, Layer +from fle.env.game.tools.admin.render.rendered_image import RenderedImage +from fle.env.game.tools.admin.render.renderer import Renderer +from fle.env.game.tools.agent.get_entities.client import GetEntities +from fle.env.game.tools import Tool MAX_TILES = ( 20 # Don't parameterise this, as the agent could break if it chooses a huge grid. diff --git a/fle/env/game/tools/admin/render/layers/connection_layers_renderer.py b/fle/env/game/tools/admin/render/layers/connection_layers_renderer.py index a799069e3..f2270fcdc 100644 --- a/fle/env/game/tools/admin/render/layers/connection_layers_renderer.py +++ b/fle/env/game/tools/admin/render/layers/connection_layers_renderer.py @@ -2,7 +2,7 @@ from PIL import ImageDraw from fle.env.game.entities import Entity, UndergroundBelt, Pipe -from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.game.tools.admin.render.layers.layer_renderer import LayerRenderer class ConnectionsLayerRenderer(LayerRenderer): diff --git a/fle/env/game/tools/admin/render/layers/entities_layer_renderer.py b/fle/env/game/tools/admin/render/layers/entities_layer_renderer.py index 5f03ad9b9..ff95d1069 100644 --- a/fle/env/game/tools/admin/render/layers/entities_layer_renderer.py +++ b/fle/env/game/tools/admin/render/layers/entities_layer_renderer.py @@ -2,7 +2,7 @@ from PIL import ImageDraw from fle.env.game.entities import Direction, EntityStatus -from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.game.tools.admin.render.layers.layer_renderer import LayerRenderer class EntitiesLayerRenderer(LayerRenderer): diff --git a/fle/env/game/tools/admin/render/layers/grid_layer_renderer.py b/fle/env/game/tools/admin/render/layers/grid_layer_renderer.py index d1b72bac3..b3603abab 100644 --- a/fle/env/game/tools/admin/render/layers/grid_layer_renderer.py +++ b/fle/env/game/tools/admin/render/layers/grid_layer_renderer.py @@ -1,7 +1,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.game.tools.admin.render.layers.layer_renderer import LayerRenderer class GridLayerRenderer(LayerRenderer): diff --git a/fle/env/game/tools/admin/render/layers/layer_renderer.py b/fle/env/game/tools/admin/render/layers/layer_renderer.py index 3850fe962..603ff35b6 100644 --- a/fle/env/game/tools/admin/render/layers/layer_renderer.py +++ b/fle/env/game/tools/admin/render/layers/layer_renderer.py @@ -2,7 +2,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig +from fle.env.game.tools.admin.render.utils.render_config import RenderConfig class LayerRenderer(ABC): diff --git a/fle/env/game/tools/admin/render/layers/marker_layers_renderer.py b/fle/env/game/tools/admin/render/layers/marker_layers_renderer.py index e35d6a0fa..94d8eb70f 100644 --- a/fle/env/game/tools/admin/render/layers/marker_layers_renderer.py +++ b/fle/env/game/tools/admin/render/layers/marker_layers_renderer.py @@ -2,7 +2,7 @@ from PIL import ImageDraw from fle.env.game.entities import Position, Layer -from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.game.tools.admin.render.layers.layer_renderer import LayerRenderer class MarkersLayerRenderer(LayerRenderer): diff --git a/fle/env/game/tools/admin/render/layers/natural_layer_renderer.py b/fle/env/game/tools/admin/render/layers/natural_layer_renderer.py index c98061056..6c7c8607b 100644 --- a/fle/env/game/tools/admin/render/layers/natural_layer_renderer.py +++ b/fle/env/game/tools/admin/render/layers/natural_layer_renderer.py @@ -4,7 +4,7 @@ from PIL import ImageDraw from fle.env.game.entities import Layer -from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.game.tools.admin.render.layers.layer_renderer import LayerRenderer class NaturalLayerRenderer(LayerRenderer): diff --git a/fle/env/game/tools/admin/render/layers/resource_layer_renderer.py b/fle/env/game/tools/admin/render/layers/resource_layer_renderer.py index 0ebedc712..1461fb357 100644 --- a/fle/env/game/tools/admin/render/layers/resource_layer_renderer.py +++ b/fle/env/game/tools/admin/render/layers/resource_layer_renderer.py @@ -2,7 +2,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.game.tools.admin.render.layers.layer_renderer import LayerRenderer class ResourcesLayerRenderer(LayerRenderer): diff --git a/fle/env/game/tools/admin/render/layers/water_layer_renderer.py b/fle/env/game/tools/admin/render/layers/water_layer_renderer.py index 9915d4d50..acc7e29cb 100644 --- a/fle/env/game/tools/admin/render/layers/water_layer_renderer.py +++ b/fle/env/game/tools/admin/render/layers/water_layer_renderer.py @@ -1,7 +1,7 @@ from typing import Dict, Callable from PIL import ImageDraw -from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.game.tools.admin.render.layers.layer_renderer import LayerRenderer class WaterLayerRenderer(LayerRenderer): diff --git a/fle/env/game/tools/admin/render/renderer.py b/fle/env/game/tools/admin/render/renderer.py index 1f43c119b..b247b8c19 100644 --- a/fle/env/game/tools/admin/render/renderer.py +++ b/fle/env/game/tools/admin/render/renderer.py @@ -2,34 +2,34 @@ from typing import List, Dict, Optional from fle.env.game.entities import Entity, Position, BoundingBox, Layer, EntityStatus -from fle.env.factorio.tools.admin.render.layers.connection_layers_renderer import ( +from fle.env.game.tools.admin.render.layers.connection_layers_renderer import ( ConnectionsLayerRenderer, ) -from fle.env.factorio.tools.admin.render.layers.marker_layers_renderer import ( +from fle.env.game.tools.admin.render.layers.marker_layers_renderer import ( MarkersLayerRenderer, ) -from fle.env.factorio.tools.admin.render.layers.resource_layer_renderer import ( +from fle.env.game.tools.admin.render.layers.resource_layer_renderer import ( ResourcesLayerRenderer, ) -from fle.env.factorio.tools.admin.render.utils.electricity_renderer import ( +from fle.env.game.tools.admin.render.utils.electricity_renderer import ( ElectricityLayerRenderer, ) -from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig -from fle.env.factorio.tools.admin.render.utils.entity_categoriser import EntityCategoriser -from fle.env.factorio.tools.admin.render.utils.colour_manager import ColourManager -from fle.env.factorio.tools.admin.render.utils.shape_renderer import ShapeRenderer -from fle.env.factorio.tools.admin.render.utils.legend_renderer import LegendRenderer -from fle.env.factorio.tools.admin.render.utils.connection_renderer import ConnectionRenderer -from fle.env.factorio.tools.admin.render.utils.image_calculator import ImageCalculator +from fle.env.game.tools.admin.render.utils.render_config import RenderConfig +from fle.env.game.tools.admin.render.utils.entity_categoriser import EntityCategoriser +from fle.env.game.tools.admin.render.utils.colour_manager import ColourManager +from fle.env.game.tools.admin.render.utils.shape_renderer import ShapeRenderer +from fle.env.game.tools.admin.render.utils.legend_renderer import LegendRenderer +from fle.env.game.tools.admin.render.utils.connection_renderer import ConnectionRenderer +from fle.env.game.tools.admin.render.utils.image_calculator import ImageCalculator # Import layer renderers -from fle.env.factorio.tools.admin.render.layers.grid_layer_renderer import GridLayerRenderer -from fle.env.factorio.tools.admin.render.layers.water_layer_renderer import WaterLayerRenderer -from fle.env.factorio.tools.admin.render.layers.natural_layer_renderer import ( +from fle.env.game.tools.admin.render.layers.grid_layer_renderer import GridLayerRenderer +from fle.env.game.tools.admin.render.layers.water_layer_renderer import WaterLayerRenderer +from fle.env.game.tools.admin.render.layers.natural_layer_renderer import ( NaturalLayerRenderer, ) -from fle.env.factorio.tools.admin.render.layers.entities_layer_renderer import ( +from fle.env.game.tools.admin.render.layers.entities_layer_renderer import ( EntitiesLayerRenderer, ) diff --git a/fle/env/game/tools/admin/render/utils/colour_manager.py b/fle/env/game/tools/admin/render/utils/colour_manager.py index d03601562..8ccf4a878 100644 --- a/fle/env/game/tools/admin/render/utils/colour_manager.py +++ b/fle/env/game/tools/admin/render/utils/colour_manager.py @@ -3,9 +3,9 @@ from collections import defaultdict from fle.env.game.entities import Entity, EntityStatus -from fle.env.game_types import prototype_by_name -from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig -from fle.env.factorio.tools.admin.render.utils.entity_categoriser import EntityCategoriser +from fle.env.game.game_types import prototype_by_name +from fle.env.game.tools.admin.render.utils.render_config import RenderConfig +from fle.env.game.tools.admin.render.utils.entity_categoriser import EntityCategoriser class ColourManager: diff --git a/fle/env/game/tools/admin/render/utils/connection_renderer.py b/fle/env/game/tools/admin/render/utils/connection_renderer.py index 468aef5bc..66b620eef 100644 --- a/fle/env/game/tools/admin/render/utils/connection_renderer.py +++ b/fle/env/game/tools/admin/render/utils/connection_renderer.py @@ -2,8 +2,8 @@ from typing import Tuple, Callable, List from PIL import ImageDraw -from fle.env import UndergroundBelt, Pipe -from fle.env.factorio.tools.admin.render.utils.colour_manager import ColourManager +from fle.env.game import UndergroundBelt, Pipe +from fle.env.game.tools.admin.render.utils.colour_manager import ColourManager class ConnectionRenderer: diff --git a/fle/env/game/tools/admin/render/utils/electricity_renderer.py b/fle/env/game/tools/admin/render/utils/electricity_renderer.py index 7607ec9cd..51688ffdd 100644 --- a/fle/env/game/tools/admin/render/utils/electricity_renderer.py +++ b/fle/env/game/tools/admin/render/utils/electricity_renderer.py @@ -3,7 +3,7 @@ from PIL import ImageDraw from fle.env.game.entities import Layer -from fle.env.factorio.tools.admin.render.layers.layer_renderer import LayerRenderer +from fle.env.game.tools.admin.render.layers.layer_renderer import LayerRenderer class ElectricityLayerRenderer(LayerRenderer): diff --git a/fle/env/game/tools/admin/render/utils/entity_categoriser.py b/fle/env/game/tools/admin/render/utils/entity_categoriser.py index ffaa33ca1..56dbcd5b1 100644 --- a/fle/env/game/tools/admin/render/utils/entity_categoriser.py +++ b/fle/env/game/tools/admin/render/utils/entity_categoriser.py @@ -1,4 +1,4 @@ -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype class EntityCategoriser: @@ -15,7 +15,7 @@ def get_entity_category(entity) -> str: Returns: String representing the entity category """ - from fle.env import ( + from fle.env.game import ( TransportBelt, Splitter, UndergroundBelt, diff --git a/fle/env/game/tools/admin/render/utils/image_calculator.py b/fle/env/game/tools/admin/render/utils/image_calculator.py index 97ba0ded8..08ca39491 100644 --- a/fle/env/game/tools/admin/render/utils/image_calculator.py +++ b/fle/env/game/tools/admin/render/utils/image_calculator.py @@ -1,7 +1,7 @@ from typing import List, Optional, Dict, Callable from fle.env.game.entities import Entity, Position, BoundingBox, Direction -from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig +from fle.env.game.tools.admin.render.utils.render_config import RenderConfig class ImageCalculator: diff --git a/fle/env/game/tools/admin/render/utils/legend_renderer.py b/fle/env/game/tools/admin/render/utils/legend_renderer.py index 3443fba55..cedc54b79 100644 --- a/fle/env/game/tools/admin/render/utils/legend_renderer.py +++ b/fle/env/game/tools/admin/render/utils/legend_renderer.py @@ -3,10 +3,10 @@ import math from fle.env.game.entities import EntityStatus -from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig -from fle.env.factorio.tools.admin.render.utils.colour_manager import ColourManager -from fle.env.factorio.tools.admin.render.utils.entity_categoriser import EntityCategoriser -from fle.env.factorio.tools.admin.render.utils.shape_renderer import ShapeRenderer +from fle.env.game.tools.admin.render.utils.render_config import RenderConfig +from fle.env.game.tools.admin.render.utils.colour_manager import ColourManager +from fle.env.game.tools.admin.render.utils.entity_categoriser import EntityCategoriser +from fle.env.game.tools.admin.render.utils.shape_renderer import ShapeRenderer class LegendRenderer: diff --git a/fle/env/game/tools/admin/render/utils/natural_renderer.py b/fle/env/game/tools/admin/render/utils/natural_renderer.py index ef9835c48..42e5b2df7 100644 --- a/fle/env/game/tools/admin/render/utils/natural_renderer.py +++ b/fle/env/game/tools/admin/render/utils/natural_renderer.py @@ -3,7 +3,7 @@ import math import random -from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig +from fle.env.game.tools.admin.render.utils.render_config import RenderConfig class NaturalRenderer: diff --git a/fle/env/game/tools/admin/render/utils/shape_renderer.py b/fle/env/game/tools/admin/render/utils/shape_renderer.py index 4b6d46744..8b5952fb3 100644 --- a/fle/env/game/tools/admin/render/utils/shape_renderer.py +++ b/fle/env/game/tools/admin/render/utils/shape_renderer.py @@ -3,7 +3,7 @@ from PIL import ImageDraw from fle.env.game.entities import Direction, EntityStatus -from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig +from fle.env.game.tools.admin.render.utils.render_config import RenderConfig class ShapeRenderer: diff --git a/fle/env/game/tools/admin/render/utils/tile_renderer.py b/fle/env/game/tools/admin/render/utils/tile_renderer.py index 5478ba6ac..bc6094492 100644 --- a/fle/env/game/tools/admin/render/utils/tile_renderer.py +++ b/fle/env/game/tools/admin/render/utils/tile_renderer.py @@ -2,7 +2,7 @@ from PIL import ImageDraw import math -from fle.env.factorio.tools.admin.render.utils.render_config import RenderConfig +from fle.env.game.tools.admin.render.utils.render_config import RenderConfig class TileRenderer: diff --git a/fle/env/game/tools/admin/render_message/client.py b/fle/env/game/tools/admin/render_message/client.py index 6aeb02f5a..504817dec 100644 --- a/fle/env/game/tools/admin/render_message/client.py +++ b/fle/env/game/tools/admin/render_message/client.py @@ -1,4 +1,4 @@ -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class RenderMessage(Tool): diff --git a/fle/env/game/tools/admin/request_path/client.py b/fle/env/game/tools/admin/request_path/client.py index 0fe28cbff..6bc4c3e6f 100644 --- a/fle/env/game/tools/admin/request_path/client.py +++ b/fle/env/game/tools/admin/request_path/client.py @@ -1,5 +1,5 @@ from fle.env.game.entities import Position -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class RequestPath(Tool): diff --git a/fle/env/game/tools/admin/save_blueprint/client.py b/fle/env/game/tools/admin/save_blueprint/client.py index a3c0a6bab..6dd1fb821 100644 --- a/fle/env/game/tools/admin/save_blueprint/client.py +++ b/fle/env/game/tools/admin/save_blueprint/client.py @@ -1,7 +1,7 @@ from typing import Tuple from fle.env.game.entities import Position -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class SaveBlueprint(Tool): diff --git a/fle/env/game/tools/admin/save_entity_state/client.py b/fle/env/game/tools/admin/save_entity_state/client.py index 405e8f716..b3f66bca3 100644 --- a/fle/env/game/tools/admin/save_entity_state/client.py +++ b/fle/env/game/tools/admin/save_entity_state/client.py @@ -3,7 +3,7 @@ import zlib from typing import Dict, List, Union -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class SaveEntityState(Tool): diff --git a/fle/env/game/tools/admin/save_research_state/client.py b/fle/env/game/tools/admin/save_research_state/client.py index 83b311244..49f369704 100644 --- a/fle/env/game/tools/admin/save_research_state/client.py +++ b/fle/env/game/tools/admin/save_research_state/client.py @@ -1,6 +1,6 @@ from fle.commons.models.research_state import ResearchState from fle.commons.models.technology_state import TechnologyState -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class SaveResearchState(Tool): diff --git a/fle/env/game/tools/agent/can_place_entity/client.py b/fle/env/game/tools/agent/can_place_entity/client.py index 1dee042a7..7f99860b6 100644 --- a/fle/env/game/tools/agent/can_place_entity/client.py +++ b/fle/env/game/tools/agent/can_place_entity/client.py @@ -1,6 +1,6 @@ -from fle.env import entities as ent -from fle.env.game_types import Prototype -from fle.env.factorio.tools import Tool +from fle.env.game import entities as ent +from fle.env.game.game_types import Prototype +from fle.env.game.tools import Tool class CanPlaceEntity(Tool): diff --git a/fle/env/game/tools/agent/connect_entities/client.py b/fle/env/game/tools/agent/connect_entities/client.py index 9e373302a..9b3b05033 100644 --- a/fle/env/game/tools/agent/connect_entities/client.py +++ b/fle/env/game/tools/agent/connect_entities/client.py @@ -3,7 +3,7 @@ import numpy -from fle.env import ( +from fle.env.game import ( EntityGroup, Entity, Position, @@ -19,33 +19,33 @@ OilRefinery, MultiFluidHandler, ) -from fle.env import DirectionInternal -from fle.env.game_types import Prototype -from fle.env.factorio.tools.admin.clear_collision_boxes.client import ClearCollisionBoxes -from fle.env.factorio.tools.admin.extend_collision_boxes.client import ExtendCollisionBoxes -from fle.env.factorio.tools.admin.get_path.client import GetPath -from fle.env.factorio.tools.admin.request_path.client import RequestPath -from fle.env.factorio.tools.agent.connect_entities.path_result import PathResult -from fle.env.factorio.tools.agent.connect_entities.resolver import ConnectionType, Resolver -from fle.env.factorio.tools.agent.connect_entities.resolvers.fluid_connection_resolver import ( +from fle.env.game import DirectionInternal +from fle.env.game.game_types import Prototype +from fle.env.game.tools.admin.clear_collision_boxes.client import ClearCollisionBoxes +from fle.env.game.tools.admin.extend_collision_boxes.client import ExtendCollisionBoxes +from fle.env.game.tools.admin.get_path.client import GetPath +from fle.env.game.tools.admin.request_path.client import RequestPath +from fle.env.game.tools.agent.connect_entities.path_result import PathResult +from fle.env.game.tools.agent.connect_entities.resolver import ConnectionType, Resolver +from fle.env.game.tools.agent.connect_entities.resolvers.fluid_connection_resolver import ( FluidConnectionResolver, ) -from fle.env.factorio.tools.agent.connect_entities.resolvers.power_connection_resolver import ( +from fle.env.game.tools.agent.connect_entities.resolvers.power_connection_resolver import ( PowerConnectionResolver, ) -from fle.env.factorio.tools.agent.connect_entities.resolvers.transport_connection_resolver import ( +from fle.env.game.tools.agent.connect_entities.resolvers.transport_connection_resolver import ( TransportConnectionResolver, ) -from fle.env.factorio.tools.agent.connect_entities.groupable_entities import ( +from fle.env.game.tools.agent.connect_entities.groupable_entities import ( _deduplicate_entities, agglomerate_groupable_entities, ) -from fle.env.factorio.tools.agent.get_entities.client import GetEntities -from fle.env.factorio.tools.agent.get_entity.client import GetEntity -from fle.env.factorio.tools.agent.inspect_inventory.client import InspectInventory -from fle.env.factorio.tools.agent.pickup_entity.client import PickupEntity -from fle.env.factorio.tools.agent.rotate_entity.client import RotateEntity -from fle.env.factorio.tools import Tool +from fle.env.game.tools.agent.get_entities.client import GetEntities +from fle.env.game.tools.agent.get_entity.client import GetEntity +from fle.env.game.tools.agent.inspect_inventory.client import InspectInventory +from fle.env.game.tools.agent.pickup_entity.client import PickupEntity +from fle.env.game.tools.agent.rotate_entity.client import RotateEntity +from fle.env.game.tools import Tool from collections.abc import Set as AbstractSet diff --git a/fle/env/game/tools/agent/connect_entities/groupable_entities.py b/fle/env/game/tools/agent/connect_entities/groupable_entities.py index 7e680abde..477b8dc76 100644 --- a/fle/env/game/tools/agent/connect_entities/groupable_entities.py +++ b/fle/env/game/tools/agent/connect_entities/groupable_entities.py @@ -2,7 +2,7 @@ from typing import List, Union from typing_extensions import cast -from fle.env import ( +from fle.env.game import ( TransportBelt, BeltGroup, Position, @@ -17,7 +17,7 @@ Direction, WallGroup, ) -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype def _deduplicate_entities(entities: List[Entity]) -> List[Entity]: diff --git a/fle/env/game/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py b/fle/env/game/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py index 1a0e44cee..bc9c57c29 100644 --- a/fle/env/game/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py +++ b/fle/env/game/tools/agent/connect_entities/resolvers/fluid_connection_resolver.py @@ -1,6 +1,6 @@ from typing import Union, Tuple, List, Optional -from fle.env import ( +from fle.env.game import ( FluidHandler, Position, Entity, @@ -13,8 +13,8 @@ PipeGroup, PumpJack, ) -from fle.env.game_types import Prototype, prototype_by_name -from fle.env.factorio.tools.agent.connect_entities.resolver import Resolver +from fle.env.game.game_types import Prototype, prototype_by_name +from fle.env.game.tools.agent.connect_entities.resolver import Resolver class FluidConnectionResolver(Resolver): diff --git a/fle/env/game/tools/agent/connect_entities/resolvers/power_connection_resolver.py b/fle/env/game/tools/agent/connect_entities/resolvers/power_connection_resolver.py index cee10babc..649f150c9 100644 --- a/fle/env/game/tools/agent/connect_entities/resolvers/power_connection_resolver.py +++ b/fle/env/game/tools/agent/connect_entities/resolvers/power_connection_resolver.py @@ -1,6 +1,6 @@ from typing import Union, Tuple, List, Set from fle.env.game.entities import Position, Entity, ElectricityGroup -from fle.env.factorio.tools.agent.connect_entities.resolver import Resolver +from fle.env.game.tools.agent.connect_entities.resolver import Resolver class PowerConnectionResolver(Resolver): diff --git a/fle/env/game/tools/agent/connect_entities/resolvers/transport_connection_resolver.py b/fle/env/game/tools/agent/connect_entities/resolvers/transport_connection_resolver.py index d4fec42df..e1954049d 100644 --- a/fle/env/game/tools/agent/connect_entities/resolvers/transport_connection_resolver.py +++ b/fle/env/game/tools/agent/connect_entities/resolvers/transport_connection_resolver.py @@ -18,7 +18,7 @@ Boiler, Generator, ) -from fle.env.factorio.tools.agent.connect_entities.resolver import Resolver +from fle.env.game.tools.agent.connect_entities.resolver import Resolver class TransportConnectionResolver(Resolver): diff --git a/fle/env/game/tools/agent/craft_item/client.py b/fle/env/game/tools/agent/craft_item/client.py index 44ff24e1c..3bf6f1cd0 100644 --- a/fle/env/game/tools/agent/craft_item/client.py +++ b/fle/env/game/tools/agent/craft_item/client.py @@ -1,8 +1,8 @@ from time import sleep -from fle.env.game_types import Prototype -from fle.env.factorio.tools.agent.inspect_inventory.client import InspectInventory -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Prototype +from fle.env.game.tools.agent.inspect_inventory.client import InspectInventory +from fle.env.game.tools import Tool class CraftItem(Tool): diff --git a/fle/env/game/tools/agent/extract_item/client.py b/fle/env/game/tools/agent/extract_item/client.py index 277635acb..3677b4c96 100644 --- a/fle/env/game/tools/agent/extract_item/client.py +++ b/fle/env/game/tools/agent/extract_item/client.py @@ -1,8 +1,8 @@ from typing import Union from fle.env.game.entities import Position, Entity -from fle.env.game_types import Prototype -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Prototype +from fle.env.game.tools import Tool class ExtractItem(Tool): diff --git a/fle/env/game/tools/agent/get_connection_amount/client.py b/fle/env/game/tools/agent/get_connection_amount/client.py index a1dc9730f..f14fb4972 100644 --- a/fle/env/game/tools/agent/get_connection_amount/client.py +++ b/fle/env/game/tools/agent/get_connection_amount/client.py @@ -1,9 +1,9 @@ from typing import Union from fle.env.game.entities import Entity, Position, EntityGroup -from fle.env.game_types import Prototype -from fle.env.factorio.tools.agent.connect_entities.client import ConnectEntities -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Prototype +from fle.env.game.tools.agent.connect_entities.client import ConnectEntities +from fle.env.game.tools import Tool class GetConnectionAmount(Tool): diff --git a/fle/env/game/tools/agent/get_entities/client.py b/fle/env/game/tools/agent/get_entities/client.py index 49c27328a..54279a76f 100644 --- a/fle/env/game/tools/agent/get_entities/client.py +++ b/fle/env/game/tools/agent/get_entities/client.py @@ -1,11 +1,11 @@ from time import sleep from typing import List, Set, Union from fle.env.game.entities import Position, Entity, EntityGroup -from fle.env.game_types import Prototype -from fle.env.factorio.tools.agent.connect_entities.groupable_entities import ( +from fle.env.game.game_types import Prototype +from fle.env.game.tools.agent.connect_entities.groupable_entities import ( agglomerate_groupable_entities, ) -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class GetEntities(Tool): diff --git a/fle/env/game/tools/agent/get_entity/client.py b/fle/env/game/tools/agent/get_entity/client.py index 2089b8116..10af3d3e6 100644 --- a/fle/env/game/tools/agent/get_entity/client.py +++ b/fle/env/game/tools/agent/get_entity/client.py @@ -2,9 +2,9 @@ from fle.env.game.entities import Position, Entity -from fle.env.game_types import Prototype -from fle.env.factorio.tools.agent.get_entities.client import GetEntities -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Prototype +from fle.env.game.tools.agent.get_entities.client import GetEntities +from fle.env.game.tools import Tool class GetEntity(Tool): diff --git a/fle/env/game/tools/agent/get_prototype_recipe/client.py b/fle/env/game/tools/agent/get_prototype_recipe/client.py index 5a0e5d4b5..b7da21f7f 100644 --- a/fle/env/game/tools/agent/get_prototype_recipe/client.py +++ b/fle/env/game/tools/agent/get_prototype_recipe/client.py @@ -1,8 +1,8 @@ from typing import Union -from fle.env import Recipe, Ingredient, Product -from fle.env.game_types import Prototype, RecipeName -from fle.env.factorio.tools import Tool +from fle.env.game import Recipe, Ingredient, Product +from fle.env.game.game_types import Prototype, RecipeName +from fle.env.game.tools import Tool class GetPrototypeRecipe(Tool): diff --git a/fle/env/game/tools/agent/get_research_progress/client.py b/fle/env/game/tools/agent/get_research_progress/client.py index 6ec6ddd4e..202301e62 100644 --- a/fle/env/game/tools/agent/get_research_progress/client.py +++ b/fle/env/game/tools/agent/get_research_progress/client.py @@ -1,7 +1,7 @@ from typing import Optional, List -from fle.env import Ingredient -from fle.env.game_types import Technology -from fle.env.factorio.tools import Tool +from fle.env.game import Ingredient +from fle.env.game.game_types import Technology +from fle.env.game.tools import Tool class GetResearchProgress(Tool): diff --git a/fle/env/game/tools/agent/get_resource_patch/client.py b/fle/env/game/tools/agent/get_resource_patch/client.py index 74177a1ab..d13af0c25 100644 --- a/fle/env/game/tools/agent/get_resource_patch/client.py +++ b/fle/env/game/tools/agent/get_resource_patch/client.py @@ -1,8 +1,8 @@ from typing import Optional from fle.env.game.entities import Position, ResourcePatch, BoundingBox -from fle.env.game_types import Resource -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Resource +from fle.env.game.tools import Tool class GetResourcePatch(Tool): diff --git a/fle/env/game/tools/agent/harvest_resource/client.py b/fle/env/game/tools/agent/harvest_resource/client.py index d7cd15c29..97741538f 100644 --- a/fle/env/game/tools/agent/harvest_resource/client.py +++ b/fle/env/game/tools/agent/harvest_resource/client.py @@ -1,11 +1,11 @@ from time import sleep from fle.env.game.entities import Position -from fle.env.game_types import Resource -from fle.env.factorio.tools.agent.get_entity.client import GetEntity -from fle.env.factorio.tools.agent.move_to.client import MoveTo -from fle.env.factorio.tools.agent.nearest.client import Nearest -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Resource +from fle.env.game.tools.agent.get_entity.client import GetEntity +from fle.env.game.tools.agent.move_to.client import MoveTo +from fle.env.game.tools.agent.nearest.client import Nearest +from fle.env.game.tools import Tool class HarvestResource(Tool): diff --git a/fle/env/game/tools/agent/insert_item/client.py b/fle/env/game/tools/agent/insert_item/client.py index b7c53cf36..452ec7f57 100644 --- a/fle/env/game/tools/agent/insert_item/client.py +++ b/fle/env/game/tools/agent/insert_item/client.py @@ -2,9 +2,9 @@ from typing import Union from fle.env.game.entities import Entity, EntityGroup, Position, BeltGroup, PipeGroup -from fle.env.game_types import Prototype -from fle.env.factorio.tools.agent.get_entities.client import GetEntities -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Prototype +from fle.env.game.tools.agent.get_entities.client import GetEntities +from fle.env.game.tools import Tool class InsertItem(Tool): diff --git a/fle/env/game/tools/agent/inspect_inventory/client.py b/fle/env/game/tools/agent/inspect_inventory/client.py index 5413e7709..75ed1e492 100644 --- a/fle/env/game/tools/agent/inspect_inventory/client.py +++ b/fle/env/game/tools/agent/inspect_inventory/client.py @@ -1,7 +1,7 @@ from typing import List, Union -from fle.env import Inventory, Entity, Position -from fle.env.factorio.tools import Tool +from fle.env.game import Inventory, Entity, Position +from fle.env.game.tools import Tool class InspectInventory(Tool): diff --git a/fle/env/game/tools/agent/launch_rocket/client.py b/fle/env/game/tools/agent/launch_rocket/client.py index a393ec65e..71bda3388 100644 --- a/fle/env/game/tools/agent/launch_rocket/client.py +++ b/fle/env/game/tools/agent/launch_rocket/client.py @@ -1,9 +1,9 @@ from typing import Union, cast from fle.env.game.entities import Position, RocketSilo -from fle.env.game_types import Prototype -from fle.env.factorio.tools.agent.get_entity.client import GetEntity -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Prototype +from fle.env.game.tools.agent.get_entity.client import GetEntity +from fle.env.game.tools import Tool class LaunchRocket(Tool): diff --git a/fle/env/game/tools/agent/move_to/client.py b/fle/env/game/tools/agent/move_to/client.py index fdf046532..80649e612 100644 --- a/fle/env/game/tools/agent/move_to/client.py +++ b/fle/env/game/tools/agent/move_to/client.py @@ -3,10 +3,10 @@ from fle.env.game.entities import Position from fle.env.game.instance import NONE -from fle.env.game_types import Prototype -from fle.env.factorio.tools.admin.get_path.client import GetPath -from fle.env.factorio.tools.admin.request_path.client import RequestPath -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Prototype +from fle.env.game.tools.admin.get_path.client import GetPath +from fle.env.game.tools.admin.request_path.client import RequestPath +from fle.env.game.tools import Tool from fle.env.lua_manager import LuaScriptManager diff --git a/fle/env/game/tools/agent/nearest/client.py b/fle/env/game/tools/agent/nearest/client.py index a3b7d17e2..89ccb43cd 100644 --- a/fle/env/game/tools/agent/nearest/client.py +++ b/fle/env/game/tools/agent/nearest/client.py @@ -1,8 +1,8 @@ from typing import Union from fle.env.game.entities import Position, ResourcePatch -from fle.env.game_types import Prototype, Resource -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Prototype, Resource +from fle.env.game.tools import Tool class Nearest(Tool): diff --git a/fle/env/game/tools/agent/nearest_buildable/client.py b/fle/env/game/tools/agent/nearest_buildable/client.py index 25198699f..229478454 100644 --- a/fle/env/game/tools/agent/nearest_buildable/client.py +++ b/fle/env/game/tools/agent/nearest_buildable/client.py @@ -1,6 +1,6 @@ -from fle.env import entities as ent -from fle.env.game_types import Prototype -from fle.env.factorio.tools import Tool +from fle.env.game import entities as ent +from fle.env.game.game_types import Prototype +from fle.env.game.tools import Tool class NearestBuildable(Tool): diff --git a/fle/env/game/tools/agent/pickup_entity/client.py b/fle/env/game/tools/agent/pickup_entity/client.py index b76b234b8..6ee26cc2b 100644 --- a/fle/env/game/tools/agent/pickup_entity/client.py +++ b/fle/env/game/tools/agent/pickup_entity/client.py @@ -1,8 +1,8 @@ from typing import Union, Optional -from fle.env import entities as ent -from fle.env.game_types import Prototype -from fle.env.factorio.tools import Tool +from fle.env.game import entities as ent +from fle.env.game.game_types import Prototype +from fle.env.game.tools import Tool class PickupEntity(Tool): diff --git a/fle/env/game/tools/agent/place_entity/client.py b/fle/env/game/tools/agent/place_entity/client.py index bdcfe6c95..94e606208 100644 --- a/fle/env/game/tools/agent/place_entity/client.py +++ b/fle/env/game/tools/agent/place_entity/client.py @@ -1,11 +1,11 @@ from time import sleep from fle.env.game.entities import Position, Entity -from fle.env import DirectionInternal, Direction -from fle.env.game_types import Prototype -from fle.env.factorio.tools.agent.get_entity.client import GetEntity -from fle.env.factorio.tools.agent.pickup_entity.client import PickupEntity -from fle.env.factorio.tools import Tool +from fle.env.game import DirectionInternal, Direction +from fle.env.game.game_types import Prototype +from fle.env.game.tools.agent.get_entity.client import GetEntity +from fle.env.game.tools.agent.pickup_entity.client import PickupEntity +from fle.env.game.tools import Tool class PlaceObject(Tool): diff --git a/fle/env/game/tools/agent/place_entity_next_to/client.py b/fle/env/game/tools/agent/place_entity_next_to/client.py index 5964908ea..f6a9b79fe 100644 --- a/fle/env/game/tools/agent/place_entity_next_to/client.py +++ b/fle/env/game/tools/agent/place_entity_next_to/client.py @@ -1,7 +1,7 @@ from fle.env.game.entities import Position, Entity -from fle.env import DirectionInternal -from fle.env.game_types import Prototype -from fle.env.factorio.tools import Tool +from fle.env.game import DirectionInternal +from fle.env.game.game_types import Prototype +from fle.env.game.tools import Tool class PlaceEntityNextTo(Tool): diff --git a/fle/env/game/tools/agent/print/client.py b/fle/env/game/tools/agent/print/client.py index 185e8b5c6..8608940d9 100644 --- a/fle/env/game/tools/agent/print/client.py +++ b/fle/env/game/tools/agent/print/client.py @@ -1,5 +1,5 @@ from fle.env.game.entities import Entity, Inventory, Position -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class Print(Tool): diff --git a/fle/env/game/tools/agent/rotate_entity/client.py b/fle/env/game/tools/agent/rotate_entity/client.py index ff34cfb30..4d2ff335e 100644 --- a/fle/env/game/tools/agent/rotate_entity/client.py +++ b/fle/env/game/tools/agent/rotate_entity/client.py @@ -3,9 +3,9 @@ Direction as DirectionA, AssemblingMachine, ) # We have 2 Direction objects to avoid circular deps -from fle.env import DirectionInternal -from fle.env.game_types import prototype_by_name -from fle.env.factorio.tools import Tool +from fle.env.game import DirectionInternal +from fle.env.game.game_types import prototype_by_name +from fle.env.game.tools import Tool class RotateEntity(Tool): diff --git a/fle/env/game/tools/agent/score/client.py b/fle/env/game/tools/agent/score/client.py index fce79ea25..7045632fc 100644 --- a/fle/env/game/tools/agent/score/client.py +++ b/fle/env/game/tools/agent/score/client.py @@ -1,4 +1,4 @@ -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class Reward(Tool): diff --git a/fle/env/game/tools/agent/send_message/client.py b/fle/env/game/tools/agent/send_message/client.py index 7c02aebae..b6840af05 100644 --- a/fle/env/game/tools/agent/send_message/client.py +++ b/fle/env/game/tools/agent/send_message/client.py @@ -1,7 +1,7 @@ from typing import Any, Dict, Optional import time -from fle.env.factorio.tools import Tool -from fle.env.factorio.tools.admin.render_message.client import RenderMessage +from fle.env.game.tools import Tool +from fle.env.game.tools.admin.render_message.client import RenderMessage import logging import uuid from a2a.types import Message, Part, TextPart diff --git a/fle/env/game/tools/agent/set_entity_recipe/client.py b/fle/env/game/tools/agent/set_entity_recipe/client.py index c14f06ac9..fda1d1ae7 100644 --- a/fle/env/game/tools/agent/set_entity_recipe/client.py +++ b/fle/env/game/tools/agent/set_entity_recipe/client.py @@ -1,8 +1,8 @@ from typing import Union from fle.env.game.entities import Entity -from fle.env.game_types import Prototype, RecipeName -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Prototype, RecipeName +from fle.env.game.tools import Tool class SetEntityRecipe(Tool): diff --git a/fle/env/game/tools/agent/set_research/client.py b/fle/env/game/tools/agent/set_research/client.py index 8ae2d61f9..72b48c9fb 100644 --- a/fle/env/game/tools/agent/set_research/client.py +++ b/fle/env/game/tools/agent/set_research/client.py @@ -1,7 +1,7 @@ from typing import List, Optional from pydantic import BaseModel -from fle.env.game_types import Technology -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import Technology +from fle.env.game.tools import Tool class Ingredient(BaseModel): diff --git a/fle/env/game/tools/agent/shift_entity/client.py b/fle/env/game/tools/agent/shift_entity/client.py index e57eb1adb..57ba2c140 100644 --- a/fle/env/game/tools/agent/shift_entity/client.py +++ b/fle/env/game/tools/agent/shift_entity/client.py @@ -1,13 +1,13 @@ from typing import Union from fle.env.game.entities import Entity, Direction -from fle.env.game_types import prototype_by_name -from fle.env import DirectionInternal as DirectionA -from fle.env.factorio.tools.agent.can_place_entity.client import CanPlaceEntity -from fle.env.factorio.tools.agent.connect_entities.client import ConnectEntities -from fle.env.factorio.tools.agent.pickup_entity.client import PickupEntity -from fle.env.factorio.tools.agent.place_entity.client import PlaceObject as PlaceEntity -from fle.env.factorio.tools import Tool +from fle.env.game.game_types import prototype_by_name +from fle.env.game import DirectionInternal as DirectionA +from fle.env.game.tools.agent.can_place_entity.client import CanPlaceEntity +from fle.env.game.tools.agent.connect_entities.client import ConnectEntities +from fle.env.game.tools.agent.pickup_entity.client import PickupEntity +from fle.env.game.tools.agent.place_entity.client import PlaceObject as PlaceEntity +from fle.env.game.tools import Tool class ShiftEntity(Tool): diff --git a/fle/env/game/tools/agent/sleep/client.py b/fle/env/game/tools/agent/sleep/client.py index 8d0c764a2..040fc1d20 100644 --- a/fle/env/game/tools/agent/sleep/client.py +++ b/fle/env/game/tools/agent/sleep/client.py @@ -1,6 +1,6 @@ from time import sleep -from fle.env.factorio.tools import Tool +from fle.env.game.tools import Tool class Sleep(Tool): diff --git a/fle/env/game/tools/init.py b/fle/env/game/tools/init.py index 7f270ca07..5f4fe890f 100644 --- a/fle/env/game/tools/init.py +++ b/fle/env/game/tools/init.py @@ -1,4 +1,4 @@ -from fle.env.factorio.tools.controller import Controller +from fle.env.game.tools.controller import Controller from fle.env.game.instance import FactorioInstance, FactorioClient diff --git a/fle/env/game/tools/tool.py b/fle/env/game/tools/tool.py index 39ccb0ab3..677aa6e75 100644 --- a/fle/env/game/tools/tool.py +++ b/fle/env/game/tools/tool.py @@ -3,7 +3,7 @@ from fle.env.game.entities import Position, Entity from fle.env.game.namespace import FactorioNamespace from fle.env.game.instance import FactorioClient -from fle.env.factorio.tools.controller import Controller +from fle.env.game.tools.controller import Controller class Tool(Controller): diff --git a/fle/env/game_session.py b/fle/env/game_session.py index df408f9fb..3ca79279d 100644 --- a/fle/env/game_session.py +++ b/fle/env/game_session.py @@ -13,55 +13,3 @@ class FactorioGameSession: def __init__(self, instance: FactorioInstance, game_state: GameState): self.instance = instance self.game_state = game_state - - async def create_factorio_instance( - instance_id: int, num_agents: int = 1, agent_cards: Optional[List[AgentCard]] = None - ) -> FactorioInstance: - """Create and asynchronously initialize a single Factorio instance""" - ips, udp_ports, tcp_ports = get_local_container_ips() - - common_kwargs = { - "address": ips[instance_id], - "tcp_port": tcp_ports[instance_id], - "bounding_box": 200, - "fast": True, - "cache_scripts": True, - "inventory": {}, - "all_technologies_researched": True, - "num_agents": num_agents, - } - - if num_agents > 1: - instance = await A2AFactorioInstance.create( - **common_kwargs, agent_cards=agent_cards - ) - else: - instance = FactorioInstance(**common_kwargs) - - instance.set_speed(10) - return instance - - -async def run_trajectory(process_id: int, config: EvalConfig): - """Entry point for running a single trajectory""" - db_client = await create_db_client() - instance = await create_factorio_instance( - process_id, len(config.agents), config.agent_cards - ) - evaluator = SimpleFactorioEvaluator( - db_client=db_client, instance=instance, value_accrual_time=1, error_penalty=0 - ) - task = config.task - task.setup(instance) - - runner = TrajectoryRunner(config.agents, db_client, evaluator, config, process_id) - - await runner.run() - await db_client.cleanup() - -async def get_next_version() -> int: - """Get next available version number""" - db_client = await create_db_client() - version = await db_client.get_largest_version() - await db_client.cleanup() - return version + 1 diff --git a/fle/env/protocols/mcp/repository.py b/fle/env/protocols/mcp/repository.py index e65157bd0..06e7e178e 100644 --- a/fle/env/protocols/mcp/repository.py +++ b/fle/env/protocols/mcp/repository.py @@ -5,7 +5,7 @@ from dulwich.objects import Blob, Tree, Commit from dulwich.repo import Repo -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.game.game_state import GameState diff --git a/fle/env/protocols/mcp/state.py b/fle/env/protocols/mcp/state.py index da331d96a..ea18fe910 100644 --- a/fle/env/protocols/mcp/state.py +++ b/fle/env/protocols/mcp/state.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import Dict, List, Optional, Any -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.commons.cluster_ips import get_local_container_ips from fle.commons.models import FactorioServer, Recipe, ResourcePatch diff --git a/fle/env/registry.py b/fle/env/registry.py index d99d4c445..d17283bd6 100644 --- a/fle/env/registry.py +++ b/fle/env/registry.py @@ -5,7 +5,7 @@ import gym from fle.commons.cluster_ips import get_local_container_ips -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.environment import FactorioGymEnv from fle.env.tasks import TaskFactory diff --git a/fle/env/tasks/default_task.py b/fle/env/tasks/default_task.py index 7299a0791..837964008 100644 --- a/fle/env/tasks/default_task.py +++ b/fle/env/tasks/default_task.py @@ -1,5 +1,5 @@ from typing import Any, Dict, List, Optional -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.tasks import TaskABC from fle.agents import TaskResponse diff --git a/fle/env/tasks/task_abc.py b/fle/env/tasks/task_abc.py index d6a144fc4..59ed1bdfe 100644 --- a/fle/env/tasks/task_abc.py +++ b/fle/env/tasks/task_abc.py @@ -1,6 +1,6 @@ from typing import Dict, List, Optional -from fle.env import Inventory -from fle.env import FactorioInstance +from fle.env.game import Inventory +from fle.env.game import FactorioInstance from fle.agents import TaskResponse from fle.env.game.game_state import GameState diff --git a/fle/env/tasks/throughput_task.py b/fle/env/tasks/throughput_task.py index 51b05c890..4e5c0878d 100644 --- a/fle/env/tasks/throughput_task.py +++ b/fle/env/tasks/throughput_task.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Optional -from fle.env import Entity -from fle.env import FactorioInstance +from fle.env.game import Entity +from fle.env.game import FactorioInstance from fle.env.tasks import TaskABC from fle.env.utils.achievements import eval_program_with_achievements from fle.agents import TaskResponse diff --git a/fle/env/tasks/unbounded_throughput_task.py b/fle/env/tasks/unbounded_throughput_task.py index a2068abfb..b18f1d99d 100644 --- a/fle/env/tasks/unbounded_throughput_task.py +++ b/fle/env/tasks/unbounded_throughput_task.py @@ -1,6 +1,6 @@ from typing import Any, Dict, List, Optional -from fle.env import Entity -from fle.env import FactorioInstance +from fle.env.game import Entity +from fle.env.game import FactorioInstance from fle.env.tasks import TaskABC from fle.env.tasks import LAB_PLAY_POPULATED_STARTING_INVENTORY, CRAFTING_STATISTICS from fle.env.utils.achievements import eval_program_with_achievements diff --git a/fle/eval/algorithms/beam/beam_search.py b/fle/eval/algorithms/beam/beam_search.py index d538f8d54..25e814ae8 100644 --- a/fle/eval/algorithms/beam/beam_search.py +++ b/fle/eval/algorithms/beam/beam_search.py @@ -18,7 +18,7 @@ from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.eval.evaluator import Evaluator from ..mcts import MCTS, GroupedFactorioLogger, InstanceGroup diff --git a/fle/eval/algorithms/beam/beam_search_milestones.py b/fle/eval/algorithms/beam/beam_search_milestones.py index cf093cea4..2186058f2 100644 --- a/fle/eval/algorithms/beam/beam_search_milestones.py +++ b/fle/eval/algorithms/beam/beam_search_milestones.py @@ -19,7 +19,7 @@ from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.tasks import ThroughputTask from ..mcts import get_mining_setup diff --git a/fle/eval/algorithms/beam/run_milestones.py b/fle/eval/algorithms/beam/run_milestones.py index 557c9acef..5fda0031d 100644 --- a/fle/eval/algorithms/beam/run_milestones.py +++ b/fle/eval/algorithms/beam/run_milestones.py @@ -12,7 +12,7 @@ from fle.agents.formatters import RecursiveReportFormatter from fle.services.db_client import DBClient -from fle.env import FactorioInstance, GameState +from fle.env.game import FactorioInstance, GameState from fle.eval.algorithms.beam import MilestonesBeamSearchExecutor from fle.eval.algorithms.mcts import SupervisedExecutorConfig from fle.env.tasks.throughput_task import ( diff --git a/fle/eval/algorithms/beam/run_parallel.py b/fle/eval/algorithms/beam/run_parallel.py index 5e09c67d6..cfd97acb6 100644 --- a/fle/eval/algorithms/beam/run_parallel.py +++ b/fle/eval/algorithms/beam/run_parallel.py @@ -14,7 +14,7 @@ from fle.commons.cluster_ips import get_local_container_ips from fle.services.db_client import create_db_client from fle.env.game.game_state import GameState -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.eval.algorithms.beam import ParallelBeamConfig, ParallelBeamSearch from fle.eval.algorithms.beam.run import MANUAL, OBSERVATION_SPACE, SYSTEM_PROMPT diff --git a/fle/eval/algorithms/independent/simple_evaluator.py b/fle/eval/algorithms/independent/simple_evaluator.py index 91fc78ab3..c530a70c9 100644 --- a/fle/eval/algorithms/independent/simple_evaluator.py +++ b/fle/eval/algorithms/independent/simple_evaluator.py @@ -7,7 +7,7 @@ from fle.env.game.game_state import GameState from fle.commons.models.program import Program from fle.env.game.entities import Entity, EntityGroup -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.utils.profits import get_achievements diff --git a/fle/eval/algorithms/independent/trajectory_runner.py b/fle/eval/algorithms/independent/trajectory_runner.py index 3981dc435..90fcf5540 100644 --- a/fle/eval/algorithms/independent/trajectory_runner.py +++ b/fle/eval/algorithms/independent/trajectory_runner.py @@ -15,7 +15,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message from fle.commons.models.program import Program -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.commons.cluster_ips import get_local_container_ips from fle.agents.llm.metrics import timing_tracker, log_metrics diff --git a/fle/eval/algorithms/mcts/__main__chunked.py b/fle/eval/algorithms/mcts/__main__chunked.py index 97083df06..1e04d5d71 100644 --- a/fle/eval/algorithms/mcts/__main__chunked.py +++ b/fle/eval/algorithms/mcts/__main__chunked.py @@ -27,7 +27,7 @@ from fle.env.game.game_state import GameState from fle.commons.models.message import Message from fle.commons.models.program import Program -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance # Configure environment os.environ.update({"FORCE_COLOR": "1", "TERM": "xterm-256color"}) diff --git a/fle/eval/algorithms/mcts/__main__objective_mcts.py b/fle/eval/algorithms/mcts/__main__objective_mcts.py index 891c4a6dd..822903fbf 100644 --- a/fle/eval/algorithms/mcts/__main__objective_mcts.py +++ b/fle/eval/algorithms/mcts/__main__objective_mcts.py @@ -20,7 +20,7 @@ from fle.env.game.game_state import GameState from fle.commons.models.message import Message from fle.commons.models.program import Program -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.eval.algorithms.mcts.samplers import ( BlueprintScenarioSampler, KLDiversityAchievementSampler, diff --git a/fle/eval/algorithms/mcts/blueprints_to_programs.py b/fle/eval/algorithms/mcts/blueprints_to_programs.py index e205f7bfd..d374c7aeb 100644 --- a/fle/eval/algorithms/mcts/blueprints_to_programs.py +++ b/fle/eval/algorithms/mcts/blueprints_to_programs.py @@ -10,7 +10,7 @@ from fle.commons.models.message import Message from fle.services.db_client import DBClient from fle.commons.models.program import Program -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance load_dotenv() diff --git a/fle/eval/algorithms/mcts/main.py b/fle/eval/algorithms/mcts/main.py index c7bc6fc5d..4c877871e 100644 --- a/fle/eval/algorithms/mcts/main.py +++ b/fle/eval/algorithms/mcts/main.py @@ -5,7 +5,7 @@ from fle.services.db_client import DBClient from fle.eval.algorithms.mcts import MCTSFactory from fle.eval.open.plots.run_results import RunResults -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance import concurrent.futures from typing import List, Tuple diff --git a/fle/eval/algorithms/mcts/parallel_mcts.py b/fle/eval/algorithms/mcts/parallel_mcts.py index 9c566acc9..05938bdfc 100644 --- a/fle/eval/algorithms/mcts/parallel_mcts.py +++ b/fle/eval/algorithms/mcts/parallel_mcts.py @@ -10,7 +10,7 @@ from rich.console import Console from fle.services.db_client import DBClient -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance # Configure logging logging.basicConfig(level=logging.INFO) diff --git a/fle/eval/algorithms/mcts/parallel_planning_mcts.py b/fle/eval/algorithms/mcts/parallel_planning_mcts.py index 8b5c95f14..af05ab1cf 100644 --- a/fle/eval/algorithms/mcts/parallel_planning_mcts.py +++ b/fle/eval/algorithms/mcts/parallel_planning_mcts.py @@ -19,7 +19,7 @@ from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.eval.algorithms.mcts.grouped_logger import GroupedFactorioLogger from fle.eval.algorithms.mcts.parallel_mcts_config import ParallelMCTSConfig from fle.eval.algorithms.mcts.planning_models import ( diff --git a/fle/eval/algorithms/mcts/plan_sampler.py b/fle/eval/algorithms/mcts/plan_sampler.py index 7d459b512..ce3933ca6 100644 --- a/fle/eval/algorithms/mcts/plan_sampler.py +++ b/fle/eval/algorithms/mcts/plan_sampler.py @@ -4,7 +4,7 @@ from fle.agents.llm.api_factory import APIFactory from fle.env.game.game_state import GameState -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance class PlanSampler: diff --git a/fle/eval/algorithms/mcts/samplers/blueprint_scenario_sampler.py b/fle/eval/algorithms/mcts/samplers/blueprint_scenario_sampler.py index cff89c7b6..cc6b8a6d2 100644 --- a/fle/eval/algorithms/mcts/samplers/blueprint_scenario_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/blueprint_scenario_sampler.py @@ -7,7 +7,7 @@ from fle.commons.models.message import Message from fle.env.game.game_state import GameState from fle.commons.models.program import Program -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance class BlueprintScenarioSampler: diff --git a/fle/eval/algorithms/mcts/supervised_task_executor_abc.py b/fle/eval/algorithms/mcts/supervised_task_executor_abc.py index c009d1157..8a68be6e6 100644 --- a/fle/eval/algorithms/mcts/supervised_task_executor_abc.py +++ b/fle/eval/algorithms/mcts/supervised_task_executor_abc.py @@ -24,7 +24,7 @@ ) from fle.env.game.game_state import GameState from fle.commons.models.program import Program -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.tasks import TaskABC from abc import ABC, abstractmethod diff --git a/fle/eval/entrypoints/beam_run.py b/fle/eval/entrypoints/beam_run.py index 9e27d8d0e..eebb846cb 100644 --- a/fle/eval/entrypoints/beam_run.py +++ b/fle/eval/entrypoints/beam_run.py @@ -11,7 +11,7 @@ from fle.agents.llm.api_factory import APIFactory from fle.services.db_client import DBClient from fle.commons.models import GameState -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from .beam_search import ParallelBeamConfig, ParallelBeamSearch diff --git a/fle/eval/entrypoints/mcts_planning_agent.py b/fle/eval/entrypoints/mcts_planning_agent.py index 87c1bc68e..24577d9e2 100644 --- a/fle/eval/entrypoints/mcts_planning_agent.py +++ b/fle/eval/entrypoints/mcts_planning_agent.py @@ -28,7 +28,7 @@ from fle.services.db_client import DBClient from fle.env.game.game_state import GameState -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance load_dotenv() diff --git a/fle/services/remote/factorio_server_login.py b/fle/services/remote/factorio_server_login.py index 961f42350..c45b473be 100644 --- a/fle/services/remote/factorio_server_login.py +++ b/fle/services/remote/factorio_server_login.py @@ -9,7 +9,7 @@ from dotenv import load_dotenv from fle.services.remote.cluster_ips import get_public_ips -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance """ This script is used to connect the client to each Factorio server running on an ECS cluster, diff --git a/tests/actions/test_can_place.py b/tests/actions/test_can_place.py index 7d4797181..001e0bd49 100644 --- a/tests/actions/test_can_place.py +++ b/tests/actions/test_can_place.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_connect_entities_dry_run.py b/tests/actions/test_connect_entities_dry_run.py index dc5010bdb..34ee2893b 100644 --- a/tests/actions/test_connect_entities_dry_run.py +++ b/tests/actions/test_connect_entities_dry_run.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/actions/test_craft.py b/tests/actions/test_craft.py index cecdd5862..e2ff3d921 100644 --- a/tests/actions/test_craft.py +++ b/tests/actions/test_craft.py @@ -1,6 +1,6 @@ import pytest -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_extract_item.py b/tests/actions/test_extract_item.py index ca4e4b0e4..bb62a4fb3 100644 --- a/tests/actions/test_extract_item.py +++ b/tests/actions/test_extract_item.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/actions/test_get_entities.py b/tests/actions/test_get_entities.py index 100c5275a..e798d7cd3 100644 --- a/tests/actions/test_get_entities.py +++ b/tests/actions/test_get_entities.py @@ -1,7 +1,7 @@ import pytest -from fle.env import entities as ent -from fle.env.game_types import Prototype, Resource +from fle.env.game import entities as ent +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_get_entity.py b/tests/actions/test_get_entity.py index 50219f54f..7c65d7b06 100644 --- a/tests/actions/test_get_entity.py +++ b/tests/actions/test_get_entity.py @@ -1,8 +1,8 @@ import pytest -from fle.env import DirectionInternal +from fle.env.game import DirectionInternal from fle.env.game.entities import Furnace, Position -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_get_prototype_recipe.py b/tests/actions/test_get_prototype_recipe.py index 70b3464ed..384ca645c 100644 --- a/tests/actions/test_get_prototype_recipe.py +++ b/tests/actions/test_get_prototype_recipe.py @@ -1,6 +1,6 @@ import pytest -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/actions/test_get_research_progress.py b/tests/actions/test_get_research_progress.py index 7934b8b3c..86acaef06 100644 --- a/tests/actions/test_get_research_progress.py +++ b/tests/actions/test_get_research_progress.py @@ -1,7 +1,7 @@ import pytest -from fle.env import FactorioInstance -from fle.env.game_types import Technology +from fle.env.game import FactorioInstance +from fle.env.game.game_types import Technology from fle.commons.cluster_ips import get_local_container_ips diff --git a/tests/actions/test_get_resource_patch.py b/tests/actions/test_get_resource_patch.py index 0c0b97dbe..fea776d39 100644 --- a/tests/actions/test_get_resource_patch.py +++ b/tests/actions/test_get_resource_patch.py @@ -1,8 +1,8 @@ import pytest -from fle.env import ResourcePatch -from fle.env import FactorioInstance -from fle.env.game_types import Resource +from fle.env.game import ResourcePatch +from fle.env.game import FactorioInstance +from fle.env.game.game_types import Resource @pytest.fixture() diff --git a/tests/actions/test_harvest_resource.py b/tests/actions/test_harvest_resource.py index 76429520f..4c76c1584 100644 --- a/tests/actions/test_harvest_resource.py +++ b/tests/actions/test_harvest_resource.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position -from fle.env.game_types import Resource, Prototype +from fle.env.game.game_types import Resource, Prototype @pytest.fixture() diff --git a/tests/actions/test_insert_item.py b/tests/actions/test_insert_item.py index d6ee18c33..d266df49a 100644 --- a/tests/actions/test_insert_item.py +++ b/tests/actions/test_insert_item.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, EntityStatus, Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_inspect_inventory.py b/tests/actions/test_inspect_inventory.py index fb9b370d1..348fd71e6 100644 --- a/tests/actions/test_inspect_inventory.py +++ b/tests/actions/test_inspect_inventory.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/actions/test_move_to.py b/tests/actions/test_move_to.py index 957bae2af..563186197 100644 --- a/tests/actions/test_move_to.py +++ b/tests/actions/test_move_to.py @@ -1,8 +1,8 @@ import pytest from fle.env.game.entities import Position -from fle.env import FactorioInstance -from fle.env.game_types import Prototype, Resource +from fle.env.game import FactorioInstance +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_nearest.py b/tests/actions/test_nearest.py index 66162cb85..4f00c109d 100644 --- a/tests/actions/test_nearest.py +++ b/tests/actions/test_nearest.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position -from fle.env.game_types import Resource +from fle.env.game.game_types import Resource @pytest.fixture() diff --git a/tests/actions/test_nearest_buildable.py b/tests/actions/test_nearest_buildable.py index 3678e4fd9..fc75e15e0 100644 --- a/tests/actions/test_nearest_buildable.py +++ b/tests/actions/test_nearest_buildable.py @@ -1,9 +1,9 @@ import pytest from fle.commons.cluster_ips import get_local_container_ips -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource from fle.env.game.entities import Position, BuildingBox, Direction -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance # @pytest.fixture() # def game(instance): diff --git a/tests/actions/test_pickup_entity.py b/tests/actions/test_pickup_entity.py index ff5f8ddcd..3d3b40d77 100644 --- a/tests/actions/test_pickup_entity.py +++ b/tests/actions/test_pickup_entity.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_place.py b/tests/actions/test_place.py index 5c02021ef..6a8f493be 100644 --- a/tests/actions/test_place.py +++ b/tests/actions/test_place.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_place_entity_next_to.py b/tests/actions/test_place_entity_next_to.py index 51ccb041b..5d0ce181d 100644 --- a/tests/actions/test_place_entity_next_to.py +++ b/tests/actions/test_place_entity_next_to.py @@ -1,8 +1,8 @@ import pytest from fle.env.game.entities import Position, Direction -from fle.env import DirectionInternal -from fle.env.game_types import Prototype, Resource +from fle.env.game import DirectionInternal +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/actions/test_place_next_to_and_rotate.py b/tests/actions/test_place_next_to_and_rotate.py index 6185f61ba..dee081300 100644 --- a/tests/actions/test_place_next_to_and_rotate.py +++ b/tests/actions/test_place_next_to_and_rotate.py @@ -1,8 +1,8 @@ import pytest from fle.env.game.entities import Position, Direction -from fle.env.game_types import Prototype, Resource -from fle.env import DirectionInternal +from fle.env.game.game_types import Prototype, Resource +from fle.env.game import DirectionInternal @pytest.fixture() diff --git a/tests/actions/test_render.py b/tests/actions/test_render.py index e78b535ca..ab40f3e93 100644 --- a/tests/actions/test_render.py +++ b/tests/actions/test_render.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, Layer -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/actions/test_rotate.py b/tests/actions/test_rotate.py index 1d433f36f..aea6dab02 100644 --- a/tests/actions/test_rotate.py +++ b/tests/actions/test_rotate.py @@ -1,6 +1,6 @@ import pytest -from fle.env.game_types import Prototype, RecipeName +from fle.env.game.game_types import Prototype, RecipeName from fle.env.game.entities import Position, Direction diff --git a/tests/actions/test_set_entity_recipe.py b/tests/actions/test_set_entity_recipe.py index e697d502a..422895b3a 100644 --- a/tests/actions/test_set_entity_recipe.py +++ b/tests/actions/test_set_entity_recipe.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/actions/test_set_research.py b/tests/actions/test_set_research.py index b529168ab..f455417b4 100644 --- a/tests/actions/test_set_research.py +++ b/tests/actions/test_set_research.py @@ -1,7 +1,7 @@ import pytest -from fle.env import FactorioInstance -from fle.env.game_types import Technology +from fle.env.game import FactorioInstance +from fle.env.game.game_types import Technology from fle.commons.cluster_ips import get_local_container_ips diff --git a/tests/benchmarks/benchmark_api.py b/tests/benchmarks/benchmark_api.py index 6107f6add..3c5244a79 100644 --- a/tests/benchmarks/benchmark_api.py +++ b/tests/benchmarks/benchmark_api.py @@ -1,5 +1,5 @@ import time -from fle.env import FactorioInstance, Direction +from fle.env.game import FactorioInstance, Direction from entities import Position from game_types import Prototype, Resource diff --git a/tests/benchmarks/benchmark_interpreter.py b/tests/benchmarks/benchmark_interpreter.py index 2db95e2bd..174f75168 100644 --- a/tests/benchmarks/benchmark_interpreter.py +++ b/tests/benchmarks/benchmark_interpreter.py @@ -1,6 +1,6 @@ import time -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance def run_string_benchmark(game: FactorioInstance, num_iterations: int = 100): diff --git a/tests/benchmarks/test_demo_video.py b/tests/benchmarks/test_demo_video.py index 2de34abb2..1cc2b76bc 100644 --- a/tests/benchmarks/test_demo_video.py +++ b/tests/benchmarks/test_demo_video.py @@ -5,7 +5,7 @@ # from fle.env.game.entities import Position # from fle.env import FactorioInstance -# from fle.env.game_types import Prototype +# from fle.env.game.game_types import Prototype # from fle.env.utils.profits import eval_program_with_profits # @pytest.fixture() diff --git a/tests/benchmarks/test_elapsed_ticks.py b/tests/benchmarks/test_elapsed_ticks.py index 204e34cfc..a264923ff 100644 --- a/tests/benchmarks/test_elapsed_ticks.py +++ b/tests/benchmarks/test_elapsed_ticks.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/benchmarks/test_script_caching.py b/tests/benchmarks/test_script_caching.py index 8ece02f4d..ccf028035 100644 --- a/tests/benchmarks/test_script_caching.py +++ b/tests/benchmarks/test_script_caching.py @@ -5,7 +5,7 @@ import io from pstats import SortKey -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance INVENTORY = { "coal": 50, diff --git a/tests/blueprints/test_blueprint_based_policies.py b/tests/blueprints/test_blueprint_based_policies.py index 1a221713b..b55c1af81 100644 --- a/tests/blueprints/test_blueprint_based_policies.py +++ b/tests/blueprints/test_blueprint_based_policies.py @@ -1,8 +1,8 @@ import pytest from fle.env.game.entities import Position, BoundingBox # , Direction -from fle.env import DirectionInternal as Direction -from fle.env.game_types import Prototype +from fle.env.game import DirectionInternal as Direction +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/blueprints/test_save_load.py b/tests/blueprints/test_save_load.py index 4981787bb..db03c247b 100644 --- a/tests/blueprints/test_save_load.py +++ b/tests/blueprints/test_save_load.py @@ -7,7 +7,7 @@ import pytest from fle.env.game.entities import Position, Direction -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/complex/test_edge_cases.py b/tests/complex/test_edge_cases.py index d1001666a..1667c0cc8 100644 --- a/tests/complex/test_edge_cases.py +++ b/tests/complex/test_edge_cases.py @@ -1,7 +1,7 @@ import pytest from time import sleep from fle.env.game.entities import Position, ResourcePatch, Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/complex/test_slow.py b/tests/complex/test_slow.py index 773be9a4f..2b12d8193 100644 --- a/tests/complex/test_slow.py +++ b/tests/complex/test_slow.py @@ -1,6 +1,6 @@ import pytest from fle.env.game.instance import FactorioInstance -from fle.env.game_types import Resource +from fle.env.game.game_types import Resource @pytest.fixture() diff --git a/tests/conftest.py b/tests/conftest.py index 9e4c6315c..1b58e2284 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,7 +5,7 @@ import pytest from fle.commons.cluster_ips import get_local_container_ips -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance # Add the src directory to the Python path src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) diff --git a/tests/connect/test_connect_pipes.py b/tests/connect/test_connect_pipes.py index 0e1719688..a906f58c3 100644 --- a/tests/connect/test_connect_pipes.py +++ b/tests/connect/test_connect_pipes.py @@ -12,7 +12,7 @@ Position, ResourcePatch, ) -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/connect/test_connect_poles.py b/tests/connect/test_connect_poles.py index 616686b56..eb2ecda48 100644 --- a/tests/connect/test_connect_poles.py +++ b/tests/connect/test_connect_poles.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, EntityStatus, BuildingBox, Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/connect/test_connect_transport_belts.py b/tests/connect/test_connect_transport_belts.py index 7c2b187e5..3d03b6031 100644 --- a/tests/connect/test_connect_transport_belts.py +++ b/tests/connect/test_connect_transport_belts.py @@ -3,7 +3,7 @@ import pytest from fle.env.game.entities import Position, ResourcePatch, BeltGroup, Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/connect/test_connect_underground_belts.py b/tests/connect/test_connect_underground_belts.py index 52b9e119f..2b198b8d4 100644 --- a/tests/connect/test_connect_underground_belts.py +++ b/tests/connect/test_connect_underground_belts.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, UndergroundBelt -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/connect/test_connect_underground_pipes.py b/tests/connect/test_connect_underground_pipes.py index e5cbff4df..a4b2c77e7 100644 --- a/tests/connect/test_connect_underground_pipes.py +++ b/tests/connect/test_connect_underground_pipes.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, UndergroundBelt -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/connect/test_connect_walls.py b/tests/connect/test_connect_walls.py index 4cb6db761..a9ac8517d 100644 --- a/tests/connect/test_connect_walls.py +++ b/tests/connect/test_connect_walls.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/connect/test_fluid_handlers.py b/tests/connect/test_fluid_handlers.py index eba3eaa44..7b94fe720 100644 --- a/tests/connect/test_fluid_handlers.py +++ b/tests/connect/test_fluid_handlers.py @@ -1,8 +1,8 @@ import pytest from fle.env.game.entities import Position, BuildingBox -from fle.env import DirectionInternal as Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game import DirectionInternal as Direction +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/entities/test_assemblers.py b/tests/entities/test_assemblers.py index 6657c5b30..c9a14cc57 100644 --- a/tests/entities/test_assemblers.py +++ b/tests/entities/test_assemblers.py @@ -1,7 +1,7 @@ import pytest -from fle.env import Direction, Position -from fle.env.game_types import Prototype, Resource +from fle.env.game import Direction, Position +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/entities/test_drill.py b/tests/entities/test_drill.py index faf424856..3e8793c9a 100644 --- a/tests/entities/test_drill.py +++ b/tests/entities/test_drill.py @@ -1,6 +1,6 @@ import pytest -from fle.env import Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game import Direction +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/entities/test_fluid_processors.py b/tests/entities/test_fluid_processors.py index a2eaa3b1f..f00746822 100644 --- a/tests/entities/test_fluid_processors.py +++ b/tests/entities/test_fluid_processors.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, Direction, Entity -from fle.env.game_types import Prototype, RecipeName, prototype_by_name, Resource +from fle.env.game.game_types import Prototype, RecipeName, prototype_by_name, Resource @pytest.fixture() diff --git a/tests/entities/test_inserters.py b/tests/entities/test_inserters.py index b46aff7d6..174e3f155 100644 --- a/tests/entities/test_inserters.py +++ b/tests/entities/test_inserters.py @@ -1,6 +1,6 @@ import pytest -from fle.env import Direction, Position -from fle.env.game_types import Prototype +from fle.env.game import Direction, Position +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/entities/test_nuclear_reactor.py b/tests/entities/test_nuclear_reactor.py index 2f01e3551..69d7db9ce 100644 --- a/tests/entities/test_nuclear_reactor.py +++ b/tests/entities/test_nuclear_reactor.py @@ -1,7 +1,7 @@ import pytest -from fle.env import Direction, EntityStatus, Position -from fle.env.game_types import Prototype, Resource, RecipeName +from fle.env.game import Direction, EntityStatus, Position +from fle.env.game.game_types import Prototype, Resource, RecipeName @pytest.fixture() diff --git a/tests/entities/test_power_generators.py b/tests/entities/test_power_generators.py index f59b47277..a7a3d31e0 100644 --- a/tests/entities/test_power_generators.py +++ b/tests/entities/test_power_generators.py @@ -1,7 +1,7 @@ import pytest -from fle.env import Direction, EntityStatus -from fle.env.game_types import Prototype +from fle.env.game import Direction, EntityStatus +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/entities/test_rockets.py b/tests/entities/test_rockets.py index cf4117114..a06b75456 100644 --- a/tests/entities/test_rockets.py +++ b/tests/entities/test_rockets.py @@ -1,6 +1,6 @@ import json from fle.env.game.entities import Position, Direction, EntityStatus -from fle.env.game_types import Resource, Prototype +from fle.env.game.game_types import Resource, Prototype import pytest diff --git a/tests/eval/test_achievements.py b/tests/eval/test_achievements.py index 51629f5b5..ecf784570 100644 --- a/tests/eval/test_achievements.py +++ b/tests/eval/test_achievements.py @@ -1,6 +1,6 @@ import unittest -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.utils.achievements import eval_program_with_achievements diff --git a/tests/eval/test_game_state.py b/tests/eval/test_game_state.py index fd0c28738..b2100e93b 100644 --- a/tests/eval/test_game_state.py +++ b/tests/eval/test_game_state.py @@ -1,5 +1,5 @@ from pydantic import BaseModel -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.game.game_state import GameState diff --git a/tests/eval/test_production_divergence.py b/tests/eval/test_production_divergence.py index 573105333..9b162c8f1 100644 --- a/tests/eval/test_production_divergence.py +++ b/tests/eval/test_production_divergence.py @@ -1,6 +1,6 @@ import unittest -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.utils.profits import eval_program_with_achievements test_string = """ diff --git a/tests/eval/test_profits.py b/tests/eval/test_profits.py index 4326bbcc7..a6c8e1ba0 100644 --- a/tests/eval/test_profits.py +++ b/tests/eval/test_profits.py @@ -1,5 +1,5 @@ import math -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.env.utils.profits import eval_program_with_profits diff --git a/tests/eval/test_save_load_python_namespace.py b/tests/eval/test_save_load_python_namespace.py index 8d4e4caae..d23d75496 100644 --- a/tests/eval/test_save_load_python_namespace.py +++ b/tests/eval/test_save_load_python_namespace.py @@ -3,8 +3,8 @@ from fle.eval.algorithms.mcts.chunked_mcts import ChunkedMCTS from fle.env.game.game_state import GameState -from fle.env.game_types import Prototype -from fle.env import FactorioInstance +from fle.env.game.game_types import Prototype +from fle.env.game import FactorioInstance FULL_PROGRAM = """ # First, let's gather some basic resources to start building our factory diff --git a/tests/functional/test_auto_fueling_iron_smelting_factory.py b/tests/functional/test_auto_fueling_iron_smelting_factory.py index c9ff03353..7f5c955b2 100644 --- a/tests/functional/test_auto_fueling_iron_smelting_factory.py +++ b/tests/functional/test_auto_fueling_iron_smelting_factory.py @@ -2,8 +2,8 @@ from time import sleep from fle.env.game.entities import Position -from fle.env import DirectionInternal as Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game import DirectionInternal as Direction +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/functional/test_auto_refilling_coal.py b/tests/functional/test_auto_refilling_coal.py index 36cbb24ba..42c0114a2 100644 --- a/tests/functional/test_auto_refilling_coal.py +++ b/tests/functional/test_auto_refilling_coal.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/functional/test_chemical_plants.py b/tests/functional/test_chemical_plants.py index fe7a61110..1342a5fe2 100644 --- a/tests/functional/test_chemical_plants.py +++ b/tests/functional/test_chemical_plants.py @@ -1,8 +1,8 @@ import pytest from fle.env.game.entities import Position, Entity -from fle.env import DirectionInternal as Direction -from fle.env.game_types import Prototype, Resource, RecipeName +from fle.env.game import DirectionInternal as Direction +from fle.env.game.game_types import Prototype, Resource, RecipeName @pytest.fixture() diff --git a/tests/functional/test_defence.py b/tests/functional/test_defence.py index 338886a79..a3770c54f 100644 --- a/tests/functional/test_defence.py +++ b/tests/functional/test_defence.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, Direction -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/functional/test_electricity_unit.py b/tests/functional/test_electricity_unit.py index ed96053c1..b17b38e17 100644 --- a/tests/functional/test_electricity_unit.py +++ b/tests/functional/test_electricity_unit.py @@ -10,8 +10,8 @@ BurnerMiningDrill, EntityStatus, ) -from fle.env import DirectionInternal as Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game import DirectionInternal as Direction +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/functional/test_factory_unit.py b/tests/functional/test_factory_unit.py index d142982ae..2aaa76807 100644 --- a/tests/functional/test_factory_unit.py +++ b/tests/functional/test_factory_unit.py @@ -2,9 +2,9 @@ import pytest -from fle.env import BurnerMiningDrill, EntityStatus, Layer -from fle.env import FactorioInstance, Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game import BurnerMiningDrill, EntityStatus, Layer +from fle.env.game import FactorioInstance, Direction +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/functional/test_full_oil_chain.py b/tests/functional/test_full_oil_chain.py index 6c27009ba..41249dd71 100644 --- a/tests/functional/test_full_oil_chain.py +++ b/tests/functional/test_full_oil_chain.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, BuildingBox -from fle.env.game_types import Prototype, Resource, RecipeName +from fle.env.game.game_types import Prototype, Resource, RecipeName @pytest.fixture() diff --git a/tests/functional/test_multi_drill_multi_furnace.py b/tests/functional/test_multi_drill_multi_furnace.py index f84597009..ff3cd2672 100644 --- a/tests/functional/test_multi_drill_multi_furnace.py +++ b/tests/functional/test_multi_drill_multi_furnace.py @@ -1,7 +1,7 @@ import pytest -from fle.env import BuildingBox, Position, Direction -from fle.env.game_types import Resource, Prototype +from fle.env.game import BuildingBox, Position, Direction +from fle.env.game.game_types import Resource, Prototype @pytest.fixture() diff --git a/tests/functional/test_multiple_drills_multiple_furnaces.py b/tests/functional/test_multiple_drills_multiple_furnaces.py index c4bac7497..cf4f92f40 100644 --- a/tests/functional/test_multiple_drills_multiple_furnaces.py +++ b/tests/functional/test_multiple_drills_multiple_furnaces.py @@ -1,7 +1,7 @@ import pytest from fle.env.game.entities import Position, Direction, BuildingBox -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/functional/test_objectives.py b/tests/functional/test_objectives.py index 5ab206653..a5dbe934f 100644 --- a/tests/functional/test_objectives.py +++ b/tests/functional/test_objectives.py @@ -12,7 +12,7 @@ EntityGroup, Direction, ) -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/functional/test_research.py b/tests/functional/test_research.py index bb0d947a3..efb59035d 100644 --- a/tests/functional/test_research.py +++ b/tests/functional/test_research.py @@ -3,7 +3,7 @@ from fle.commons.cluster_ips import get_local_container_ips from fle.env.game.instance import FactorioInstance from fle.env.game.entities import Direction -from fle.env.game_types import Resource, Prototype, Technology +from fle.env.game.game_types import Resource, Prototype, Technology from fle.env.game.game_state import GameState diff --git a/tests/functional/test_small_iron_factory.py b/tests/functional/test_small_iron_factory.py index 208f546c8..9bf6a6d38 100644 --- a/tests/functional/test_small_iron_factory.py +++ b/tests/functional/test_small_iron_factory.py @@ -1,6 +1,6 @@ import pytest from fle.env.game.entities import Position, Direction -from fle.env.game_types import Prototype, Resource +from fle.env.game.game_types import Prototype, Resource @pytest.fixture() diff --git a/tests/gym_env/test_observation.py b/tests/gym_env/test_observation.py index 14bcabd61..ce47d82fd 100644 --- a/tests/gym_env/test_observation.py +++ b/tests/gym_env/test_observation.py @@ -3,7 +3,7 @@ # from fle.env.gym_env.validation import validate_observation from fle.env.game.entities import Position, Direction -from fle.env.game_types import Prototype +from fle.env.game.game_types import Prototype def test_reset_observation(instance): diff --git a/tests/status/test_pipes_status.py b/tests/status/test_pipes_status.py index 90fd31587..fa3b307cf 100644 --- a/tests/status/test_pipes_status.py +++ b/tests/status/test_pipes_status.py @@ -1,7 +1,7 @@ import pytest -from fle.env import Position, EntityStatus -from fle.env.game_types import Prototype +from fle.env.game import Position, EntityStatus +from fle.env.game.game_types import Prototype @pytest.fixture() diff --git a/tests/status/test_poles_status.py b/tests/status/test_poles_status.py index 9347c2bd0..6e9385009 100644 --- a/tests/status/test_poles_status.py +++ b/tests/status/test_poles_status.py @@ -1,7 +1,7 @@ import pytest -from fle.env import Direction, Position, EntityStatus -from fle.env.game_types import Resource, Prototype +from fle.env.game import Direction, Position, EntityStatus +from fle.env.game.game_types import Resource, Prototype @pytest.fixture() diff --git a/tests/status/test_pump_status.py b/tests/status/test_pump_status.py index 78969d602..f0fe23908 100644 --- a/tests/status/test_pump_status.py +++ b/tests/status/test_pump_status.py @@ -2,8 +2,8 @@ import pytest -from fle.env import Direction, EntityStatus -from fle.env.game_types import Resource, Prototype +from fle.env.game import Direction, EntityStatus +from fle.env.game.game_types import Resource, Prototype @pytest.fixture() diff --git a/tests/test_eval.py b/tests/test_eval.py index 149055d62..4633c1a5a 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -1,7 +1,7 @@ import unittest from fle.commons.cluster_ips import get_local_container_ips -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance embedded_function = """ def inspect_inventory_wrapper(): diff --git a/tests/test_inventory_deduction.py b/tests/test_inventory_deduction.py index b4b6434b8..74e18655d 100644 --- a/tests/test_inventory_deduction.py +++ b/tests/test_inventory_deduction.py @@ -1,6 +1,6 @@ import pytest -from fle.env import FactorioInstance -from fle.env.game_types import Prototype +from fle.env.game import FactorioInstance +from fle.env.game.game_types import Prototype from fle.env.game.entities import Position, Direction @pytest.fixture() diff --git a/tests/test_production_stats.py b/tests/test_production_stats.py index 218b9b2ad..fe1d60fd9 100644 --- a/tests/test_production_stats.py +++ b/tests/test_production_stats.py @@ -1,8 +1,8 @@ import unittest -from fle.env import FactorioInstance +from fle.env.game import FactorioInstance from fle.services.rcon import _lua2python -from fle.env.game_types import Resource +from fle.env.game.game_types import Resource class TestProductionStats(unittest.TestCase): diff --git a/tests/test_warnings.py b/tests/test_warnings.py index c5eea4362..b349ee8f0 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -1,8 +1,8 @@ import unittest from fle.commons.cluster_ips import get_local_container_ips -from fle.env.game_types import Prototype, Resource -from fle.env import FactorioInstance, Direction +from fle.env.game.game_types import Prototype, Resource +from fle.env.game import FactorioInstance, Direction from fle.env.game.game_state import GameState From 437116d05293aa075ae96e017db5a5b14a48095d Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 31 Jul 2025 18:17:07 +0530 Subject: [PATCH 36/53] added config + path changes --- examples/agents/backtracking_agent.py | 2 +- examples/agents/backtracking_system.py | 2 +- examples/agents/basic_agent.py | 2 +- examples/agents/visual_agent.py | 4 +- fle/agents/data/run_trace.py | 2 +- fle/commons/runner.py | 2 +- fle/env/game/config.py | 0 fle/env/game/factorio_client.py | 2 +- fle/env/game/instance.py | 2 +- fle/env/gym_env/run_eval.py | 2 +- fle/eval/algorithms/beam/beam_search.py | 2 +- .../algorithms/beam/beam_search_milestones.py | 2 +- fle/eval/algorithms/beam/run_milestones.py | 2 +- fle/eval/algorithms/beam/run_parallel.py | 2 +- .../independent/simple_evaluator.py | 2 +- .../independent/trajectory_runner.py | 2 +- fle/eval/algorithms/mcts/__main__chunked.py | 2 +- .../algorithms/mcts/__main__objective_mcts.py | 2 +- .../algorithms/mcts/blueprints_to_programs.py | 2 +- fle/eval/algorithms/mcts/evaluator.py | 2 +- fle/eval/algorithms/mcts/main.py | 2 +- fle/eval/algorithms/mcts/mcts.py | 2 +- fle/eval/algorithms/mcts/objective_mcts.py | 2 +- fle/eval/algorithms/mcts/parallel_mcts.py | 2 +- .../algorithms/mcts/parallel_planning_mcts.py | 2 +- fle/eval/algorithms/mcts/results.py | 2 +- .../algorithms/mcts/samplers/beam_sampler.py | 2 +- .../algorithms/mcts/samplers/db_sampler.py | 2 +- .../dynamic_reward_weighted_sampler.py | 2 +- .../mcts/samplers/kld_achievement_sampler.py | 2 +- .../mcts/supervised_task_executor_abc.py | 2 +- fle/eval/entrypoints/beam_run.py | 2 +- fle/eval/entrypoints/mcts_planning_agent.py | 2 +- fle/eval/open/plots/run_results.py | 2 +- fle/eval/open/plots/run_visualiser.py | 2 +- fle/eval/open/plots/simple_run_visualiser.py | 2 +- .../plots/simple_run_visualiser_with_ticks.py | 2 +- .../plots/visualise_production_figures.py | 2 +- fle/services/db/config.py | 0 fle/{commons => services/db}/db_client.py | 0 fle/services/db_client.py | 864 ------------------ fle/services/docker/config.py | 0 fle/services/{ => docker}/docker_manager.py | 0 fle/trajectory_runner.py | 2 +- tests/multiagent/test_messages.py | 2 +- 45 files changed, 40 insertions(+), 904 deletions(-) create mode 100644 fle/env/game/config.py create mode 100644 fle/services/db/config.py rename fle/{commons => services/db}/db_client.py (100%) delete mode 100644 fle/services/db_client.py create mode 100644 fle/services/docker/config.py rename fle/services/{ => docker}/docker_manager.py (100%) diff --git a/examples/agents/backtracking_agent.py b/examples/agents/backtracking_agent.py index c9b4440be..c2e420f21 100644 --- a/examples/agents/backtracking_agent.py +++ b/examples/agents/backtracking_agent.py @@ -8,7 +8,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message -from fle.env.namespace import FactorioNamespace +from fle.env.game.namespace import FactorioNamespace from fle.agents.models import CompletionResult, Response from fle.agents.llm.parsing import Policy diff --git a/examples/agents/backtracking_system.py b/examples/agents/backtracking_system.py index 7b0af6373..a25d60a6e 100644 --- a/examples/agents/backtracking_system.py +++ b/examples/agents/backtracking_system.py @@ -3,7 +3,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message -from fle.env.namespace import FactorioNamespace +from fle.env.game.namespace import FactorioNamespace from fle.agents.models import CompletionResult, Response from fle.agents.llm.parsing import Policy diff --git a/examples/agents/basic_agent.py b/examples/agents/basic_agent.py index bf5b40998..a5717f001 100644 --- a/examples/agents/basic_agent.py +++ b/examples/agents/basic_agent.py @@ -1,7 +1,7 @@ from typing import Optional import tenacity -from fle.env.namespace import FactorioNamespace +from fle.env.game.namespace import FactorioNamespace from tenacity import retry_if_exception_type, wait_exponential from fle.commons.models.conversation import Conversation diff --git a/examples/agents/visual_agent.py b/examples/agents/visual_agent.py index 31886d322..48b1e2360 100644 --- a/examples/agents/visual_agent.py +++ b/examples/agents/visual_agent.py @@ -10,8 +10,8 @@ from fle.agents.models import CompletionResult, Response from fle.commons.models.conversation import Conversation from fle.commons.models.generation_parameters import GenerationParameters -from fle.env import Layer, Position -from fle.env.namespace import FactorioNamespace +from fle.env.game import Layer, Position +from fle.env.game.namespace import FactorioNamespace from fle.agents.llm.api_factory import APIFactory from fle.agents.llm.parsing import parse_response diff --git a/fle/agents/data/run_trace.py b/fle/agents/data/run_trace.py index 13179c3e6..29798ad82 100644 --- a/fle/agents/data/run_trace.py +++ b/fle/agents/data/run_trace.py @@ -2,7 +2,7 @@ import os from dotenv import load_dotenv -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.env.game import FactorioInstance # Load environment variables diff --git a/fle/commons/runner.py b/fle/commons/runner.py index c9b376f1e..d4ffb0440 100644 --- a/fle/commons/runner.py +++ b/fle/commons/runner.py @@ -2,7 +2,7 @@ from typing import Any, Dict, List, Optional, Tuple, Generic, TypeVar import time from dataclasses import dataclass -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.program import Program from fle.env.game.game_state import GameState from fle.commons.models.conversation import Conversation diff --git a/fle/env/game/config.py b/fle/env/game/config.py new file mode 100644 index 000000000..e69de29bb diff --git a/fle/env/game/factorio_client.py b/fle/env/game/factorio_client.py index 96eb35584..58181ce12 100644 --- a/fle/env/game/factorio_client.py +++ b/fle/env/game/factorio_client.py @@ -9,7 +9,7 @@ from fle.env.game.lua_manager import LuaScriptManager, ToolHookRegistry from fle.env.game.namespace import FactorioNamespace -from fle.services.docker_manager import ServerSettings +from fle.services.docker.docker_manager import ServerSettings from fle.services.rcon import _lua2python diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index c0ee1484f..5145e4dd2 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -22,7 +22,7 @@ from fle.env.utils.controller_loader.system_prompt_generator import \ SystemPromptGenerator from fle.services.rcon import _lua2python -from fle.services.docker_manager import ServerSettings +from fle.services.docker.docker_manager import ServerSettings CHUNK_SIZE = 32 MAX_SAMPLES = 5000 diff --git a/fle/env/gym_env/run_eval.py b/fle/env/gym_env/run_eval.py index cc99d106a..29e4d6e6e 100644 --- a/fle/env/gym_env/run_eval.py +++ b/fle/env/gym_env/run_eval.py @@ -16,7 +16,7 @@ from fle.env.gym_env.trajectory_runner import GymTrajectoryRunner from fle.env.registry import get_environment_info, list_available_environments from fle.eval.algorithms.independent import get_next_version -from fle.services.db_client import create_db_client +from fle.services.db.db_client import create_db_client load_dotenv() diff --git a/fle/eval/algorithms/beam/beam_search.py b/fle/eval/algorithms/beam/beam_search.py index 25e814ae8..1a8360f4b 100644 --- a/fle/eval/algorithms/beam/beam_search.py +++ b/fle/eval/algorithms/beam/beam_search.py @@ -12,7 +12,7 @@ from rich.console import Console from fle.agents.formatters import ConversationFormatter, DefaultFormatter -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.conversation import Conversation from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters diff --git a/fle/eval/algorithms/beam/beam_search_milestones.py b/fle/eval/algorithms/beam/beam_search_milestones.py index 2186058f2..1528272f6 100644 --- a/fle/eval/algorithms/beam/beam_search_milestones.py +++ b/fle/eval/algorithms/beam/beam_search_milestones.py @@ -13,7 +13,7 @@ ) from tenacity import retry, wait_exponential -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.conversation import Conversation from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters diff --git a/fle/eval/algorithms/beam/run_milestones.py b/fle/eval/algorithms/beam/run_milestones.py index 5fda0031d..3957e8605 100644 --- a/fle/eval/algorithms/beam/run_milestones.py +++ b/fle/eval/algorithms/beam/run_milestones.py @@ -11,7 +11,7 @@ from dotenv import load_dotenv from fle.agents.formatters import RecursiveReportFormatter -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.env.game import FactorioInstance, GameState from fle.eval.algorithms.beam import MilestonesBeamSearchExecutor from fle.eval.algorithms.mcts import SupervisedExecutorConfig diff --git a/fle/eval/algorithms/beam/run_parallel.py b/fle/eval/algorithms/beam/run_parallel.py index cfd97acb6..4cb0198c5 100644 --- a/fle/eval/algorithms/beam/run_parallel.py +++ b/fle/eval/algorithms/beam/run_parallel.py @@ -12,7 +12,7 @@ from fle.agents.formatters import RecursiveReportFormatter from fle.agents.llm.api_factory import APIFactory from fle.commons.cluster_ips import get_local_container_ips -from fle.services.db_client import create_db_client +from fle.services.db.db_client import create_db_client from fle.env.game.game_state import GameState from fle.env.game import FactorioInstance from fle.eval.algorithms.beam import ParallelBeamConfig, ParallelBeamSearch diff --git a/fle/eval/algorithms/independent/simple_evaluator.py b/fle/eval/algorithms/independent/simple_evaluator.py index c530a70c9..701e53307 100644 --- a/fle/eval/algorithms/independent/simple_evaluator.py +++ b/fle/eval/algorithms/independent/simple_evaluator.py @@ -2,7 +2,7 @@ import copy from typing import List, Tuple, Union, Dict -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.achievements import ProductionFlows from fle.env.game.game_state import GameState from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/independent/trajectory_runner.py b/fle/eval/algorithms/independent/trajectory_runner.py index 90fcf5540..b80582d16 100644 --- a/fle/eval/algorithms/independent/trajectory_runner.py +++ b/fle/eval/algorithms/independent/trajectory_runner.py @@ -10,7 +10,7 @@ from fle.agents import CompletionResult, CompletionReason from fle.agents.agent_abc import AgentABC -from fle.services.db_client import DBClient, create_db_client +from fle.services.db.db_client import DBClient, create_db_client from fle.eval.algorithms.independent.simple_evaluator import SimpleFactorioEvaluator from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message diff --git a/fle/eval/algorithms/mcts/__main__chunked.py b/fle/eval/algorithms/mcts/__main__chunked.py index 1e04d5d71..a07fe5264 100644 --- a/fle/eval/algorithms/mcts/__main__chunked.py +++ b/fle/eval/algorithms/mcts/__main__chunked.py @@ -22,7 +22,7 @@ from fle.agents.llm.api_factory import APIFactory from fle.commons.cluster_ips import get_local_container_ips -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.conversation import Conversation from fle.env.game.game_state import GameState from fle.commons.models.message import Message diff --git a/fle/eval/algorithms/mcts/__main__objective_mcts.py b/fle/eval/algorithms/mcts/__main__objective_mcts.py index 822903fbf..701ee5f46 100644 --- a/fle/eval/algorithms/mcts/__main__objective_mcts.py +++ b/fle/eval/algorithms/mcts/__main__objective_mcts.py @@ -15,7 +15,7 @@ ) from fle.agents.llm.api_factory import APIFactory from fle.commons.cluster_ips import get_local_container_ips -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.conversation import Conversation from fle.env.game.game_state import GameState from fle.commons.models.message import Message diff --git a/fle/eval/algorithms/mcts/blueprints_to_programs.py b/fle/eval/algorithms/mcts/blueprints_to_programs.py index d374c7aeb..6ced02ed0 100644 --- a/fle/eval/algorithms/mcts/blueprints_to_programs.py +++ b/fle/eval/algorithms/mcts/blueprints_to_programs.py @@ -8,7 +8,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.program import Program from fle.env.game import FactorioInstance diff --git a/fle/eval/algorithms/mcts/evaluator.py b/fle/eval/algorithms/mcts/evaluator.py index 3ba587609..84f1675ee 100644 --- a/fle/eval/algorithms/mcts/evaluator.py +++ b/fle/eval/algorithms/mcts/evaluator.py @@ -4,7 +4,7 @@ from fle.env.utils.profits import get_achievements -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.env.game.game_state import GameState from fle.commons.models.program import Program from fle.env.game.entities import Entity, EntityGroup diff --git a/fle/eval/algorithms/mcts/main.py b/fle/eval/algorithms/mcts/main.py index 4c877871e..19bd44eb7 100644 --- a/fle/eval/algorithms/mcts/main.py +++ b/fle/eval/algorithms/mcts/main.py @@ -2,7 +2,7 @@ import asyncio from dotenv import load_dotenv from fle.commons.cluster_ips import get_local_container_ips -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.eval.algorithms.mcts import MCTSFactory from fle.eval.open.plots.run_results import RunResults from fle.env.game import FactorioInstance diff --git a/fle/eval/algorithms/mcts/mcts.py b/fle/eval/algorithms/mcts/mcts.py index 3fb827d22..845fbf39b 100644 --- a/fle/eval/algorithms/mcts/mcts.py +++ b/fle/eval/algorithms/mcts/mcts.py @@ -14,7 +14,7 @@ from fle.agents.llm.parsing import PythonParser from tenacity import retry, retry_if_exception_type, wait_exponential -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.conversation import Conversation from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters diff --git a/fle/eval/algorithms/mcts/objective_mcts.py b/fle/eval/algorithms/mcts/objective_mcts.py index cc421be5a..9a7304210 100644 --- a/fle/eval/algorithms/mcts/objective_mcts.py +++ b/fle/eval/algorithms/mcts/objective_mcts.py @@ -11,7 +11,7 @@ from fle.agents.llm.api_factory import APIFactory from tenacity import retry, retry_if_exception_type, wait_exponential -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.conversation import Conversation from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters diff --git a/fle/eval/algorithms/mcts/parallel_mcts.py b/fle/eval/algorithms/mcts/parallel_mcts.py index 05938bdfc..76b0789eb 100644 --- a/fle/eval/algorithms/mcts/parallel_mcts.py +++ b/fle/eval/algorithms/mcts/parallel_mcts.py @@ -9,7 +9,7 @@ from fle.eval.evaluator import Evaluator from rich.console import Console -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.env.game import FactorioInstance # Configure logging diff --git a/fle/eval/algorithms/mcts/parallel_planning_mcts.py b/fle/eval/algorithms/mcts/parallel_planning_mcts.py index af05ab1cf..357e52a77 100644 --- a/fle/eval/algorithms/mcts/parallel_planning_mcts.py +++ b/fle/eval/algorithms/mcts/parallel_planning_mcts.py @@ -13,7 +13,7 @@ ConversationFormatter, StructurePreservingFormatter, ) -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.conversation import Conversation from fle.env.game.game_state import GameState from fle.commons.models.generation_parameters import GenerationParameters diff --git a/fle/eval/algorithms/mcts/results.py b/fle/eval/algorithms/mcts/results.py index b6519b5f8..72f7d6be7 100644 --- a/fle/eval/algorithms/mcts/results.py +++ b/fle/eval/algorithms/mcts/results.py @@ -1,7 +1,7 @@ import os import asyncio from dotenv import load_dotenv -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from eval.open.plots.run_results import RunResults import questionary import neptune diff --git a/fle/eval/algorithms/mcts/samplers/beam_sampler.py b/fle/eval/algorithms/mcts/samplers/beam_sampler.py index fcc797a81..4d2cf7e2e 100644 --- a/fle/eval/algorithms/mcts/samplers/beam_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/beam_sampler.py @@ -6,7 +6,7 @@ from psycopg2.extras import DictCursor from tenacity import retry_if_exception_type, wait_exponential -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.eval.algorithms.mcts.samplers.db_sampler import DBSampler from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/mcts/samplers/db_sampler.py b/fle/eval/algorithms/mcts/samplers/db_sampler.py index ab523d627..48dc6ea20 100644 --- a/fle/eval/algorithms/mcts/samplers/db_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/db_sampler.py @@ -3,7 +3,7 @@ from psycopg2.extras import DictCursor -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.program import Program diff --git a/fle/eval/algorithms/mcts/samplers/dynamic_reward_weighted_sampler.py b/fle/eval/algorithms/mcts/samplers/dynamic_reward_weighted_sampler.py index 7cfed6200..100ec6814 100644 --- a/fle/eval/algorithms/mcts/samplers/dynamic_reward_weighted_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/dynamic_reward_weighted_sampler.py @@ -8,7 +8,7 @@ from psycopg2.extras import DictCursor from tenacity import retry_if_exception_type, wait_exponential -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.program import Program from .db_sampler import DBSampler diff --git a/fle/eval/algorithms/mcts/samplers/kld_achievement_sampler.py b/fle/eval/algorithms/mcts/samplers/kld_achievement_sampler.py index dcbba2d91..0ecac9989 100644 --- a/fle/eval/algorithms/mcts/samplers/kld_achievement_sampler.py +++ b/fle/eval/algorithms/mcts/samplers/kld_achievement_sampler.py @@ -8,7 +8,7 @@ from psycopg2.extras import DictCursor from tenacity import retry_if_exception_type, wait_exponential -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models.program import Program from .db_sampler import DBSampler diff --git a/fle/eval/algorithms/mcts/supervised_task_executor_abc.py b/fle/eval/algorithms/mcts/supervised_task_executor_abc.py index 8a68be6e6..de4d94c0e 100644 --- a/fle/eval/algorithms/mcts/supervised_task_executor_abc.py +++ b/fle/eval/algorithms/mcts/supervised_task_executor_abc.py @@ -11,7 +11,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.generation_parameters import GenerationParameters from fle.agents.formatters.conversation_formatter_abc import DefaultFormatter -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.eval.evaluator import Evaluator from fle.eval.algorithms.mcts.grouped_logger import GroupedFactorioLogger from fle.eval.algorithms.mcts.parallel_supervised_config import SupervisedExecutorConfig diff --git a/fle/eval/entrypoints/beam_run.py b/fle/eval/entrypoints/beam_run.py index eebb846cb..db18ecec8 100644 --- a/fle/eval/entrypoints/beam_run.py +++ b/fle/eval/entrypoints/beam_run.py @@ -9,7 +9,7 @@ from fle.agents.formatters import RecursiveFormatter from fle.agents.llm.api_factory import APIFactory -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.commons.models import GameState from fle.env.game import FactorioInstance diff --git a/fle/eval/entrypoints/mcts_planning_agent.py b/fle/eval/entrypoints/mcts_planning_agent.py index 24577d9e2..c67ad9a74 100644 --- a/fle/eval/entrypoints/mcts_planning_agent.py +++ b/fle/eval/entrypoints/mcts_planning_agent.py @@ -26,7 +26,7 @@ from dotenv import load_dotenv from rich import print -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from fle.env.game.game_state import GameState from fle.env.game import FactorioInstance diff --git a/fle/eval/open/plots/run_results.py b/fle/eval/open/plots/run_results.py index f7509ee5c..9f4332c13 100644 --- a/fle/eval/open/plots/run_results.py +++ b/fle/eval/open/plots/run_results.py @@ -8,7 +8,7 @@ import networkx as nx import numpy as np -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient @dataclass diff --git a/fle/eval/open/plots/run_visualiser.py b/fle/eval/open/plots/run_visualiser.py index b1e543c9a..cc2271546 100644 --- a/fle/eval/open/plots/run_visualiser.py +++ b/fle/eval/open/plots/run_visualiser.py @@ -13,7 +13,7 @@ from matplotlib.offsetbox import OffsetImage, AnnotationBbox from PIL import Image -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from eval.open.plots.run_results import RunResults load_dotenv() diff --git a/fle/eval/open/plots/simple_run_visualiser.py b/fle/eval/open/plots/simple_run_visualiser.py index 96446eab6..19bd75c3b 100644 --- a/fle/eval/open/plots/simple_run_visualiser.py +++ b/fle/eval/open/plots/simple_run_visualiser.py @@ -14,7 +14,7 @@ from matplotlib.offsetbox import OffsetImage, AnnotationBbox import os -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from eval.open.independent_runs.value_calculator import ValueCalculator load_dotenv() diff --git a/fle/eval/open/plots/simple_run_visualiser_with_ticks.py b/fle/eval/open/plots/simple_run_visualiser_with_ticks.py index 326022d3e..776dc555f 100644 --- a/fle/eval/open/plots/simple_run_visualiser_with_ticks.py +++ b/fle/eval/open/plots/simple_run_visualiser_with_ticks.py @@ -14,7 +14,7 @@ from matplotlib.offsetbox import OffsetImage, AnnotationBbox import os -from fle.services.db_client import PostgresDBClient +from fle.services.db.db_client import PostgresDBClient from eval.open.independent_runs.value_calculator import ValueCalculator load_dotenv() diff --git a/fle/eval/open/plots/visualise_production_figures.py b/fle/eval/open/plots/visualise_production_figures.py index 04595dec5..13ff6909d 100644 --- a/fle/eval/open/plots/visualise_production_figures.py +++ b/fle/eval/open/plots/visualise_production_figures.py @@ -15,7 +15,7 @@ from dotenv import load_dotenv from matplotlib import image as mpimg -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient from eval.open.independent_runs.value_calculator import ValueCalculator load_dotenv() diff --git a/fle/services/db/config.py b/fle/services/db/config.py new file mode 100644 index 000000000..e69de29bb diff --git a/fle/commons/db_client.py b/fle/services/db/db_client.py similarity index 100% rename from fle/commons/db_client.py rename to fle/services/db/db_client.py diff --git a/fle/services/db_client.py b/fle/services/db_client.py deleted file mode 100644 index a5e7c08c0..000000000 --- a/fle/services/db_client.py +++ /dev/null @@ -1,864 +0,0 @@ -import json -import logging -import math -import os -import random -import sqlite3 -import statistics -import threading -from abc import ABC -from contextlib import contextmanager -from pathlib import Path -from typing import Any, Dict, List, Optional - -import psycopg2 -import tenacity -from psycopg2.extras import DictCursor -from psycopg2.pool import ThreadedConnectionPool -from tenacity import retry_if_exception_type, wait_exponential, wait_random_exponential - -from fle.commons.models.conversation import Conversation -from fle.env.game.game_state import GameState -from fle.commons.models.program import Program - -# Configure logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - - -class DBClient(ABC): - def __init__( - self, - max_conversation_length: int = 20, - min_connections: int = 5, - max_connections: int = 20, - **db_config, - ): - self.max_conversation_length = max_conversation_length - # Don't store connection as instance variable - # Instead create connection pool - # self.pool = [] - # self.max_pool_size = 5 - self._pool = None - self.min_connections = min_connections - self.max_connections = max_connections - self._lock = threading.Lock() - self.db_config = db_config - - async def initialize(self): - """Initialize the connection pool""" - pass - - @contextmanager - def get_connection(self): - """Regular context manager for database connections""" - pass - - async def get_beam_heads(self, version: int, beam_width: int) -> List[Program]: - """Get the highest value programs across all depths for a given version.""" - try: - with self.get_connection() as conn: - with conn.cursor(cursor_factory=DictCursor) as cur: - # Use a CTE to get diverse set of programs - cur.execute( - """ - WITH ProgramsByDepth AS ( - SELECT DISTINCT ON (depth) * - FROM programs - WHERE version = %s - AND state_json IS NOT NULL - AND value IS NOT NULL - ORDER BY depth, value DESC - ) - SELECT * FROM ( - SELECT * FROM ProgramsByDepth - ORDER BY value DESC - LIMIT %s - ) as depth_diverse - UNION DISTINCT - SELECT * FROM ( - SELECT * FROM programs - WHERE version = %s - AND state_json IS NOT NULL - AND value IS NOT NULL - ORDER BY value DESC - LIMIT %s - ) as value_focused - ORDER BY value DESC - LIMIT %s - """, - (version, beam_width, version, beam_width * 2, beam_width), - ) - - results = cur.fetchall() - if not results: - logger.warning(f"No programs found for version {version}") - return [] - - programs = [Program.from_row(dict(row)) for row in results] - depths = [p.depth for p in programs] - logger.info( - f"Found {len(programs)} beam heads for version {version} - {depths}" - ) - return programs - except Exception as e: - logger.error(f"Error fetching beam heads: {e}", exc_info=True) - return [] - - async def version_exists(self, version: int) -> bool: - """Check if a version exists in the database""" - try: - with self.get_connection() as conn: - with conn.cursor() as cur: - cur.execute( - """ - SELECT EXISTS( - SELECT 1 FROM programs WHERE version = %s - ) - """, - (version,), - ) - return cur.fetchone()[0] - except Exception as e: - print(f"Error checking version existence: {e}") - return False - - async def get_version_metadata(self, version: int) -> dict: - """Get metadata for a specific version""" - try: - with self.get_connection() as conn: - with conn.cursor(cursor_factory=DictCursor) as cur: - cur.execute( - """ - SELECT DISTINCT version_description, model - FROM programs - WHERE version = %s - LIMIT 1 - """, - (version,), - ) - result = cur.fetchone() - return dict(result) if result else {} - except Exception as e: - print(f"Error fetching version metadata: {e}") - return {} - - @tenacity.retry( - retry=retry_if_exception_type( - (psycopg2.OperationalError, psycopg2.InterfaceError, psycopg2.DatabaseError) - ), - wait=wait_random_exponential(multiplier=1, min=4, max=10), - ) - async def create_program(self, program: Program) -> Program: - """Create a new program, now with connection management""" - try: - with self.get_connection() as conn: - with conn.cursor() as cur: - cur.execute( - """ - INSERT INTO programs (code, value, visits, parent_id, state_json, conversation_json, - completion_token_usage, prompt_token_usage, token_usage, response, - holdout_value, raw_reward, version, version_description, model, meta, - achievements_json, instance, depth, advantage, ticks, timing_metrics_json) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) - RETURNING id, created_at - """, - ( - program.code, - program.value, - 0, - program.parent_id, - program.state.to_raw() if program.state else None, - json.dumps(program.conversation.dict()), - program.completion_token_usage, - program.prompt_token_usage, - program.token_usage, - program.response, - program.holdout_value, - program.raw_reward, - program.version, - program.version_description, - program.model, - json.dumps(program.meta), - json.dumps(program.achievements), - program.instance, - program.depth / 2, - program.advantage, - program.ticks, - json.dumps(program.timing_metrics) - if program.timing_metrics - else None, - ), - ) - - id, created_at = cur.fetchone() - conn.commit() - program.id = id - program.created_at = created_at - return program - except Exception as e: - conn.rollback() - print(f"Error creating program: {e}") - raise e - - async def cleanup(self): - """Clean up database resources""" - if self._pool is not None: - with self._lock: - if self._pool is not None: - try: - self._pool.closeall() - except Exception as e: - logger.error(f"Error closing connection pool: {e}") - finally: - self._pool = None - - @tenacity.retry( - retry=retry_if_exception_type( - (psycopg2.OperationalError, psycopg2.InterfaceError) - ), - wait=wait_exponential(multiplier=1, min=4, max=10), - ) - async def get_all_program_rewards(self, version: int = None) -> List[float]: - """Get all program rewards with proper connection management""" - query = """ - SELECT value - FROM programs - WHERE value IS NOT NULL - """ - if version is not None: - query += " AND version = %s" - - params = (version,) if version is not None else () - - try: - with self.get_connection() as conn: - with conn.cursor() as cur: - cur.execute(query.strip(), params) - results = cur.fetchall() - return [row[0] for row in results] - except Exception as e: - print(f"Error fetching program rewards: {e}") - return [] - - @tenacity.retry( - retry=retry_if_exception_type( - (psycopg2.OperationalError, psycopg2.InterfaceError) - ), - wait=wait_exponential(multiplier=1, min=4, max=10), - ) - async def get_largest_version(self) -> int: - query = """ - SELECT MAX(version) - FROM programs - """ - - try: - with self.get_connection() as conn: - with conn.cursor() as cur: - cur.execute(query) - result = cur.fetchone() - return result[0] if result and result[0] is not None else 0 - except Exception as e: - print(f"Error fetching largest version: {e}") - return 0 - - async def get_largest_depth_in_version(self, version): - query = f""" - SELECT MAX(depth) - FROM programs - WHERE version = {version} - """ - - try: - with self.get_connection() as conn: - with conn.cursor() as cur: - cur.execute(query) - result = cur.fetchone() - return result[0] if result and result[0] is not None else 0 - except Exception as e: - print(f"Error fetching largest depth: {e}") - return 0 - - @tenacity.retry( - retry=retry_if_exception_type( - (psycopg2.OperationalError, psycopg2.InterfaceError) - ), - wait=wait_random_exponential(multiplier=1, min=4, max=10), - ) - async def sample_parent( - self, - version=1, - compression_strength: Optional[float] = None, - adaptive_period: int = 100, - ) -> Optional[Program]: - """ - Sample parent with proper connection management and adjusted reward scaling. - - Args: - version: The version of programs to sample from - compression_strength: Fixed compression strength between 0 and 1. - If None, uses adaptive compression. Higher values mean more exploitation. Lower means more exploration. - adaptive_period: Number of steps for a full sine wave cycle when using - adaptive compression. - """ - try: - with self.get_connection() as conn: - with conn.cursor(cursor_factory=DictCursor) as cur: - # First get the current step count for adaptive compression - if compression_strength is None: - cur.execute( - f"SELECT COUNT(*) as step_count FROM programs WHERE version = {version}" - ) - step_count = cur.fetchone()["step_count"] - # Calculate adaptive compression using sine wave - # sin goes from -1 to 1, so we transform to 0 to 1 - compression_strength = ( - math.sin(2 * math.pi * step_count / adaptive_period) + 1 - ) / 2 - - cur.execute( - """ - WITH recent AS ( - SELECT id, value, conversation_json - FROM programs - WHERE version = %s - AND advantage IS NOT NULL - -- AND jsonb_array_length(conversation_json->'messages') < %s - ORDER BY created_at DESC - LIMIT 300 - ) - SELECT id, advantage - FROM recent - """, - (version), - ) - - results = cur.fetchall() - if not results: - return None - - # Get statistics of the value distribution - values = [row["advantage"] for row in results] - mean_value = statistics.mean(values) - std_value = statistics.stdev(values) if len(values) > 1 else 1.0 - - # Apply reward transformation to handle power-law distribution - def transform_reward(value): - # Z-score normalization - z_score = ( - (value - mean_value) / std_value if std_value > 0 else 0 - ) - - # Compress extreme values using tanh with current compression strength - compressed = math.tanh(z_score * compression_strength) - - # Scale back to positive values and add small epsilon - return (compressed + 1.0) / 2.0 + 1e-6 - - # Log current compression state - print( - f"Using compression strength: {compression_strength:.3f} " - f"({'adaptive' if compression_strength is None else 'fixed'})" - ) - - # Calculate transformed weights - weights = [ - (row["id"], transform_reward(row["advantage"])) - for row in results - ] - - # Normalize weights - total_weight = sum(w[1] for w in weights) - if total_weight == 0: - sampled_id = random.choice([w[0] for w in weights]) - else: - normalized_weights = [ - (id, w / total_weight) for id, w in weights - ] - sampled_id = random.choices( - [id for id, _ in normalized_weights], - weights=[w for _, w in normalized_weights], - k=1, - )[0] - - # Fetch the selected program - cur.execute( - """ - SELECT * FROM programs WHERE id = %s - """, - (sampled_id,), - ) - - row = cur.fetchone() - return Program.from_row(dict(row)) if row else None - except Exception as e: - print(f"Error sampling parent: {e}") - raise e - - async def update_program(self, program_id: int, updates: Dict[str, Any]) -> Program: - """Update program with proper connection management""" - try: - with self.get_connection() as conn: - with conn.cursor() as cur: - # Handle timing_metrics separately since it needs to be JSON serialized - timing_metrics = updates.pop("timing_metrics", None) - if timing_metrics is not None: - updates["timing_metrics_json"] = json.dumps(timing_metrics) - - set_clauses = [f"{k} = %s" for k in updates.keys()] - values = list(updates.values()) - - cur.execute( - f""" - UPDATE programs - SET {", ".join(set_clauses)} - WHERE id = %s - RETURNING * - """, - values + [program_id], - ) - - conn.commit() - row = cur.fetchone() - return Program.from_row( - dict(zip([desc[0] for desc in cur.description], row)) - ) - except Exception as e: - print(f"Error updating program: {e}") - raise e - - async def get_resume_state( - self, resume_version, process_id, agent_idx=-1 - ) -> tuple[ - Optional[GameState], Optional[Conversation], Optional[int], Optional[int] - ]: - """Get the state to resume from""" - try: - # Get most recent successful program to resume from - query = """ - SELECT * FROM programs - WHERE version = %s - AND state_json IS NOT NULL - AND value IS NOT NULL - -- AND meta->>'process_id' = %s::text - AND instance = %s - ORDER BY created_at DESC - LIMIT 1 - """ - - with self.get_connection() as conn: - with conn.cursor() as cur: - cur.execute(query, (resume_version, process_id, agent_idx)) - results = cur.fetchall() - if not results: - print(f"No valid programs found for version {resume_version} ") - return None, None, None, None - - # Choose a program to resume from - program = Program.from_row( - dict(zip([desc[0] for desc in cur.description], results[0])) - ) - return program.state, program.conversation, program.id, program.depth - - except Exception as e: - print(f"Error getting resume state: {e}") - return None, None, None, None - - -class PostgresDBClient(DBClient): - def __init__( - self, - max_conversation_length: int = 20, - min_connections: int = 5, - max_connections: int = 20, - **db_config, - ): - super().__init__( - max_conversation_length, min_connections, max_connections, **db_config - ) - - async def initialize(self): - """Initialize the connection pool""" - if self._pool is None: - async with self._lock: - if self._pool is None: # Double check pattern - self._pool = ThreadedConnectionPool( - self.min_connections, self.max_connections, **self.db_config - ) - - def _ensure_pool(self): - """Ensure connection pool exists with proper locking""" - if self._pool is None: - with self._lock: - if self._pool is None: - self._pool = ThreadedConnectionPool( - self.min_connections, self.max_connections, **self.db_config - ) - - @contextmanager - def get_connection(self): - """Regular context manager for database connections""" - self._ensure_pool() - conn = None - try: - conn = self._pool.getconn() - yield conn - finally: - if conn: - try: - self._pool.putconn(conn) - except Exception as e: - print(f"Error returning connection to pool: {e}") - try: - self._pool.putconn(conn, close=True) - except: - pass - - -class SQLliteDBClient(DBClient): - def __init__( - self, - max_conversation_length: int = 20, - min_connections: int = 5, - max_connections: int = 20, - **db_config, - ): - super().__init__( - max_conversation_length, min_connections, max_connections, **db_config - ) - self.database_file = self.db_config.get("database_file") - - async def initialize(self): - """Initialize the connection pool""" - if self._pool is None: - async with self._lock: - if self._pool is None: # Double check pattern - self._pool = ThreadedConnectionPool( - self.min_connections, self.max_connections, **self.db_config - ) - - @contextmanager - def get_connection(self): - """Context manager for SQLite database connections""" - conn = None - try: - conn = sqlite3.connect(self.database_file) - yield conn - finally: - if conn: - conn.close() - - @tenacity.retry( - retry=retry_if_exception_type( - (psycopg2.OperationalError, psycopg2.InterfaceError) - ), - wait=wait_exponential(multiplier=1, min=4, max=10), - ) - async def get_largest_version(self) -> int: - query = """ - SELECT MAX(version) - FROM programs - """ - - try: - with self.get_connection() as conn: - cur = conn.cursor() - cur.execute(query) - result = cur.fetchone() - return result[0] if result and result[0] is not None else 0 - except Exception as e: - print(f"Error fetching largest version: {e}") - return 0 - - async def get_resume_state( - self, resume_version, process_id, agent_idx=-1 - ) -> tuple[ - Optional[GameState], Optional[Conversation], Optional[int], Optional[int] - ]: - """Get the state to resume from""" - try: - # Get most recent successful program to resume from - query = """ - SELECT * FROM programs - WHERE version = ? - AND state_json IS NOT NULL - AND value IS NOT NULL - AND json_extract(meta, '$.process_id') = ? - AND instance = ? - ORDER BY created_at DESC - LIMIT 1 - """ - - with self.get_connection() as conn: - cur = conn.cursor() - cur.execute(query, (resume_version, process_id, agent_idx)) - results = cur.fetchall() - - if not results: - print(f"No valid programs found for version {resume_version}") - return None, None, None, None - resulting_program_dict = dict( - zip([desc[0] for desc in cur.description], results[0]) - ) - # make meta, state_json achievements_json and conversation_json table a dict - resulting_program_dict["meta"] = json.loads(resulting_program_dict["meta"]) - resulting_program_dict["achievements_json"] = json.loads( - resulting_program_dict["achievements_json"] - ) - resulting_program_dict["conversation_json"] = json.loads( - resulting_program_dict["conversation_json"] - ) - resulting_program_dict["state_json"] = json.loads( - resulting_program_dict["state_json"] - ) - # Choose a program to resume from - program = Program.from_row(resulting_program_dict) - return program.state, program.conversation, program.id, program.depth - - except Exception as e: - print(f"Error getting resume state: {e}") - return None, None, None, None - - @tenacity.retry( - retry=retry_if_exception_type( - (psycopg2.OperationalError, psycopg2.InterfaceError, psycopg2.DatabaseError) - ), - wait=wait_random_exponential(multiplier=1, min=4, max=10), - ) - async def create_program(self, program: Program) -> Program: - """Create a new program, now with connection management""" - try: - with self.get_connection() as conn: - cur = conn.cursor() - - # Insert the program data - cur.execute( - """ - INSERT INTO programs (code, value, visits, parent_id, state_json, conversation_json, - completion_token_usage, prompt_token_usage, token_usage, response, - holdout_value, raw_reward, version, version_description, model, meta, - achievements_json, instance, depth, advantage, ticks, timing_metrics_json) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, - ( - program.code, - program.value, - 0, # Assuming visits is initially set to 0 - program.parent_id, - program.state.to_raw() if program.state else None, - json.dumps(program.conversation.dict()), - program.completion_token_usage, - program.prompt_token_usage, - program.token_usage, - program.response, - program.holdout_value, - program.raw_reward, - program.version, - program.version_description, - program.model, - json.dumps(program.meta), - json.dumps(program.achievements), - program.instance, - program.depth / 2, - program.advantage, - program.ticks, - json.dumps(program.timing_metrics) - if program.timing_metrics - else None, - ), - ) - - # Get the last inserted row ID - program.id = cur.lastrowid - - # Retrieve the created_at timestamp - cur.execute( - "SELECT created_at FROM programs WHERE id = ?", (program.id,) - ) - program.created_at = cur.fetchone()[0] - conn.commit() - # Return the updated program object - return program - except Exception as e: - conn.rollback() - print(f"Error creating program: {e}") - raise e - - -def create_default_sqlite_db(db_file: str) -> None: - """Create SQLite database with required schema if it doesn't exist""" - db_path = Path(db_file) - - # Create directory if it doesn't exist - db_path.parent.mkdir(parents=True, exist_ok=True) - - # Create database and table if they don't exist - conn = sqlite3.connect(db_file) - try: - cursor = conn.cursor() - - # Check if programs table exists - cursor.execute(""" - SELECT name FROM sqlite_master - WHERE type='table' AND name='programs' - """) - - if not cursor.fetchone(): - print(f"Creating SQLite database schema in {db_file}") - cursor.execute(""" - CREATE TABLE programs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - code TEXT NOT NULL, - value REAL DEFAULT 0.0, - visits INTEGER DEFAULT 0, - parent_id INTEGER, - state_json TEXT, - conversation_json TEXT NOT NULL, - completion_token_usage INTEGER, - prompt_token_usage INTEGER, - token_usage INTEGER, - response TEXT, - holdout_value REAL, - raw_reward REAL, - version INTEGER DEFAULT 1, - version_description TEXT DEFAULT '', - model TEXT DEFAULT 'gpt-4o', - meta TEXT, - achievements_json TEXT, - instance INTEGER DEFAULT -1, - depth REAL DEFAULT 0.0, - advantage REAL DEFAULT 0.0, - ticks INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - timing_metrics_json TEXT - ) - """) - conn.commit() - print("SQLite database schema created successfully!") - - finally: - conn.close() - - -def create_default_postgres_db(**db_config) -> None: - """Create PostgreSQL database with required schema if it doesn't exist""" - conn = None - try: - conn = psycopg2.connect(**db_config) - cursor = conn.cursor() - - # Check if programs table exists - cursor.execute(""" - SELECT EXISTS ( - SELECT FROM information_schema.tables - WHERE table_schema = 'public' - AND table_name = 'programs' - ) - """) - - table_exists = cursor.fetchone()[0] - - if not table_exists: - print("Creating PostgreSQL database schema") - cursor.execute(""" - CREATE TABLE programs ( - id SERIAL PRIMARY KEY, - code TEXT NOT NULL, - value REAL DEFAULT 0.0, - visits INTEGER DEFAULT 0, - parent_id INTEGER, - state_json TEXT, - conversation_json TEXT NOT NULL, - completion_token_usage INTEGER, - prompt_token_usage INTEGER, - token_usage INTEGER, - response TEXT, - holdout_value REAL, - raw_reward REAL, - version INTEGER DEFAULT 1, - version_description TEXT DEFAULT '', - model TEXT DEFAULT 'gpt-4o', - meta TEXT, - achievements_json TEXT, - instance INTEGER DEFAULT -1, - depth REAL DEFAULT 0.0, - advantage REAL DEFAULT 0.0, - ticks INTEGER DEFAULT 0, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - timing_metrics_json TEXT - ) - """) - conn.commit() - print("PostgreSQL database schema created successfully!") - - except Exception as e: - print(f"Error creating PostgreSQL schema: {e}") - if conn: - conn.rollback() - raise - finally: - if conn: - conn.close() - - -async def create_db_client( - max_conversation_length: int = 40, - min_connections: int = 2, - max_connections: int = 5, -) -> DBClient: - """ - Create database client based on environment configuration. - Defaults to SQLite with automatic setup. - """ - # Check for database type preference - db_type = os.getenv("FLE_DB_TYPE", "sqlite").lower() - - if db_type == "postgres": - # Use PostgreSQL if explicitly requested and configured - required_vars = [ - "SKILLS_DB_HOST", - "SKILLS_DB_PORT", - "SKILLS_DB_NAME", - "SKILLS_DB_USER", - "SKILLS_DB_PASSWORD", - ] - missing_vars = [var for var in required_vars if not os.getenv(var)] - - if missing_vars: - print( - f"Warning: PostgreSQL requested but missing environment variables: {missing_vars}" - ) - print("Falling back to SQLite...") - raise Exception(f"Missing environment variables: {missing_vars}") - db_config = { - "host": os.getenv("SKILLS_DB_HOST"), - "port": os.getenv("SKILLS_DB_PORT"), - "dbname": os.getenv("SKILLS_DB_NAME"), - "user": os.getenv("SKILLS_DB_USER"), - "password": os.getenv("SKILLS_DB_PASSWORD"), - } - create_default_postgres_db(**db_config) - - return PostgresDBClient( - max_conversation_length=max_conversation_length, - min_connections=min_connections, - max_connections=max_connections, - **db_config, - ) - elif db_type == "sqlite": - # Default to SQLite - sqlite_file = os.getenv("SQLITE_DB_FILE", ".fle/data.db") - print(f"Using SQLite database file: {sqlite_file}") - - # Auto-create SQLite database if it doesn't exist - create_default_sqlite_db(sqlite_file) - - return SQLliteDBClient( - max_conversation_length=max_conversation_length, - min_connections=min_connections, - max_connections=max_connections, - database_file=sqlite_file, - ) - else: - raise Exception(f"Invalid database type: {db_type}") diff --git a/fle/services/docker/config.py b/fle/services/docker/config.py new file mode 100644 index 000000000..e69de29bb diff --git a/fle/services/docker_manager.py b/fle/services/docker/docker_manager.py similarity index 100% rename from fle/services/docker_manager.py rename to fle/services/docker/docker_manager.py diff --git a/fle/trajectory_runner.py b/fle/trajectory_runner.py index 1a744549a..b05458e77 100644 --- a/fle/trajectory_runner.py +++ b/fle/trajectory_runner.py @@ -12,7 +12,7 @@ from fle.env.gym_env.config import GymEvalConfig from fle.env.gym_env.observation import Observation from fle.env.gym_env.trajectory_logger import TrajectoryLogger -from fle.services.db_client import DBClient +from fle.services.db.db_client import DBClient class GymTrajectoryRunner: diff --git a/tests/multiagent/test_messages.py b/tests/multiagent/test_messages.py index 93ccedc7c..c231da570 100644 --- a/tests/multiagent/test_messages.py +++ b/tests/multiagent/test_messages.py @@ -5,7 +5,7 @@ import pytest from fle.agents.basic_agent import BasicAgent -from fle.services.db_client import PostgresDBClient +from fle.services.db.db_client import PostgresDBClient from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message from fle.env.a2a_instance import A2AFactorioInstance From f0799f4b59bed614cc09d339f24901f2cc6d3ec5 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 7 Aug 2025 14:38:19 +0530 Subject: [PATCH 37/53] wow --- fle/agents/gym_agent.py | 15 +-- fle/env/game/config.py | 105 ++++++++++++++++++ fle/env/game/factorio_client.py | 21 ++-- fle/env/game/instance.py | 4 +- fle/{ => env}/trajectory_runner.py | 2 +- fle/eval/algorithms/independent/__init__.py | 1 - .../independent/trajectory_runner.py | 6 - fle/eval/entrypoints/independent_run_a2a.py | 4 +- fle/run.py | 9 +- fle/{env/gym_env => }/run_eval.py | 5 +- fle/services/db/db_client.py | 13 ++- fle/services/docker/config.py | 87 +++++++++++++++ fle/services/docker/docker_manager.py | 102 +++++------------ tests/actions/_test_inspect_entities.py | 2 +- tests/actions/_test_shift_entity.py | 2 +- tests/actions/test_can_place.py | 4 +- .../actions/test_connect_entities_dry_run.py | 2 +- tests/actions/test_craft.py | 2 +- tests/actions/test_extract_item.py | 2 +- tests/actions/test_get_entities.py | 2 +- tests/actions/test_get_entity.py | 2 +- tests/actions/test_get_prototype_recipe.py | 2 +- tests/actions/test_get_research_progress.py | 2 +- tests/actions/test_get_resource_patch.py | 2 +- tests/actions/test_harvest_resource.py | 2 +- tests/actions/test_insert_item.py | 2 +- tests/actions/test_inspect_inventory.py | 2 +- tests/actions/test_move_to.py | 2 +- tests/actions/test_nearest.py | 2 +- tests/actions/test_pickup_entity.py | 2 +- tests/actions/test_place.py | 2 +- tests/actions/test_place_entity_next_to.py | 2 +- .../actions/test_place_next_to_and_rotate.py | 2 +- tests/actions/test_print.py | 2 +- tests/actions/test_render.py | 2 +- tests/actions/test_request_path.py | 2 +- tests/actions/test_rotate.py | 2 +- tests/actions/test_score.py | 2 +- tests/actions/test_set_entity_recipe.py | 2 +- tests/actions/test_set_research.py | 2 +- tests/actions/test_sleep.py | 2 +- tests/benchmarks/test_elapsed_ticks.py | 2 +- .../test_blueprint_based_policies.py | 2 +- tests/blueprints/test_save_load.py | 2 +- tests/complex/test_edge_cases.py | 2 +- tests/connect/test_connect_pipes.py | 2 +- tests/connect/test_connect_poles.py | 2 +- tests/connect/test_connect_transport_belts.py | 2 +- .../connect/test_connect_underground_belts.py | 2 +- .../connect/test_connect_underground_pipes.py | 2 +- tests/connect/test_connect_walls.py | 2 +- tests/connect/test_fluid_handlers.py | 2 +- tests/entities/test_assemblers.py | 2 +- tests/entities/test_drill.py | 2 +- tests/entities/test_fluid_processors.py | 2 +- tests/entities/test_inserters.py | 2 +- tests/entities/test_nuclear_reactor.py | 2 +- tests/entities/test_power_generators.py | 2 +- tests/entities/test_rockets.py | 2 +- ...test_auto_fueling_iron_smelting_factory.py | 2 +- tests/functional/test_auto_refilling_coal.py | 2 +- tests/functional/test_defence.py | 2 +- tests/functional/test_electricity_unit.py | 2 +- tests/functional/test_factory_unit.py | 2 +- tests/functional/test_full_oil_chain.py | 2 +- .../test_multi_drill_multi_furnace.py | 2 +- .../test_multiple_drills_multiple_furnaces.py | 2 +- tests/functional/test_objectives.py | 2 +- tests/functional/test_research.py | 2 +- tests/functional/test_small_iron_factory.py | 2 +- tests/status/test_pipes_status.py | 2 +- tests/status/test_poles_status.py | 2 +- tests/status/test_pump_status.py | 2 +- tests/test_functions.py | 2 +- tests/test_inventory_deduction.py | 2 +- tests/test_rcon_utils.py | 2 +- tests/test_variables.py | 2 +- 77 files changed, 322 insertions(+), 182 deletions(-) rename fle/{ => env}/trajectory_runner.py (100%) rename fle/{env/gym_env => }/run_eval.py (96%) diff --git a/fle/agents/gym_agent.py b/fle/agents/gym_agent.py index 0d7928803..cf83c1fe6 100644 --- a/fle/agents/gym_agent.py +++ b/fle/agents/gym_agent.py @@ -1,22 +1,19 @@ import copy from typing import Any, Optional -from fle.env.gym_env.observation import Observation - +from fle.agents.agent_abc import AgentABC +from fle.agents.formatters import RecursiveReportFormatter +from fle.agents.llm.api_factory import APIFactory +from fle.agents.llm.parsing import Policy, parse_response +from fle.agents.models import CompletionResult from fle.commons.models.conversation import Conversation from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.program import Program +from fle.env.gym_env.observation import Observation from fle.env.gym_env.observation_formatter import BasicObservationFormatter from fle.env.gym_env.system_prompt_formatter import SystemPromptFormatter from fle.env.tasks import TaskABC -from fle.agents.models import CompletionResult -from fle.agents.llm.parsing import Policy -from fle.agents.agent_abc import AgentABC -from fle.agents.formatters import RecursiveReportFormatter -from fle.agents.llm.api_factory import APIFactory -from fle.agents.llm.parsing import parse_response - GYM_AGENT_INSTRUCTIONS = """ # Factorio Gym Agent Instructions diff --git a/fle/env/game/config.py b/fle/env/game/config.py index e69de29bb..b99fc31f5 100644 --- a/fle/env/game/config.py +++ b/fle/env/game/config.py @@ -0,0 +1,105 @@ +# fle/env/game/config.py + +import os +from typing import Dict, Optional, Any +from pathlib import Path + +from pydantic import BaseModel, Field, field_validator +# from pydantic_settings import BaseSettings + + +class GameConfig(BaseModel): + """Central configuration for the Factorio game environment using Pydantic Settings""" + + # Core game settings + peaceful: bool = Field(default=True, description="Peaceful mode (no enemies)") + all_technologies_researched: bool = Field(default=True, description="Start with all technologies researched") + fast_mode: bool = Field(default=True, description="Enable fast mode") + num_agents: int = Field(default=1, ge=1, description="Number of agents") + + # Performance and limits + chunk_size: int = Field(default=32, description="Chunk size for map operations") + max_samples: int = Field(default=5000, description="Maximum samples for analysis") + max_sequential_exception_count: int = Field(default=1, description="Max sequential exceptions before stopping") + + # Inventory and starting conditions + initial_inventory: Dict[str, int] = Field(default_factory=dict, description="Initial inventory for agents") + + # Environment variables and paths + factorio_path: Optional[Path] = Field(default=None, description="Path to Factorio installation") + mods_path: Optional[Path] = Field(default=None, description="Path to Factorio mods") + saves_path: Optional[Path] = Field(default=None, description="Path to Factorio saves") + + @field_validator("factorio_path", "mods_path", "saves_path", mode="before") + @classmethod + def validate_paths(cls, v): + """Convert string paths to Path objects""" + if v is not None and isinstance(v, str): + return Path(v) + return v + + @field_validator("initial_inventory", mode="before") + @classmethod + def validate_inventory(cls, v): + """Validate inventory format""" + if isinstance(v, str): + # Try to parse as JSON if it's a string + import json + try: + return json.loads(v) + except json.JSONDecodeError: + raise ValueError("initial_inventory must be valid JSON") + return v + + def model_dump(self, **kwargs) -> Dict[str, Any]: + """Convert config to dictionary for serialization""" + return super().model_dump(**kwargs) + + def model_dump_json(self, **kwargs) -> str: + """Convert config to JSON string""" + return super().model_dump_json(**kwargs) + + @classmethod + def model_validate_json(cls, json_str: str) -> "GameConfig": + """Create config from JSON string""" + return super().model_validate_json(json_str) + + +# Default configuration instance +DEFAULT_CONFIG = GameConfig() + +# Global configuration instance that can be modified +current_config = DEFAULT_CONFIG + + +def get_config() -> GameConfig: + """Get the current configuration""" + return current_config + + +def set_config(config: GameConfig) -> None: + """Set the current configuration""" + global current_config + current_config = config + + +def update_config(**kwargs) -> None: + """Update specific configuration parameters""" + global current_config + # Create a new config with updated values + new_config = current_config.model_copy(update=kwargs) + set_config(new_config) + + +def load_config_from_file(file_path: str) -> GameConfig: + """Load configuration from a JSON file""" + with open(file_path, 'r') as f: + import json + data = json.load(f) + return GameConfig.model_validate(data) + + +def save_config_to_file(config: GameConfig, file_path: str) -> None: + """Save configuration to a JSON file""" + with open(file_path, 'w') as f: + f.write(config.model_dump_json(indent=2)) \ No newline at end of file diff --git a/fle/env/game/factorio_client.py b/fle/env/game/factorio_client.py index 58181ce12..3dfb8ce58 100644 --- a/fle/env/game/factorio_client.py +++ b/fle/env/game/factorio_client.py @@ -1,4 +1,3 @@ -import asyncio import threading from contextlib import contextmanager from time import time as timer @@ -9,7 +8,6 @@ from fle.env.game.lua_manager import LuaScriptManager, ToolHookRegistry from fle.env.game.namespace import FactorioNamespace -from fle.services.docker.docker_manager import ServerSettings from fle.services.rcon import _lua2python @@ -68,18 +66,22 @@ class FactorioClient: """ A class that represents a Factorio server and provides an interface for interacting with it. """ + rcon_client: RCONClient - server_config: ServerSettings cache_scripts: bool tool_hook_registry: ToolHookRegistry lua_script_manager: LuaScriptManager script_dict: Dict[str, str] - + def __init__( - self, server_settings: ServerSettings, cache_scripts=True + self, rcon_port: int, address: str, rcon_password: str, cache_scripts=True ): - self.rcon_client = self.get_rcon_client() + self.address = address + self.rcon_port = rcon_port + self.rcon_password = rcon_password self.cache_scripts = cache_scripts + + self.rcon_client = self.get_rcon_client() self.tool_hook_registry = ToolHookRegistry() self.lua_script_manager = LuaScriptManager( self.rcon_client, self.tool_hook_registry, cache_scripts @@ -88,9 +90,6 @@ def __init__( **self.lua_script_manager.lib_scripts, **self.lua_script_manager.tool_scripts, } - self.rcon_password = server_settings.rcon_password - self.rcon_port = server_settings.rcon_port - self.address = server_settings.address def get_rcon_client(self, address=None): return RCONClient( @@ -125,9 +124,7 @@ def connect_to_server(self): except ConnectionError as e: print(e) - print(f"Connected to {self.address}" - f"client at tcp/{self.rcon_port}." - f"Password: {self.rcon_password}") + print(f"Connected to {self.address}" f"client at tcp/{self.rcon_port}.") return rcon_client def _get_command(self, command, parameters=[], measured=True): diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index 5145e4dd2..ceb1430a1 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -70,7 +70,7 @@ class FactorioInstance: def __init__( self, - server_settings: ServerSettings, + client: FactorioClient, fast=False, inventory=None, all_technologies_researched=True, @@ -78,10 +78,10 @@ def __init__( num_agents=1, **kwargs, ): + self.client = client self.id = str(uuid.uuid4())[:8] self.num_agents = num_agents self.persistent_vars = {} - self.tcp_port = tcp_port self.all_technologies_researched = all_technologies_researched self.fast = fast self._speed = 1 diff --git a/fle/trajectory_runner.py b/fle/env/trajectory_runner.py similarity index 100% rename from fle/trajectory_runner.py rename to fle/env/trajectory_runner.py index b05458e77..cb607f0f9 100644 --- a/fle/trajectory_runner.py +++ b/fle/env/trajectory_runner.py @@ -5,9 +5,9 @@ from fle.agents import CompletionReason, CompletionResult from fle.agents.gym_agent import GymAgent from fle.commons.models.conversation import Conversation -from fle.env.game.game_state import GameState from fle.commons.models.program import Program from fle.env.environment import FactorioGymEnv +from fle.env.game.game_state import GameState from fle.env.gym_env.action import Action from fle.env.gym_env.config import GymEvalConfig from fle.env.gym_env.observation import Observation diff --git a/fle/eval/algorithms/independent/__init__.py b/fle/eval/algorithms/independent/__init__.py index d6363ea90..05d9daf9e 100644 --- a/fle/eval/algorithms/independent/__init__.py +++ b/fle/eval/algorithms/independent/__init__.py @@ -21,7 +21,6 @@ EvalConfig, create_factorio_instance, run_process, - get_next_version, ) from .value_calculator import ( diff --git a/fle/eval/algorithms/independent/trajectory_runner.py b/fle/eval/algorithms/independent/trajectory_runner.py index b80582d16..c52e4aa72 100644 --- a/fle/eval/algorithms/independent/trajectory_runner.py +++ b/fle/eval/algorithms/independent/trajectory_runner.py @@ -493,9 +493,3 @@ def run_process(process_id: int, config: EvalConfig): asyncio.run(run_trajectory(process_id, config)) -async def get_next_version() -> int: - """Get next available version number""" - db_client = await create_db_client() - version = await db_client.get_largest_version() - await db_client.cleanup() - return version + 1 diff --git a/fle/eval/entrypoints/independent_run_a2a.py b/fle/eval/entrypoints/independent_run_a2a.py index 60511e4a4..0785e982c 100644 --- a/fle/eval/entrypoints/independent_run_a2a.py +++ b/fle/eval/entrypoints/independent_run_a2a.py @@ -2,10 +2,9 @@ import argparse import multiprocessing from dotenv import load_dotenv -from fle.agents.basic_agent import BasicAgent +# from examples.agents.basic_agent import BasicAgent from fle.eval.algorithms.independent import ( run_process, - get_next_version, create_factorio_instance, EvalConfig, ) @@ -14,6 +13,7 @@ import json from dataclasses import dataclass from fle.commons.cluster_ips import get_local_container_ips +from fle.services.db.db_client import get_next_version load_dotenv() diff --git a/fle/run.py b/fle/run.py index 4d0578ab5..05a0e9cf3 100644 --- a/fle/run.py +++ b/fle/run.py @@ -1,11 +1,12 @@ import argparse -import sys +import asyncio +import importlib.resources import shutil import subprocess +import sys from pathlib import Path -import importlib.resources -import asyncio -from fle.env.gym_env.run_eval import main as run_eval + +from fle.run_eval import main as run_eval def fle_init(): diff --git a/fle/env/gym_env/run_eval.py b/fle/run_eval.py similarity index 96% rename from fle/env/gym_env/run_eval.py rename to fle/run_eval.py index 29e4d6e6e..f39867174 100644 --- a/fle/env/gym_env/run_eval.py +++ b/fle/run_eval.py @@ -13,10 +13,9 @@ from fle.env.gym_env.config import GymEvalConfig, GymRunConfig from fle.env.gym_env.observation_formatter import BasicObservationFormatter from fle.env.gym_env.system_prompt_formatter import SystemPromptFormatter -from fle.env.gym_env.trajectory_runner import GymTrajectoryRunner +from fle.env.trajectory_runner import GymTrajectoryRunner from fle.env.registry import get_environment_info, list_available_environments -from fle.eval.algorithms.independent import get_next_version -from fle.services.db.db_client import create_db_client +from fle.services.db.db_client import create_db_client, get_next_version load_dotenv() diff --git a/fle/services/db/db_client.py b/fle/services/db/db_client.py index a5e7c08c0..2095e4cd6 100644 --- a/fle/services/db/db_client.py +++ b/fle/services/db/db_client.py @@ -15,11 +15,12 @@ import tenacity from psycopg2.extras import DictCursor from psycopg2.pool import ThreadedConnectionPool -from tenacity import retry_if_exception_type, wait_exponential, wait_random_exponential +from tenacity import (retry_if_exception_type, wait_exponential, + wait_random_exponential) from fle.commons.models.conversation import Conversation -from fle.env.game.game_state import GameState from fle.commons.models.program import Program +from fle.env.game.game_state import GameState # Configure logging logging.basicConfig(level=logging.INFO) @@ -862,3 +863,11 @@ async def create_db_client( ) else: raise Exception(f"Invalid database type: {db_type}") + + +async def get_next_version() -> int: + """Get next available version number""" + db_client = await create_db_client() + version = await db_client.get_largest_version() + await db_client.cleanup() + return version + 1 \ No newline at end of file diff --git a/fle/services/docker/config.py b/fle/services/docker/config.py index e69de29bb..9d865a103 100644 --- a/fle/services/docker/config.py +++ b/fle/services/docker/config.py @@ -0,0 +1,87 @@ +import os +from pathlib import Path +import platform +from enum import Enum +from pydantic import BaseModel, Field + +ROOT_DIR = Path(__file__).resolve().parent.parent.parent + +import logging + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class Scenario(Enum): + OPEN_WORLD = "open_world" + DEFAULT_LAB_SCENARIO = "default_lab_scenario" + + +class Mode(Enum): + SAVE_BASED = "save-based" + SCENARIO = "scenario" + + +def _detect_mods_path(os_name: str) -> str: + if any(x in os_name for x in ("MINGW", "MSYS", "CYGWIN")): + path = os.getenv("APPDATA", "") + mods = Path(path) / "Factorio" / "mods" + if mods.exists(): + return str(mods) + return str( + Path(os.getenv("USERPROFILE", "")) + / "AppData" + / "Roaming" + / "Factorio" + / "mods" + ) + return str( + Path.home() + / "Applications" + / "Factorio.app" + / "Contents" + / "Resources" + / "mods" + ) + + +class DockerConfig(BaseModel): + """Configuration knobs for Factorio headless servers managed by fle.services.docker.""" + + arch: str = Field(default_factory=platform.machine) + address: str = "localhost" + os_name: str = Field(default_factory=platform.system) + + saves_path: Path = Field(default_factory=lambda: ROOT_DIR / ".fle" / "saves") + screenshots_dir: Path = Field( + default_factory=lambda: ROOT_DIR / "data" / "_screenshots" + ) + mods_path: str = Field(default_factory=lambda: _detect_mods_path(platform.system())) + + image_name: str = "factoriotools/factorio:1.1.110" + rcon_port: int = 27015 + udp_port: int = 34197 + + scenario_name: str = Scenario.DEFAULT_LAB_SCENARIO.value + scenario_dir: Path = Field( + default_factory=lambda: Path(__file__).parent.resolve() + / "factorio" + / "scenarios" + ) + server_config_dir: Path = Field( + default_factory=lambda: Path(__file__).parent.resolve() / "factorio" / "config" + ) + + mode: str = Mode.SAVE_BASED.value + temp_playing_dir: str = "/opt/factorio/temp/currently-playing" + name_prefix: str = "factorio_" + + factorio_password: str = Field( + default_factory=lambda: ( + Path(__file__).parent.resolve() / "factorio" / "config" / "rconpw" + ) + .read_text() + .strip() + ) + + model_config = {"extra": "forbid", "frozen": True} diff --git a/fle/services/docker/docker_manager.py b/fle/services/docker/docker_manager.py index 6b379ddb7..29b1afffb 100644 --- a/fle/services/docker/docker_manager.py +++ b/fle/services/docker/docker_manager.py @@ -1,82 +1,15 @@ -#!/usr/bin/env python3 -import os -import platform import argparse -from pathlib import Path import asyncio import aiodocker from aiodocker.exceptions import DockerError from typing import List -import json - -from enum import Enum - -ROOT_DIR = Path(__file__).resolve().parent.parent - -import logging - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - - -class Scenario(Enum): - OPEN_WORLD = "open_world" - DEFAULT_LAB_SCENARIO = "default_lab_scenario" - - -class Mode(Enum): - SAVE_BASED = "save-based" - SCENARIO = "scenario" - - -class ServerSettings: - def __init__(self): - self.arch = platform.machine() - self.address = "localhost" - self.os_name = platform.system() - self.saves_path = ROOT_DIR / ".fle" / "saves" - self.screenshots_dir = ROOT_DIR / "data" / "_screenshots" - self.mods_path = self._detect_mods_path() - self.image_name = "factoriotools/factorio:1.1.110" - self.rcon_port = 27015 - self.udp_port = 34197 - self.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value - self.scenario_dir = Path(__file__).parent.resolve() / "factorio" / "scenarios" - self.server_config_dir = Path(__file__).parent.resolve() / "factorio" / "config" - self.mode = Mode.SAVE_BASED.value - self.temp_playing_dir = "/opt/factorio/temp/currently-playing" - self.name_prefix = "factorio_" - with open(self.server_config_dir / "rconpw", "r") as f: - self.factorio_password = f.read().strip() - - def _detect_mods_path(self) -> str: - if any(x in self.os_name for x in ("MINGW", "MSYS", "CYGWIN")): - path = os.getenv("APPDATA", "") - mods = Path(path) / "Factorio" / "mods" - if mods.exists(): - return str(mods) - return str( - Path(os.getenv("USERPROFILE", "")) - / "AppData" - / "Roaming" - / "Factorio" - / "mods" - ) - else: - return str( - Path.home() - / "Applications" - / "Factorio.app" - / "Contents" - / "Resources" - / "mods" - ) +from fle.services.docker.config import DockerConfig, Scenario, Mode class FactorioHeadlessClusterManager: def __init__( self, - config: ServerSettings, + config: DockerConfig, docker_platform: str, num_instances: int, dry_run: bool = False, @@ -86,7 +19,7 @@ def __init__( self.num = num_instances self.dry_run = dry_run self.docker = aiodocker.Docker() - self.docker_configs = {} + self.docker_configs_raw = {} async def _ensure_image(self): try: @@ -133,6 +66,16 @@ def _get_volumes(self, instance_index: int, for_server: bool = True) -> list: } # HostConfig.Binds expects ["host:container:mode", ...] return [f"{host}:{b['bind']}:{b['mode']}" for host, b in vols.items()] + + def _make_save_latest(self, instance_index: int, save_name: str): + save_dir = self.config.saves_path / str(instance_index) + save_dir.mkdir(parents=True, exist_ok=True) + save_zips = list(save_dir.glob(f"{save_name}*.zip")) + if len(save_zips) > 0: + save_zips[0].touch(exist_ok=True) + + return save_zips[0] + def _get_environment(self) -> list: env = { @@ -144,6 +87,7 @@ def _get_environment(self) -> list: "SERVER_SCENARIO": self.config.scenario_name, "DLC_SPACE_AGE": "false", "MODE": self.config.mode, + # "SAVE_NAME": save_name, } if self.config.mode == Mode.SCENARIO.value: env["PRESET"] = "default" @@ -251,6 +195,14 @@ async def restart(self): tasks = [ctr.restart() for ctr in ctrs] await asyncio.gather(*tasks) + async def restart_with_save(self, instance_index: int, save_name: str): + ctr = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}{instance_index}"]}) + if len(ctr) == 0: + raise ValueError(f"Container {self.config.name_prefix}{instance_index} not found") + ctr = ctr[0] + self._make_save_latest(instance_index, save_name) + await ctr.restart() + async def hot_reload_scenario(self): # Sync scenario files into the server's temp directory for hot-reload containers = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) @@ -293,10 +245,10 @@ async def attach_docker_configs(self): config_to_keep = ["Env", "Entrypoint", "Cmd", "WorkingDir"] network_to_keep = ["Ports", "IPAddress", "Gateway"] for container_info in container_infos: - self.docker_configs[container_info["Name"]] = {k: container_info[k] for k in info_to_keep} - self.docker_configs[container_info["Name"]]["Config"] = {k: container_info["Config"][k] for k in config_to_keep} - self.docker_configs[container_info["Name"]]["NetworkSettings"] = {k: container_info["NetworkSettings"][k] for k in network_to_keep} - self.docker_configs[container_info["Name"]]["HostConfig"] = {k: container_info["HostConfig"][k] for k in hostconfig_to_keep} + self.docker_configs_raw[container_info["Name"]] = {k: container_info[k] for k in info_to_keep} + self.docker_configs_raw[container_info["Name"]]["Config"] = {k: container_info["Config"][k] for k in config_to_keep} + self.docker_configs_raw[container_info["Name"]]["NetworkSettings"] = {k: container_info["NetworkSettings"][k] for k in network_to_keep} + self.docker_configs_raw[container_info["Name"]]["HostConfig"] = {k: container_info["HostConfig"][k] for k in hostconfig_to_keep} async def get_local_container_ips(self) -> tuple[List[str], List[int], List[int]]: """Get IP addresses of running Factorio containers in the local Docker setup.""" @@ -377,7 +329,7 @@ def parse_args(): async def main(): args = parse_args() - config = ServerSettings() + config = DockerConfig() docker_platform = ( "linux/arm64" if config.arch in ("arm64", "aarch64") else "linux/amd64" diff --git a/tests/actions/_test_inspect_entities.py b/tests/actions/_test_inspect_entities.py index 3c3ae3a68..aef73d972 100644 --- a/tests/actions/_test_inspect_entities.py +++ b/tests/actions/_test_inspect_entities.py @@ -4,7 +4,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "coal": 5, diff --git a/tests/actions/_test_shift_entity.py b/tests/actions/_test_shift_entity.py index 4657c9974..89ecebfa9 100644 --- a/tests/actions/_test_shift_entity.py +++ b/tests/actions/_test_shift_entity.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace instance.reset() diff --git a/tests/actions/test_can_place.py b/tests/actions/test_can_place.py index 001e0bd49..c3c2711a9 100644 --- a/tests/actions/test_can_place.py +++ b/tests/actions/test_can_place.py @@ -5,13 +5,13 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace instance.reset() -def test_can_place(game): +def test_can_place(instance): """ Place a boiler at (0, 0) :param game: diff --git a/tests/actions/test_connect_entities_dry_run.py b/tests/actions/test_connect_entities_dry_run.py index 34ee2893b..28db28140 100644 --- a/tests/actions/test_connect_entities_dry_run.py +++ b/tests/actions/test_connect_entities_dry_run.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "transport-belt": 12, } diff --git a/tests/actions/test_craft.py b/tests/actions/test_craft.py index e2ff3d921..2d9a05b73 100644 --- a/tests/actions/test_craft.py +++ b/tests/actions/test_craft.py @@ -4,7 +4,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() instance.set_inventory( { diff --git a/tests/actions/test_extract_item.py b/tests/actions/test_extract_item.py index bb62a4fb3..58950f2bd 100644 --- a/tests/actions/test_extract_item.py +++ b/tests/actions/test_extract_item.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "iron-chest": 1, "iron-plate": 10, diff --git a/tests/actions/test_get_entities.py b/tests/actions/test_get_entities.py index e798d7cd3..845e5791c 100644 --- a/tests/actions/test_get_entities.py +++ b/tests/actions/test_get_entities.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "coal": 10, diff --git a/tests/actions/test_get_entity.py b/tests/actions/test_get_entity.py index 7c65d7b06..6b7c5636d 100644 --- a/tests/actions/test_get_entity.py +++ b/tests/actions/test_get_entity.py @@ -6,7 +6,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "coal": 5, diff --git a/tests/actions/test_get_prototype_recipe.py b/tests/actions/test_get_prototype_recipe.py index 384ca645c..6eaead2cf 100644 --- a/tests/actions/test_get_prototype_recipe.py +++ b/tests/actions/test_get_prototype_recipe.py @@ -4,7 +4,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = {"assembling-machine-1": 1} instance.reset() yield instance.namespace diff --git a/tests/actions/test_get_research_progress.py b/tests/actions/test_get_research_progress.py index 86acaef06..d869699d6 100644 --- a/tests/actions/test_get_research_progress.py +++ b/tests/actions/test_get_research_progress.py @@ -6,7 +6,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): # game.initial_inventory = {'assembling-machine-1': 1} ips, udp_ports, tcp_ports = get_local_container_ips() instance = FactorioInstance( diff --git a/tests/actions/test_get_resource_patch.py b/tests/actions/test_get_resource_patch.py index fea776d39..7cdb5f6dd 100644 --- a/tests/actions/test_get_resource_patch.py +++ b/tests/actions/test_get_resource_patch.py @@ -6,7 +6,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace instance.reset() diff --git a/tests/actions/test_harvest_resource.py b/tests/actions/test_harvest_resource.py index 4c76c1584..7a154ec64 100644 --- a/tests/actions/test_harvest_resource.py +++ b/tests/actions/test_harvest_resource.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace # instance.reset() diff --git a/tests/actions/test_insert_item.py b/tests/actions/test_insert_item.py index d266df49a..793776f54 100644 --- a/tests/actions/test_insert_item.py +++ b/tests/actions/test_insert_item.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "iron-chest": 1, "iron-ore": 500, diff --git a/tests/actions/test_inspect_inventory.py b/tests/actions/test_inspect_inventory.py index 348fd71e6..00a1d67c2 100644 --- a/tests/actions/test_inspect_inventory.py +++ b/tests/actions/test_inspect_inventory.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "coal": 50, diff --git a/tests/actions/test_move_to.py b/tests/actions/test_move_to.py index 563186197..df004b311 100644 --- a/tests/actions/test_move_to.py +++ b/tests/actions/test_move_to.py @@ -6,7 +6,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace # instance.reset() diff --git a/tests/actions/test_nearest.py b/tests/actions/test_nearest.py index 4f00c109d..eff3d8c0a 100644 --- a/tests/actions/test_nearest.py +++ b/tests/actions/test_nearest.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace instance.reset() diff --git a/tests/actions/test_pickup_entity.py b/tests/actions/test_pickup_entity.py index 3d3b40d77..6eeb6d053 100644 --- a/tests/actions/test_pickup_entity.py +++ b/tests/actions/test_pickup_entity.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 1, "boiler": 1, diff --git a/tests/actions/test_place.py b/tests/actions/test_place.py index 6a8f493be..f4123340a 100644 --- a/tests/actions/test_place.py +++ b/tests/actions/test_place.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 1, "boiler": 1, diff --git a/tests/actions/test_place_entity_next_to.py b/tests/actions/test_place_entity_next_to.py index 5d0ce181d..843a4458c 100644 --- a/tests/actions/test_place_entity_next_to.py +++ b/tests/actions/test_place_entity_next_to.py @@ -6,7 +6,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "boiler": 3, "transport-belt": 1, diff --git a/tests/actions/test_place_next_to_and_rotate.py b/tests/actions/test_place_next_to_and_rotate.py index dee081300..ad4c4b67a 100644 --- a/tests/actions/test_place_next_to_and_rotate.py +++ b/tests/actions/test_place_next_to_and_rotate.py @@ -6,7 +6,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "boiler": 1, "transport-belt": 1, diff --git a/tests/actions/test_print.py b/tests/actions/test_print.py index 7f150dc36..e0f204cd6 100644 --- a/tests/actions/test_print.py +++ b/tests/actions/test_print.py @@ -2,7 +2,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace instance.reset() diff --git a/tests/actions/test_render.py b/tests/actions/test_render.py index ab40f3e93..b397aadfa 100644 --- a/tests/actions/test_render.py +++ b/tests/actions/test_render.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "iron-chest": 1, "small-electric-pole": 20, diff --git a/tests/actions/test_request_path.py b/tests/actions/test_request_path.py index 25d0439cc..b1c40485a 100644 --- a/tests/actions/test_request_path.py +++ b/tests/actions/test_request_path.py @@ -4,7 +4,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace instance.reset() diff --git a/tests/actions/test_rotate.py b/tests/actions/test_rotate.py index aea6dab02..536a1c4db 100644 --- a/tests/actions/test_rotate.py +++ b/tests/actions/test_rotate.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "iron-chest": 1, "pipe": 10, diff --git a/tests/actions/test_score.py b/tests/actions/test_score.py index 2865a0902..2fc1d1d08 100644 --- a/tests/actions/test_score.py +++ b/tests/actions/test_score.py @@ -2,7 +2,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace diff --git a/tests/actions/test_set_entity_recipe.py b/tests/actions/test_set_entity_recipe.py index 422895b3a..618380242 100644 --- a/tests/actions/test_set_entity_recipe.py +++ b/tests/actions/test_set_entity_recipe.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): game.initial_inventory = {"assembling-machine-1": 1} instance.reset() yield instance.namespace diff --git a/tests/actions/test_set_research.py b/tests/actions/test_set_research.py index f455417b4..f03b5748f 100644 --- a/tests/actions/test_set_research.py +++ b/tests/actions/test_set_research.py @@ -6,7 +6,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): # game.initial_inventory = {'assembling-machine-1': 1} # from gym import FactorioInstance ips, udp_ports, tcp_ports = get_local_container_ips() diff --git a/tests/actions/test_sleep.py b/tests/actions/test_sleep.py index a5bb7f420..fb20cf350 100644 --- a/tests/actions/test_sleep.py +++ b/tests/actions/test_sleep.py @@ -4,7 +4,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace instance.reset() diff --git a/tests/benchmarks/test_elapsed_ticks.py b/tests/benchmarks/test_elapsed_ticks.py index a264923ff..7eb8884b0 100644 --- a/tests/benchmarks/test_elapsed_ticks.py +++ b/tests/benchmarks/test_elapsed_ticks.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "iron-plate": 400, "iron-gear-wheel": 1, diff --git a/tests/blueprints/test_blueprint_based_policies.py b/tests/blueprints/test_blueprint_based_policies.py index b55c1af81..3f62d99e4 100644 --- a/tests/blueprints/test_blueprint_based_policies.py +++ b/tests/blueprints/test_blueprint_based_policies.py @@ -6,7 +6,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "burner-mining-drill": 5, "electric-mining-drill": 20, diff --git a/tests/blueprints/test_save_load.py b/tests/blueprints/test_save_load.py index db03c247b..d26472656 100644 --- a/tests/blueprints/test_save_load.py +++ b/tests/blueprints/test_save_load.py @@ -11,7 +11,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "burner-mining-drill": 5, "electric-mining-drill": 20, diff --git a/tests/complex/test_edge_cases.py b/tests/complex/test_edge_cases.py index 1667c0cc8..a73ec4e0b 100644 --- a/tests/complex/test_edge_cases.py +++ b/tests/complex/test_edge_cases.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 10, "burner-mining-drill": 10, diff --git a/tests/connect/test_connect_pipes.py b/tests/connect/test_connect_pipes.py index a906f58c3..e853bc699 100644 --- a/tests/connect/test_connect_pipes.py +++ b/tests/connect/test_connect_pipes.py @@ -16,7 +16,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-furnace": 10, diff --git a/tests/connect/test_connect_poles.py b/tests/connect/test_connect_poles.py index eb2ecda48..4b5a08fae 100644 --- a/tests/connect/test_connect_poles.py +++ b/tests/connect/test_connect_poles.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-furnace": 10, diff --git a/tests/connect/test_connect_transport_belts.py b/tests/connect/test_connect_transport_belts.py index 3d03b6031..ea88292e5 100644 --- a/tests/connect/test_connect_transport_belts.py +++ b/tests/connect/test_connect_transport_belts.py @@ -7,7 +7,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-furnace": 10, diff --git a/tests/connect/test_connect_underground_belts.py b/tests/connect/test_connect_underground_belts.py index 2b198b8d4..78381e9ec 100644 --- a/tests/connect/test_connect_underground_belts.py +++ b/tests/connect/test_connect_underground_belts.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-furnace": 10, diff --git a/tests/connect/test_connect_underground_pipes.py b/tests/connect/test_connect_underground_pipes.py index a4b2c77e7..04d62ff51 100644 --- a/tests/connect/test_connect_underground_pipes.py +++ b/tests/connect/test_connect_underground_pipes.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-furnace": 10, diff --git a/tests/connect/test_connect_walls.py b/tests/connect/test_connect_walls.py index a9ac8517d..cfb2d476c 100644 --- a/tests/connect/test_connect_walls.py +++ b/tests/connect/test_connect_walls.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-wall": 100, diff --git a/tests/connect/test_fluid_handlers.py b/tests/connect/test_fluid_handlers.py index 7b94fe720..99d1f7010 100644 --- a/tests/connect/test_fluid_handlers.py +++ b/tests/connect/test_fluid_handlers.py @@ -6,7 +6,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-furnace": 10, diff --git a/tests/entities/test_assemblers.py b/tests/entities/test_assemblers.py index c9a14cc57..48502f405 100644 --- a/tests/entities/test_assemblers.py +++ b/tests/entities/test_assemblers.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "solar-panel": 3, diff --git a/tests/entities/test_drill.py b/tests/entities/test_drill.py index 3e8793c9a..2f2c94c98 100644 --- a/tests/entities/test_drill.py +++ b/tests/entities/test_drill.py @@ -4,7 +4,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "solar-panel": 3, diff --git a/tests/entities/test_fluid_processors.py b/tests/entities/test_fluid_processors.py index f00746822..b2166256c 100644 --- a/tests/entities/test_fluid_processors.py +++ b/tests/entities/test_fluid_processors.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "pipe": 200, diff --git a/tests/entities/test_inserters.py b/tests/entities/test_inserters.py index 174e3f155..2cfdd590a 100644 --- a/tests/entities/test_inserters.py +++ b/tests/entities/test_inserters.py @@ -4,7 +4,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "solar-panel": 3, diff --git a/tests/entities/test_nuclear_reactor.py b/tests/entities/test_nuclear_reactor.py index 69d7db9ce..d1b2b10d7 100644 --- a/tests/entities/test_nuclear_reactor.py +++ b/tests/entities/test_nuclear_reactor.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "nuclear-reactor": 1, diff --git a/tests/entities/test_power_generators.py b/tests/entities/test_power_generators.py index a7a3d31e0..d625df0cb 100644 --- a/tests/entities/test_power_generators.py +++ b/tests/entities/test_power_generators.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "solar-panel": 3, diff --git a/tests/entities/test_rockets.py b/tests/entities/test_rockets.py index a06b75456..66c22b46b 100644 --- a/tests/entities/test_rockets.py +++ b/tests/entities/test_rockets.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "rocket-silo": 1, diff --git a/tests/functional/test_auto_fueling_iron_smelting_factory.py b/tests/functional/test_auto_fueling_iron_smelting_factory.py index 7f5c955b2..5d22c97a5 100644 --- a/tests/functional/test_auto_fueling_iron_smelting_factory.py +++ b/tests/functional/test_auto_fueling_iron_smelting_factory.py @@ -7,7 +7,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "burner-mining-drill": 2, "stone-furnace": 1, diff --git a/tests/functional/test_auto_refilling_coal.py b/tests/functional/test_auto_refilling_coal.py index 42c0114a2..873ca776d 100644 --- a/tests/functional/test_auto_refilling_coal.py +++ b/tests/functional/test_auto_refilling_coal.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 1, "iron-chest": 3, diff --git a/tests/functional/test_defence.py b/tests/functional/test_defence.py index a3770c54f..3e08696df 100644 --- a/tests/functional/test_defence.py +++ b/tests/functional/test_defence.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 1, "iron-chest": 3, diff --git a/tests/functional/test_electricity_unit.py b/tests/functional/test_electricity_unit.py index b17b38e17..c95d76ae0 100644 --- a/tests/functional/test_electricity_unit.py +++ b/tests/functional/test_electricity_unit.py @@ -15,7 +15,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 1, "transport-belt": 30, diff --git a/tests/functional/test_factory_unit.py b/tests/functional/test_factory_unit.py index 2aaa76807..3c13ecc2d 100644 --- a/tests/functional/test_factory_unit.py +++ b/tests/functional/test_factory_unit.py @@ -8,7 +8,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 1, "burner-mining-drill": 3, diff --git a/tests/functional/test_full_oil_chain.py b/tests/functional/test_full_oil_chain.py index 41249dd71..ffc4fee7d 100644 --- a/tests/functional/test_full_oil_chain.py +++ b/tests/functional/test_full_oil_chain.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 1, "transport-belt": 30, diff --git a/tests/functional/test_multi_drill_multi_furnace.py b/tests/functional/test_multi_drill_multi_furnace.py index ff3cd2672..2a8001644 100644 --- a/tests/functional/test_multi_drill_multi_furnace.py +++ b/tests/functional/test_multi_drill_multi_furnace.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "burner-mining-drill": 2, "stone-furnace": 5, diff --git a/tests/functional/test_multiple_drills_multiple_furnaces.py b/tests/functional/test_multiple_drills_multiple_furnaces.py index cf4f92f40..da4becb2d 100644 --- a/tests/functional/test_multiple_drills_multiple_furnaces.py +++ b/tests/functional/test_multiple_drills_multiple_furnaces.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "coal": 200, "burner-mining-drill": 10, diff --git a/tests/functional/test_objectives.py b/tests/functional/test_objectives.py index a5dbe934f..ea190e9dc 100644 --- a/tests/functional/test_objectives.py +++ b/tests/functional/test_objectives.py @@ -16,7 +16,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 1, "boiler": 5, diff --git a/tests/functional/test_research.py b/tests/functional/test_research.py index efb59035d..71c0260ec 100644 --- a/tests/functional/test_research.py +++ b/tests/functional/test_research.py @@ -8,7 +8,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): initial_inventory = { "coal": 50, "copper-plate": 50, diff --git a/tests/functional/test_small_iron_factory.py b/tests/functional/test_small_iron_factory.py index 9bf6a6d38..55b52226f 100644 --- a/tests/functional/test_small_iron_factory.py +++ b/tests/functional/test_small_iron_factory.py @@ -4,7 +4,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 5, "iron-chest": 2, diff --git a/tests/status/test_pipes_status.py b/tests/status/test_pipes_status.py index fa3b307cf..b2524b9f3 100644 --- a/tests/status/test_pipes_status.py +++ b/tests/status/test_pipes_status.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-furnace": 10, diff --git a/tests/status/test_poles_status.py b/tests/status/test_poles_status.py index 6e9385009..046cd8bc6 100644 --- a/tests/status/test_poles_status.py +++ b/tests/status/test_poles_status.py @@ -5,7 +5,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-furnace": 10, diff --git a/tests/status/test_pump_status.py b/tests/status/test_pump_status.py index f0fe23908..d3de80021 100644 --- a/tests/status/test_pump_status.py +++ b/tests/status/test_pump_status.py @@ -7,7 +7,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { **instance.initial_inventory, "stone-furnace": 10, diff --git a/tests/test_functions.py b/tests/test_functions.py index 1df7d402b..6a09d3aa9 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -2,7 +2,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace diff --git a/tests/test_inventory_deduction.py b/tests/test_inventory_deduction.py index 74e18655d..067f0283f 100644 --- a/tests/test_inventory_deduction.py +++ b/tests/test_inventory_deduction.py @@ -4,7 +4,7 @@ from fle.env.game.entities import Position, Direction @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { 'pipe': 10, 'small-electric-pole': 5, diff --git a/tests/test_rcon_utils.py b/tests/test_rcon_utils.py index 16b81b5c8..bcceee96f 100644 --- a/tests/test_rcon_utils.py +++ b/tests/test_rcon_utils.py @@ -4,7 +4,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.reset() yield instance.namespace diff --git a/tests/test_variables.py b/tests/test_variables.py index f10d2ddc3..10beed187 100644 --- a/tests/test_variables.py +++ b/tests/test_variables.py @@ -2,7 +2,7 @@ @pytest.fixture() -def game(instance): +def game(instance: FactorioInstance): instance.initial_inventory = { "stone-furnace": 10, "burner-mining-drill": 10, From d9f9f7b806f2370926be2121efce0a065c88b951 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 12 Aug 2025 19:43:44 +0530 Subject: [PATCH 38/53] a lotta things --- fle/commons/models/program.py | 61 +++- fle/env/environment.py | 200 ++--------- fle/env/game/factorio_client.py | 8 +- fle/env/game/instance.py | 214 +++++++----- fle/env/game_session.py | 380 ++++++++++++++++++++- fle/env/gym_env/config.py | 13 +- fle/env/gym_env/observation_formatter.py | 2 +- fle/env/gym_env/trajectory_logger.py | 2 +- fle/env/{gym_env => models}/action.py | 2 +- fle/env/{gym_env => models}/observation.py | 0 fle/env/trajectory_runner.py | 130 ++----- fle/run_eval.py | 1 - fle/services/docker/docker_manager.py | 8 + 13 files changed, 646 insertions(+), 375 deletions(-) rename fle/env/{gym_env => models}/action.py (99%) rename fle/env/{gym_env => models}/observation.py (100%) diff --git a/fle/commons/models/program.py b/fle/commons/models/program.py index 9a659d95b..c8a13db04 100644 --- a/fle/commons/models/program.py +++ b/fle/commons/models/program.py @@ -2,12 +2,11 @@ from typing import Dict, List, Optional import numpy as np -from pydantic import ConfigDict, BaseModel, Field - -from fle.commons.models.timing_metrics import TimingMetrics +from pydantic import BaseModel, ConfigDict, Field from fle.commons.models.achievements import ProductionFlows from fle.commons.models.conversation import Conversation +from fle.commons.models.timing_metrics import TimingMetrics from fle.env.game.game_state import GameState @@ -84,3 +83,59 @@ def from_row(cls, row: Dict): if row.get("timing_metrics_json") else [], ) + + @classmethod + async def from_policy( + cls, + policy, + agent_idx: int, + reward: float, + response: str, + error_occurred: bool, + game_state: GameState, + process_id: int, + model: str, + version: int, + version_description: str, + saved_program_id: Optional[int] = None, + ): + """Create a Program object from a Policy and environment results + + Args: + policy: The Policy object to convert + agent_idx: Index of the agent in the multi-agent setup + reward: The reward from the environment step + response: The raw text response from the environment + error_occurred: Whether an error occurred during execution + + Returns: + Program object with all necessary metadata and results + """ + messages = policy.input_conversation.model_dump()["messages"] + depth = len(messages) - 2 + + # Create program from policy with environment results + program = cls( + code=policy.code, + conversation=policy.input_conversation, + response=response, + token_usage=policy.meta.total_tokens, + completion_token_usage=policy.meta.output_tokens, + prompt_token_usage=policy.meta.input_tokens, + version=version, + instance=agent_idx, + model=model, + version_description=version_description, + value=reward, + state=game_state, + meta={ + "model": model, + "process_id": process_id, + "error_occurred": error_occurred, + }, + depth=depth, + ) + if saved_program_id is not None: + program.id = saved_program_id + + return program diff --git a/fle/env/environment.py b/fle/env/environment.py index e315eff42..a608d9f5a 100644 --- a/fle/env/environment.py +++ b/fle/env/environment.py @@ -11,11 +11,18 @@ from fle.agents import Response, TaskResponse from fle.commons.models.achievements import ProductionFlows from fle.env.game.game_state import GameState -from fle.env.game import FactorioInstance -from fle.env.gym_env.action import Action -from fle.env.gym_env.observation import AgentMessage, GameInfo, Observation +# from fle.env.game import FactorioInstance +from fle.env.models.action import Action +from fle.env.models.observation import ( + AgentMessage, + GameInfo, + Observation, + ProductionFlows, + TaskResponse, +) from fle.env.tasks import TaskABC from fle.env.utils.profits import get_achievements +from fle.env.game_session import AgentSession, GameSession # need to do this since gym doesn't work with numpy>=2.0 otherwise. np.bool8 = np.dtype(np.bool) @@ -196,14 +203,15 @@ class FactorioGymEnv(gym.Env): def __init__( self, - instance: FactorioInstance, + # instance: FactorioInstance, + game_session: GameSession, task: Optional[TaskABC] = None, value_accrual_time: int = 10, error_penalty: float = 10.0, ): super().__init__() - self.instance = instance + self.game_session = game_session self.task = task self.value_accrual_time = value_accrual_time self.error_penalty = error_penalty @@ -212,7 +220,7 @@ def __init__( self.action_space = spaces.Dict( { "agent_idx": spaces.Discrete( - instance.num_agents + game_session.num_agents ), # Index of the agent taking the action "game_state": ObsSpaces.VERY_LONG_TEXT, # The game state to reset to before running code (GameState.to_raw() str) "code": ObsSpaces.LONG_TEXT, # The Python code to execute @@ -247,92 +255,6 @@ def __init__( } ) - self.current_state = None - self.initial_score = 0 - self.last_observation = None - # Track last message timestamp for each agent - self.last_message_timestamps = {i: 0.0 for i in range(instance.num_agents)} - self._last_production_flows = {} - - def get_observation( - self, agent_idx: int = 0, response: Optional[Response] = None - ) -> Observation: - """Convert the current game state into a gym observation""" - namespace = self.instance.namespaces[agent_idx] - # Get entity observations - entities = namespace.get_entities() - entity_obs = [str(e) for e in entities] - - # Get inventory observations - inventory_obs = namespace.inspect_inventory() - - # Get research observations - research_obs = namespace._save_research_state() - - # Get game info - game_info = GameInfo( - tick=self.instance.get_elapsed_ticks(), - time=self.instance.get_elapsed_ticks() / 60, - speed=self.instance._speed, - ) - - # Get flows - flows = namespace._get_production_stats() - flows_obs = ProductionFlows.from_dict(flows) - - # Get messages - messages = namespace.get_messages() - messages_obs = [] - latest_timestamp = self.last_message_timestamps[agent_idx] - - for msg in messages: - if msg["timestamp"] > self.last_message_timestamps[agent_idx]: - messages_obs.append( - AgentMessage( - sender=msg["sender"], - content=msg["message"], - timestamp=msg["timestamp"], - ) - ) - latest_timestamp = max(latest_timestamp, msg["timestamp"]) - - # Update last message timestamp - if messages_obs: - self.last_message_timestamps[agent_idx] = latest_timestamp - - # Get task verification if available - task_verification = None - if response and hasattr(response, "task"): - task_verification = TaskResponse( - success=response.task.success, - meta=response.task.meta if hasattr(response.task, "meta") else {}, - ) - - # Get serialized functions - serialized_functions = [] - for func in namespace.get_functions(): - serialized_functions.append( - {"name": func.name, "pickled_function": pickle.dumps(func).hex()} - ) - - observation = Observation( - raw_text=response.response if response else "", - entities=entity_obs, # Convert entities to strings - inventory=inventory_obs, - research=research_obs, - game_info=game_info, - score=response.score if response else 0.0, - flows=flows_obs, - task_verification=task_verification, - messages=messages_obs, - serialized_functions=serialized_functions, - ) - - # Store observation for next step - self.last_observation = observation - - return observation - def step( self, action: Action ) -> Tuple[Dict[str, Any], float, bool, bool, Dict[str, Any]]: @@ -350,40 +272,17 @@ def step( info: Additional information """ assert isinstance(action, Action) - action = action.to_dict() - agent_idx = action["agent_idx"] - code = action["code"] - game_state_raw = action["game_state"] - if game_state_raw: - self.reset_instance(GameState.parse_raw(game_state_raw)) - - namespace = self.instance.namespaces[agent_idx] - # Use last post_production_flows as pre_production_flows if available - if self._last_production_flows.get(agent_idx) is not None: - start_production_flows = ProductionFlows.from_dict( - self._last_production_flows[agent_idx] - ) - else: - start_production_flows = ProductionFlows.from_dict( - namespace._get_production_stats() - ) - initial_score, _ = namespace.score() - - # Execute the action - score, eval_time, result = self.instance.eval( - code, agent_idx=agent_idx, timeout=60 - ) - - # Check for errors - error_occurred = "error" in result.lower() or "exception: " in result.lower() + agent_session = self.game_session.agent_sessions[action.agent_idx] + eval_results = agent_session.eval(code=action.code) # Calculate reward - if error_occurred: + if eval_results.error_occurred: reward = -self.error_penalty else: # Wait for value accrual time.sleep(self.value_accrual_time) - reward = score - initial_score + reward = eval_results.score - eval_results.initial_score + reward = float(reward) # Ensure reward is always a float # Get task verification if task exists @@ -395,81 +294,48 @@ def step( task_success = self.task.verify(reward, self.instance, step_statistics={}) # Then enhance the response with task output task_response = self.task.enhance_response_with_task_output( - result, task_success + eval_results.result, task_success ) terminated = task_success.success # Get post-execution flows and calculate achievements - current_flows = ProductionFlows.from_dict(namespace._get_production_stats()) - achievements = get_achievements( - start_production_flows.__dict__, current_flows.__dict__ - ) + current_flows = eval_results.current_flows + achievements = agent_session.get_achievements(eval_results) # Store for next step - self._last_production_flows[agent_idx] = current_flows.__dict__ + self.agent_session.last_production_flows = current_flows.__dict__ # Create response object for observation response = Response( - code=f"```python\n{code}\n```", + code=f"```python\n{action.code}\n```", created_at=datetime.datetime.now(), score=reward, achievements=achievements, - step=0, + step=agent_session.steps, ticks=self.instance.get_elapsed_ticks(), - flows=start_production_flows.get_new_flows(current_flows), - response=task_response if task_response else result, + flows=eval_results.start_production_flows.get_new_flows(current_flows), + response=task_response if task_response else eval_results.result, task=task_success if task_success else TaskResponse(success=False, meta={}), - error=error_occurred, + error=eval_results.error_occurred, program_id=None, ) # Get observation for the acting agent - observation = self.get_observation(agent_idx, response) + observation = agent_session.get_observation(response) # Get additional info info = { - "error_occurred": error_occurred, - "result": result, + "error_occurred": eval_results.error_occurred, + "result": eval_results.result, "ticks": self.instance.get_elapsed_ticks(), "flows": response.flows, - "agent_idx": agent_idx, - "last_message_timestamp": self.last_message_timestamps[agent_idx], + "agent_idx": agent_session.agent_idx, + "last_message_timestamp": agent_session.last_message_timestamp, "task_verification": task_response, "output_game_state": output_game_state, } return observation.to_dict(), reward, terminated, truncated, info - def reset_instance(self, state: Optional[GameState] = None) -> None: - """Reset the Factorio instance to a given state or initial state. - - Args: - state: Optional[GameState] to reset to. If None, resets to initial state. - """ - self.instance.reset(state) - self._last_production_flows = {i: None for i in range(self.instance.num_agents)} - - def reset( - self, options: Optional[Dict[str, Any]] = None, seed: Optional[int] = None - ) -> Dict[str, Any]: - """Reset the environment to initial state - - Args: - options: dict containing 'game_state' key with Optional[GameState] value to reset to - seed: Not used - """ - if options is None: - options = {} - game_state = options.get("game_state") - self.reset_instance(game_state) - - self.initial_score, _ = self.instance.namespaces[0].score() - self.last_observation = None # Reset last observation - # Reset message timestamps - self.last_message_timestamps = {i: 0.0 for i in range(self.instance.num_agents)} - # Convert observation to dictionary to match gym standards - observation = self.get_observation(0).to_dict() - return observation, {} # Return observation for first agent - def close(self): """Clean up resources""" self.instance.cleanup() diff --git a/fle/env/game/factorio_client.py b/fle/env/game/factorio_client.py index 3dfb8ce58..576f982aa 100644 --- a/fle/env/game/factorio_client.py +++ b/fle/env/game/factorio_client.py @@ -74,8 +74,14 @@ class FactorioClient: script_dict: Dict[str, str] def __init__( - self, rcon_port: int, address: str, rcon_password: str, cache_scripts=True + self, + instance_id: int, + rcon_port: int, + address: str, + rcon_password: str, + cache_scripts=True, ): + self.instance_id = instance_id self.address = address self.rcon_port = rcon_port self.rcon_password = rcon_password diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index ceb1430a1..23ba5c180 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -19,8 +19,9 @@ from fle.env.game.factorio_client import FactorioClient from fle.env.game.game_state import GameState from fle.env.game.namespace import FactorioNamespace -from fle.env.utils.controller_loader.system_prompt_generator import \ - SystemPromptGenerator +from fle.env.utils.controller_loader.system_prompt_generator import ( + SystemPromptGenerator, +) from fle.services.rcon import _lua2python from fle.services.docker.docker_manager import ServerSettings @@ -62,10 +63,110 @@ def from_factorio_direction(cls, direction): return direction.value * 2 +class AgentInstance: + agent_idx: int + namespace: FactorioNamespace + last_message_timestamp: float + # _last_production_flow: Optional[ProductionFlows] + + def __init__(self, agent_idx: int): + self.agent_idx = agent_idx + + @property + def color(self) -> str: + """Returns a unique color string based on agent_idx""" + if self.agent_idx == 1: + return "{r=0,g=1,b=0,a=1}" # Green for agent 1 + elif self.agent_idx == 2: + return "{r=0,g=0,b=1,a=1}" # Blue for agent 2 + else: + # For additional agents, generate colors deterministically + # Use agent_idx to seed the color components while keeping them in [0,1] + r = ((self.agent_idx * 167) % 255) / 255 + g = ((self.agent_idx * 223) % 255) / 255 + b = ((self.agent_idx * 89) % 255) / 255 + return f"{{r={r:.3f},g={g:.3f},b={b:.3f},a=1}}" + + @property + def initial_position(self) -> str: + return f'{{x=0,y=({self.agent_idx-1})*2}}' + + def get_system_prompt(self) -> str: + """ + Get the system prompt for the Factorio environment. + This includes all the available actions, objects, and entities that the agent can interact with. + We get the system prompt by loading the schema, definitions, and entity definitions from their source files. + These are converted to their signatures - leaving out the implementations. + :return: + """ + execution_path = Path(os.path.dirname(os.path.realpath(__file__))) + generator = SystemPromptGenerator(str(execution_path)) + return generator.generate(self.num_agents, self.agent_idx) + + def eval_with_error(self, expr, timeout=60): + """Evaluate an expression with a timeout, and return the result without error handling""" + + def handler(signum, frame): + raise TimeoutError() + + signal.signal(signal.SIGALRM, handler) + signal.alarm(timeout) + + try: + return self.namespace.eval_with_timeout(expr) + finally: + signal.alarm(0) + + def eval(self, expr, timeout=60): + "Evaluate several lines of input, returning the result of the last line with a timeout" + try: + return self.eval_with_error(expr, timeout) + except TimeoutError: + return -1, "", "Error: Evaluation timed out" + except Exception as e: + message = e.args[0].replace("\\n", "") + return -1, "", f"{message}".strip() + + def set_inventory(self, client: FactorioClient, inventory: Dict[str, Any]): + with client.transaction() as t: + t.add_command("clear_inventory", self.agent_idx + 1) + + with client.transaction() as t: + inventory_items = {k: v for k, v in inventory.items()} + inventory_items_json = json.dumps(inventory_items) + player_idx = self.agent_idx + 1 + t.add_command( + f"/sc global.actions.initialise_inventory({player_idx}, '{inventory_items_json}')", + raw=True, + ) + + def _reset(self, client: FactorioClient, inventory: Dict[str, Any]): + with client.transaction() as t: + player_index = self.agent_idx + 1 + t.add_command( + f"/sc global.actions.regenerate_resources({player_index})", raw=True + ) + t.add_command( + f"/sc global.actions.clear_entities({player_index})", raw=True + ) + inventory_items = {k: v for k, v in inventory.items()} + inventory_items_json = json.dumps(inventory_items) + t.add_command( + f"/sc global.actions.initialise_inventory({player_index}, '{inventory_items_json}')", + raw=True, + ) + + def _agent_character_command(self) -> List[str]: + idx = self.agent_idx + 1 + return [ + f'/sc global.agent_characters[{idx}] = game.surfaces[1].create_entity{{name="character",position={self.initial_position},force=game.forces.player}}', + f'/sc global.agent_characters[{idx}].color={self.color}' + ] + class FactorioInstance: namespace_class = FactorioNamespace _cleanup_registered = False # Only register cleanup once per process - namespaces: List[FactorioNamespace] + agent_instances: List[AgentInstance] client: FactorioClient def __init__( @@ -89,10 +190,12 @@ def __init__( self._is_initialised = False self.peaceful = peaceful - self.namespaces = [self.namespace_class(self, i) for i in range(num_agents)] + self.agent_instances = [AgentInstance(i) for i in range(num_agents)] # Register controllers with the server - self.client.register_controllers(self.namespaces) + self.client.register_controllers( + [instance.namespace for instance in self.agent_instances] + ) if inventory is None: inventory = {} @@ -103,7 +206,10 @@ def __init__( self.first_namespace.score() except Exception: # Invalidate cache if there is an error - self.client.register_controllers(self.namespaces, invalidate_cache=True) + self.client.register_controllers( + [instance.namespace for instance in self.agent_instances], + invalidate_cache=True, + ) self.initialise(fast) self.initial_score, goal = self.first_namespace.score() @@ -114,8 +220,8 @@ def __init__( @property def namespace(self): - if len(self.namespaces) == 1: - return self.namespaces[0] + if len(self.agent_instances) == 1: + return self.agent_instances[0].namespace else: raise ValueError("Can only use .namespace for single-agent instances") @@ -123,7 +229,7 @@ def namespace(self): def first_namespace( self, ) -> Optional[FactorioNamespace]: # Add this property if used - return self.namespaces[0] if self.namespaces else None + return self.agent_instances[0].namespace if self.agent_instances else None @property def is_multiagent(self): @@ -142,42 +248,6 @@ def get_elapsed_ticks(self): return 0 return int(response) - def get_system_prompt(self, agent_idx: int = 0) -> str: - """ - Get the system prompt for the Factorio environment. - This includes all the available actions, objects, and entities that the agent can interact with. - We get the system prompt by loading the schema, definitions, and entity definitions from their source files. - These are converted to their signatures - leaving out the implementations. - :return: - """ - execution_path = Path(os.path.dirname(os.path.realpath(__file__))) - generator = SystemPromptGenerator(str(execution_path)) - return generator.generate(self.num_agents, agent_idx) - - def eval_with_error(self, expr, agent_idx=0, timeout=60): - """Evaluate an expression with a timeout, and return the result without error handling""" - - def handler(signum, frame): - raise TimeoutError() - - signal.signal(signal.SIGALRM, handler) - signal.alarm(timeout) - - try: - return self.namespaces[agent_idx].eval_with_timeout(expr) - finally: - signal.alarm(0) - - def eval(self, expr, agent_idx=0, timeout=60): - "Evaluate several lines of input, returning the result of the last line with a timeout" - try: - return self.eval_with_error(expr, agent_idx, timeout) - except TimeoutError: - return -1, "", "Error: Evaluation timed out" - except Exception as e: - message = e.args[0].replace("\\n", "") - return -1, "", f"{message}".strip() - def _reset_static_achievement_counters(self): """ This resets the cached production flows that we track for achievements and diversity sampling. @@ -200,33 +270,17 @@ def _reset(self, inventories: List[Dict[str, Any]]): "/sc global.alerts = {}; game.reset_game_state(); global.actions.reset_production_stats(); global.actions.regenerate_resources(1)", raw=True, ) - # self.server.add_command('/sc script.on_nth_tick(nil)', raw=True) # Remove all dangling event handlers - for i in range(self.num_agents): - player_index = i + 1 - t.add_command( - f"/sc global.actions.regenerate_resources({player_index})", raw=True - ) - # self.server.add_command('clear_inventory', player_index) with self.client.transaction() as t: t.add_command("/sc global.actions.clear_walking_queue()", raw=True) - for i in range(self.num_agents): - player_index = i + 1 - t.add_command( - f"/sc global.actions.clear_entities({player_index})", raw=True - ) - inventory_items = {k: v for k, v in inventories[i].items()} - inventory_items_json = json.dumps(inventory_items) - t.add_command( - f"/sc global.actions.initialise_inventory({player_index}, '{inventory_items_json}')", - raw=True, - ) if self.all_technologies_researched: t.add_command( "/sc global.agent_characters[1].force.research_all_technologies()", raw=True, ) + for instance in self.agent_instances: + instance._reset(self.client, inventories[instance.agent_idx]) # self.clear_entities() self._reset_static_achievement_counters() self._reset_elapsed_ticks() @@ -237,8 +291,8 @@ def reset(self, game_state: Optional[GameState] = None): not game_state or len(game_state.inventories) == self.num_agents ), "Game state must have the same number of inventories as num_agents" - for namespace in self.namespaces: - namespace.reset() + for instance in self.agent_instances: + instance.namespace.reset() if not game_state: # Reset the game instance @@ -271,14 +325,16 @@ def reset(self, game_state: Optional[GameState] = None): if game_state.agent_messages: for i in range(self.num_agents): if i < len(game_state.agent_messages): - self.namespaces[i].load_messages(game_state.agent_messages[i]) + self.agent_instances[i].namespace.load_messages( + game_state.agent_messages[i] + ) # Reset elapsed ticks self._reset_elapsed_ticks() # Load variables / functions from game state for i in range(self.num_agents): - self.namespaces[i].load(game_state.namespaces[i]) + self.agent_instances[i].namespace.load(game_state.namespaces[i]) try: self.initial_score, _ = self.first_namespace.score() @@ -289,19 +345,6 @@ def reset(self, game_state: Optional[GameState] = None): with self.client.transaction() as t: t.add_command("/sc rendering.clear()", raw=True) - def set_inventory(self, inventory: Dict[str, Any], agent_idx: int = 0): - with self.client.transaction() as t: - t.add_command("clear_inventory", agent_idx + 1) - - with self.client.transaction() as t: - inventory_items = {k: v for k, v in inventory.items()} - inventory_items_json = json.dumps(inventory_items) - player_idx = agent_idx + 1 - t.add_command( - f"/sc global.actions.initialise_inventory({player_idx}, '{inventory_items_json}')", - raw=True, - ) - def initialise(self, fast=True): with self.client.transaction() as t: t.add_command("/sc global.alerts = {}", raw=True) @@ -338,14 +381,13 @@ def _create_agent_game_characters(self): """Create Factorio characters for all agents in the game.""" # Create characters in Factorio with self.client.transaction() as t: - color_logic = "" - if self.num_agents > 1: - color_logic = "if i==1 then char.color={r=0,g=1,b=0,a=1} elseif i==2 then char.color={r=0,g=0,b=1,a=1} end;" - t.add_command( - f'/sc global.agent_characters = {{}}; for _,c in pairs(game.surfaces[1].find_entities_filtered{{type="character"}}) do if c then c.destroy() end end; for i=1,{self.num_agents} do local char = game.surfaces[1].create_entity{{name="character",position={{x=0,y=(i-1)*2}},force=game.forces.player}}; {color_logic} global.agent_characters[i]=char end', + f'/sc global.agent_characters = {{}};', raw=True, ) + for instance in self.agent_instances: + for command in instance._agent_character_command(): + t.add_command(command, raw=True) t.add_command("/sc player = global.agent_characters[1]", raw=True) def get_warnings(self, seconds=10): @@ -487,4 +529,4 @@ def run_snippet_file_in_factorio_env(self, file_path, clean=True): self.cleanup() def cleanup(self): - self.client.cleanup() \ No newline at end of file + self.client.cleanup() diff --git a/fle/env/game_session.py b/fle/env/game_session.py index 3ca79279d..299716d23 100644 --- a/fle/env/game_session.py +++ b/fle/env/game_session.py @@ -1,15 +1,373 @@ -from typing import List +import datetime +import pickle +import time +from typing import Any, Dict, List, Optional, Tuple -# from fle.env.run_envs import get_local_container_ips -from fle.env.game.instance import FactorioInstance -from fle.env.game.game_state import GameState +from a2a.types import AgentCard + +from fle.agents.models import Response from fle.commons.models.program import Program +from fle.env.a2a_instance import A2AFactorioInstance +from fle.env.a2a_namespace import A2AFactorioNamespace +from fle.env.game.config import GameConfig +from fle.env.game.factorio_client import FactorioClient +from fle.env.game.game_state import GameState +from fle.env.game.instance import FactorioInstance, AgentInstance +from fle.env.game.namespace import FactorioNamespace +from fle.env.models.action import Action +from fle.env.models.observation import ( + AgentMessage, + GameInfo, + Observation, + ProductionFlows, + TaskResponse, +) +from fle.env.utils.profits import get_achievements +from fle.services.docker.config import DockerConfig +from fle.services.docker.docker_manager import FactorioHeadlessClusterManager, FactorioHeadlessServer +from fle.services.db.db_client import DBClient, create_db_client + + +class AgentSession: + """High-level facade over a single Factorio agent instance.""" + + steps: int + namespace: FactorioNamespace | A2AFactorioNamespace + agent_instance: AgentInstance + last_message_timestamp: float + last_observation: Optional[Observation] + _last_production_flow: Optional[ProductionFlows] + version: int + version_description: str + db_client: DBClient + + class EvalResults: + def __init__(self, result: str, initial_score: float, score: float, eval_time: float): + self.result = result + self.initial_score = initial_score + self.score = score + self.eval_time = eval_time + + @property + def error_occurred(self) -> bool: + return "error" in self.result.lower() or "exception: " in self.result.lower() + + @property + def current_flows(self) -> ProductionFlows: + return ProductionFlows.from_dict(self.namespace._get_production_stats()) + + + def __init__( + self, agent_idx: int, agent_instance: AgentInstance, db_client: DBClient + ): + self.agent_idx = agent_idx + self.steps = 0 + self.agent_instance = agent_instance + self.namespace = agent_instance.namespace + self.db_client = db_client + self.reset() + + @property + def version(self) -> int: + return self.db_client.get_largest_version() + + @property + def version_description(self) -> str: + return self.db_client.get_version_description(self.version) + + def get_observation( + self, response: Optional[Response] = None + ) -> Observation: + """Convert the current game state into an observation""" + # Get entity observations + entities = self.namespace.get_entities() + entity_obs = [str(e) for e in entities] + + # Get inventory observations + inventory_obs = self.namespace.inspect_inventory() + + # Get research observations + research_obs = self.namespace._save_research_state() + + # Get game info + game_info = GameInfo( + tick=self.namespace.instance.get_elapsed_ticks(), + time=self.namespace.instance.get_elapsed_ticks() / 60, + speed=self.namespace.instance._speed, + ) -class FactorioGameSession: - instance: FactorioInstance - game_state: GameState - programs: List[Program] + # Get flows + flows = self.namespace._get_production_stats() + flows_obs = ProductionFlows.from_dict(flows) + + # Get messages + messages = self.namespace.get_messages() + messages_obs = [] + latest_timestamp = self.last_message_timestamp + + for msg in messages: + if msg["timestamp"] > self.last_message_timestamp: + messages_obs.append( + AgentMessage( + sender=msg["sender"], + content=msg["message"], + timestamp=msg["timestamp"], + ) + ) + latest_timestamp = max(latest_timestamp, msg["timestamp"]) + + # Update last message timestamp + if messages_obs: + self.last_message_timestamp = latest_timestamp + + # Get task verification if available + task_verification = None + if response and hasattr(response, "task"): + task_verification = TaskResponse( + success=response.task.success, + meta=response.task.meta if hasattr(response.task, "meta") else {}, + ) + + # Get serialized functions + serialized_functions = [] + for func in self.namespace.get_functions(): + serialized_functions.append( + {"name": func.name, "pickled_function": pickle.dumps(func).hex()} + ) + + observation = Observation( + raw_text=response.response if response else "", + entities=entity_obs, # Convert entities to strings + inventory=inventory_obs, + research=research_obs, + game_info=game_info, + score=response.score if response else 0.0, + flows=flows_obs, + task_verification=task_verification, + messages=messages_obs, + serialized_functions=serialized_functions, + ) + + # Store observation for next step + self.last_observation = observation + + return observation + + async def _initialize_trajectory_state(self, version: int, process_id: int) -> Tuple[GameState, List[int]]: + """Initialize trajectory state, either from resume or fresh start + + Returns: + Tuple of (current_state, agent_steps) + """ + current_state = None + + if version and self.db_client is None: + return None, 0 + + ( + current_state, + agent_conversation, + parent_id, + depth, + ) = await self.db_client.get_resume_state( + resume_version=version, + process_id=process_id, + agent_idx=self.agent_idx, + ) + if current_state: + self.steps = depth + + if not current_state: + current_state = self.config.task.starting_game_state + + return current_state, agent_conversation + + def action_from_code(self, code: str) -> Action: + """Create an Action object from a Policy""" + return Action( + code=code, + game_state=self.last_observation.state, + ) - def __init__(self, instance: FactorioInstance, game_state: GameState): - self.instance = instance - self.game_state = game_state + def eval(self, code: str) -> EvalResults: + if self.last_observation.state: + self.reset_instance(self.last_observation.state) + + # Use last post_production_flows as pre_production_flows if available + start_production_flows = ProductionFlows.from_dict( + self._last_production_flows.get(self.agent_idx) or self.namespace._get_production_stats() + ) + initial_score, _ = self.namespace.score() + + # Execute the action + score, eval_time, result = self.agent_instance.eval( + code, timeout=60 + ) + # return result, initial_score, score, eval_time + return self.EvalResults(result, initial_score, score, eval_time) + + def get_achievements(self, eval_results: EvalResults) -> List[str]: + return get_achievements( + self.start_production_flows.__dict__, eval_results.current_flows.__dict__ + ) + + def reset(self) -> None: + self.namespace.reset() + self.last_observation = None + self.last_message_timestamp = 0.0 + self._last_production_flow = None + + +class GameSession: + """High-level facade over a single Factorio headless server instance. + + Encapsulates: client, instance, namespaces, and access to server lifecycle + via the cluster manager. Provides convenience methods that pool low-level + usage behind a consistent API suitable for gym runners and registries. + """ + + server: FactorioHeadlessServer + instance: FactorioInstance | A2AFactorioInstance + agent_sessions: List[AgentSession] + game_state: Optional[GameState] + + def __init__( + self, + instance_id: int, + server: FactorioHeadlessServer, + ): + self.instance_id = instance_id + self.server = server + self.agent_sessions = self.create_agent_sessions() + self.game_state = None + + def set_speed(self, speed: int) -> None: + self.instance.set_speed(speed) + + def save(self, save_name: str) -> None: + # Factorio console save command (no /sc) + self.client.send_command(f"/save {save_name}") + + async def restart_with_save(self, save_name: str) -> None: + # Ensure the save exists and restart the bound container + await self._manager.server_manager.restart_with_save( + self.instance_id, save_name + ) + + def cleanup(self) -> None: + self.instance.cleanup() + + def reset_instance(self, state: Optional[GameState] = None) -> None: + """Reset the Factorio instance to a given state or initial state. + + Args: + state: Optional[GameState] to reset to. If None, resets to initial state. + """ + self.instance.reset(state) + for session in self.agent_sessions: + session.reset() + + def reset( + self, options: Optional[Dict[str, Any]] = None, seed: Optional[int] = None + ) -> Dict[str, Any]: + """Reset the environment to initial state + + Args: + options: dict containing 'game_state' key with Optional[GameState] value to reset to + seed: Not used + """ + if options is None: + options = {} + game_state = options.get("game_state") + self.reset_instance(game_state) + + for session in self.agent_sessions: + session.reset() + + # Convert observation to dictionary to match gym standards + observation = self.agent_sessions[0].get_observation().to_dict() + return observation, {} # Return observation for first agent + + +class GameSessionManager: + """Creates and manages `GameSession`s and headless servers. + + Responsibilities: + - Own the Docker-backed headless cluster lifecycle + - Construct `FactorioClient` endpoints for a given `instance_id` + - Create `FactorioInstance`/`A2AFactorioInstance` bound to that client + - Provide restart hooks for save-based flow + """ + + game_config: GameConfig + docker_config: DockerConfig + server_manager: FactorioHeadlessClusterManager + + def __init__( + self, + game_config: GameConfig, + docker_config: DockerConfig, + server_manager: FactorioHeadlessClusterManager, + ): + self.game_config = game_config + self.docker_config = docker_config + self.server_manager = server_manager + + def _make_client(self, instance_id: int) -> FactorioClient: + address = self.docker_config.address + rcon_port = self.docker_config.rcon_port + instance_id + rcon_password = self.docker_config.factorio_password + return FactorioClient( + instance_id=instance_id, + rcon_port=rcon_port, + address=address, + rcon_password=rcon_password, + cache_scripts=True, + ) + + async def start_cluster(self) -> None: + await self.server_manager.start() + + async def stop_cluster(self) -> None: + await self.server_manager.stop() + + async def restart_cluster(self) -> None: + await self.server_manager.restart() + + async def create_session( + self, + instance_id: int, + num_agents: int = 1, + agent_cards: Optional[List[AgentCard]] = None, + ) -> GameSession: + client = self._make_client(instance_id) + + common_kwargs = dict( + client=client, + fast=self.game_config.fast_mode, + all_technologies_researched=self.game_config.all_technologies_researched, + peaceful=self.game_config.peaceful, + num_agents=num_agents, + ) + + if num_agents > 1: + instance = await A2AFactorioInstance.create( + **common_kwargs, agent_cards=agent_cards + ) + else: + instance = FactorioInstance(**common_kwargs) + + # Default speed for evaluation + instance.set_speed(10) + + return GameSession( + manager=self, + instance_id=instance_id, + instance=instance, + client=client, + ) + + async def restart_session_with_save( + self, session: GameSession, save_name: str + ) -> None: + await session.restart_with_save(save_name) diff --git a/fle/env/gym_env/config.py b/fle/env/gym_env/config.py index bc730e343..6e7f384fc 100644 --- a/fle/env/gym_env/config.py +++ b/fle/env/gym_env/config.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from pydantic import BaseModel, model_validator from typing import List, Optional from fle.agents.gym_agent import GymAgent @@ -7,10 +7,8 @@ from a2a.types import AgentCard -@dataclass -class GymRunConfig: +class GymRunConfig(BaseModel): """Configuration for a single gym environment evaluation run""" - env_id: str # Gym environment ID from registry (e.g., "Factorio-iron_ore_throughput_16-v0") model: str version: Optional[int] = None @@ -19,8 +17,7 @@ class GymRunConfig: observation_formatter: Optional[BasicObservationFormatter] = None -@dataclass -class GymEvalConfig: +class GymEvalConfig(BaseModel): """Configuration for gym evaluation""" agents: List[GymAgent] @@ -31,6 +28,8 @@ class GymEvalConfig: agent_cards: Optional[List[AgentCard]] = None env_id: Optional[str] = None # Gym environment ID for registry-based creation - def __post_init__(self): + @model_validator(mode="after") + def validate_task(self): if self.task is None and hasattr(self.agents[0], "task"): self.task = self.agents[0].task + return self diff --git a/fle/env/gym_env/observation_formatter.py b/fle/env/gym_env/observation_formatter.py index 4e968c62a..ef8fa104f 100644 --- a/fle/env/gym_env/observation_formatter.py +++ b/fle/env/gym_env/observation_formatter.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from typing import Any, Dict, List, Optional -from fle.env.gym_env.observation import Observation +from fle.env.models.observation import Observation @dataclass diff --git a/fle/env/gym_env/trajectory_logger.py b/fle/env/gym_env/trajectory_logger.py index 3c8439b18..7d657b807 100644 --- a/fle/env/gym_env/trajectory_logger.py +++ b/fle/env/gym_env/trajectory_logger.py @@ -6,7 +6,7 @@ from fle.agents.gym_agent import GymAgent from fle.commons.models.program import Program -from fle.env.gym_env.observation import Observation +from fle.env.models.observation import Observation class TrajectoryLogger: diff --git a/fle/env/gym_env/action.py b/fle/env/models/action.py similarity index 99% rename from fle/env/gym_env/action.py rename to fle/env/models/action.py index 719c992d1..8e26777f4 100644 --- a/fle/env/gym_env/action.py +++ b/fle/env/models/action.py @@ -7,7 +7,7 @@ @dataclass class Action: """Action for the Factorio gym environment""" - + agent_idx: int code: str game_state: Optional[GameState] diff --git a/fle/env/gym_env/observation.py b/fle/env/models/observation.py similarity index 100% rename from fle/env/gym_env/observation.py rename to fle/env/models/observation.py diff --git a/fle/env/trajectory_runner.py b/fle/env/trajectory_runner.py index cb607f0f9..7b8b3d902 100644 --- a/fle/env/trajectory_runner.py +++ b/fle/env/trajectory_runner.py @@ -8,33 +8,34 @@ from fle.commons.models.program import Program from fle.env.environment import FactorioGymEnv from fle.env.game.game_state import GameState -from fle.env.gym_env.action import Action from fle.env.gym_env.config import GymEvalConfig -from fle.env.gym_env.observation import Observation from fle.env.gym_env.trajectory_logger import TrajectoryLogger -from fle.services.db.db_client import DBClient +from fle.env.models.action import Action +from fle.env.models.observation import Observation +from fle.env.game_session import GameSession, AgentSession class GymTrajectoryRunner: """Handles program generation and evaluation for a single trajectory in the gym environment""" - + config: GymEvalConfig + gym_env: FactorioGymEnv + process_id: int + game_session: GameSession + def __init__( self, config: GymEvalConfig, gym_env: FactorioGymEnv, process_id: int, - db_client: Optional[DBClient], + game_session: GameSession, log_dir: Optional[str] = None, ): self.config = config self.agents = config.agents self.gym_env = gym_env - self.instance = gym_env.unwrapped.instance # Get instance from gym environment - self.db_client = db_client self.process_id = process_id self.start_time = time.time() - - # Initialize trajectory logger + self.game_session = game_session self.logger = TrajectoryLogger( start_time=self.start_time, trajectory_length=self.config.task.trajectory_length, @@ -72,84 +73,17 @@ def _log_trajectory_state( self.logger.log_observation_and_program( agent, agent_idx, agent_step, observation, program ) - - async def create_program_from_policy( - self, - policy, - agent_idx: int, - reward: float, - response: str, - error_occurred: bool, - game_state: GameState, - ) -> Program: - """Create a Program object from a Policy and environment results - - Args: - policy: The Policy object to convert - agent_idx: Index of the agent in the multi-agent setup - reward: The reward from the environment step - response: The raw text response from the environment - error_occurred: Whether an error occurred during execution - - Returns: - Program object with all necessary metadata and results - """ - messages = policy.input_conversation.model_dump()["messages"] - depth = len(messages) - 2 - - # Create program from policy with environment results - program = Program( - code=policy.code, - conversation=policy.input_conversation, - response=response, - token_usage=policy.meta.total_tokens, - completion_token_usage=policy.meta.output_tokens, - prompt_token_usage=policy.meta.input_tokens, - version=self.config.version, - instance=agent_idx, - model=self.agents[agent_idx].model, - version_description=self.config.version_description, - value=reward, - state=game_state, - meta={ - "model": self.agents[agent_idx].model, - "process_id": self.process_id, - "error_occurred": error_occurred, - }, - depth=depth, - ) - if self.config.version and self.db_client is not None: - saved_program = await self.db_client.create_program(program) - program.id = saved_program.id - - return program - - async def _initialize_trajectory_state(self) -> Tuple[GameState, List[int]]: + + async def _initialize_trajectory_state(self, agent_session: AgentSession) -> Tuple[GameState, List[int]]: """Initialize trajectory state, either from resume or fresh start Returns: Tuple of (current_state, agent_steps) """ - current_state = None - agent_steps = [0] * len(self.agents) - - if self.config.version and self.db_client is not None: - for agent_idx in range(len(self.agents)): - ( - current_state, - agent_conversation, - parent_id, - depth, - ) = await self.db_client.get_resume_state( - resume_version=self.config.version, - process_id=self.process_id, - agent_idx=agent_idx, - ) - if current_state: - agent_steps[agent_idx] = depth - self.agents[agent_idx].reset(agent_conversation) - - if not current_state: + current_state, agent_conversation = await agent_session.get_resume_state() + if current_state: + self.agents[agent_idx].reset(agent_conversation) + else: current_state = self.config.task.starting_game_state self.gym_env.reset(options={"game_state": current_state}) @@ -161,14 +95,14 @@ async def _initialize_trajectory_state(self) -> Tuple[GameState, List[int]]: conversation.add_user_message(formatted_obs) agent.reset(conversation) - return current_state, agent_steps + return current_state async def run(self): """Run a single trajectory""" # Initialize state based on resume or fresh start max_steps = self.config.task.trajectory_length - current_state, agent_steps = await self._initialize_trajectory_state() + await self._initialize_trajectory_state(self.game_session.agent_sessions[0]) # Save system prompts for all agents at the start for agent_idx, agent in enumerate(self.agents): @@ -177,39 +111,43 @@ async def run(self): # Run trajectory for _, agent_idx in product(range(max_steps), range(len(self.agents))): agent = self.agents[agent_idx] + agent_session = self.game_session.agent_sessions[agent_idx] iteration_start = time.time() agent_completed = False try: # Loop while the agent is not completed yet - while not agent_completed and agent_steps[agent_idx] < max_steps: + while not agent_completed and agent_session.steps < max_steps: # Generate policy using agent's method policy = await agent.generate_policy() - agent_steps[agent_idx] += 1 + agent_session.steps += 1 if not policy: print( - f"Policy generation failed for agent {agent_idx} at iteration {agent_steps[agent_idx]}" + f"Policy generation failed for agent {agent_idx} at iteration {agent_session.steps}" ) break # Execute step in the environment - action = Action( - agent_idx=agent_idx, code=policy.code, game_state=current_state - ) + action = agent_session.action_from_code(policy.code) obs_dict, reward, terminated, truncated, info = self.gym_env.step( - action + action, + agent_session, ) observation = Observation.from_dict(obs_dict) output_game_state = info["output_game_state"] done = terminated or truncated # Create program from policy with environment results - program = await self.create_program_from_policy( - policy=policy, + program = await Program.from_policy( agent_idx=agent_idx, + policy=policy, reward=reward, response=obs_dict["raw_text"], error_occurred=info["error_occurred"], game_state=output_game_state, + process_id=self.process_id, + model=agent.model, + version=agent_session.version, + version_description=agent_session.version_description, ) # Update agent's conversation with the program and its results @@ -222,7 +160,7 @@ async def run(self): iteration_start, agent, agent_idx, - agent_steps[agent_idx], + agent_session.steps, program, observation, ) @@ -237,7 +175,7 @@ async def run(self): # Check if done and exit if configured if done and self.config.exit_on_task_success: completion_result = CompletionResult( - step=agent_steps[agent_idx], reason=CompletionReason.SUCCESS + step=agent_session.steps, reason=CompletionReason.SUCCESS ) for agent in self.agents: await agent.end(completion_result) @@ -245,6 +183,6 @@ async def run(self): except Exception as e: print( - f"Error in trajectory runner iteration {agent_steps[agent_idx]}: {e}" + f"Error in trajectory runner iteration {agent_session.steps}: {e}" ) continue diff --git a/fle/run_eval.py b/fle/run_eval.py index f39867174..3c95295fd 100644 --- a/fle/run_eval.py +++ b/fle/run_eval.py @@ -70,7 +70,6 @@ async def run_trajectory(run_idx: int, config: GymEvalConfig): runner = GymTrajectoryRunner( config=config, gym_env=gym_env, - db_client=db_client, log_dir=log_dir, process_id=run_idx, ) diff --git a/fle/services/docker/docker_manager.py b/fle/services/docker/docker_manager.py index 29b1afffb..0bf81ba30 100644 --- a/fle/services/docker/docker_manager.py +++ b/fle/services/docker/docker_manager.py @@ -6,6 +6,14 @@ from fle.services.docker.config import DockerConfig, Scenario, Mode +class FactorioHeadlessServer: + def __init__(self, config: DockerConfig, docker_platform: str, num_instances: int, dry_run: bool = False): + self.docker_platform = docker_platform + self.config = config + self.num = num_instances + self.dry_run = dry_run + self.docker = aiodocker.Docker() + class FactorioHeadlessClusterManager: def __init__( self, From a9788d66272229b897e21b1c1df3271b8d9dc7bb Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 12 Aug 2025 19:47:17 +0530 Subject: [PATCH 39/53] generic-ify --- fle/env/{game_session.py => session.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename fle/env/{game_session.py => session.py} (100%) diff --git a/fle/env/game_session.py b/fle/env/session.py similarity index 100% rename from fle/env/game_session.py rename to fle/env/session.py From 4404fea3ae7fdc2c9a06e1ed4e205c0f8d60b4d3 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 12 Aug 2025 22:56:10 +0530 Subject: [PATCH 40/53] even more changes --- fle/env/a2a_instance.py | 2 +- fle/env/game/factorio_client.py | 1 + fle/env/game/instance.py | 23 ++-- fle/env/game/tools/tool.py | 4 +- fle/env/session.py | 173 ++++++++----------------- fle/env/session_logger.py | 0 fle/env/session_manager.py | 98 ++++++++++++++ fle/env/trajectory_runner.py | 13 +- fle/services/docker/docker_manager.py | 180 +++++++++++++++++++------- 9 files changed, 315 insertions(+), 179 deletions(-) create mode 100644 fle/env/session_logger.py create mode 100644 fle/env/session_manager.py diff --git a/fle/env/a2a_instance.py b/fle/env/a2a_instance.py index 0e28afad5..13d7806a7 100644 --- a/fle/env/a2a_instance.py +++ b/fle/env/a2a_instance.py @@ -25,7 +25,7 @@ def __new__(cls, *args, **kwargs): "Direct instantiation of A2AFactorioInstance is not allowed. " "Please use A2AFactorioInstance.create() instead." ) - return super().__new__(cls) + return super().__new__(cls, *args, **kwargs) @classmethod async def create( diff --git a/fle/env/game/factorio_client.py b/fle/env/game/factorio_client.py index 576f982aa..5d5b95632 100644 --- a/fle/env/game/factorio_client.py +++ b/fle/env/game/factorio_client.py @@ -107,6 +107,7 @@ def get_rcon_client(self, address=None): def ensure_rcon_client(self): if not self.rcon_client: self.rcon_client = self.get_rcon_client() + self.rcon_client.connect() return self.rcon_client def run_rcon_print(self, command: str): diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index 23ba5c180..36807afc6 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -193,9 +193,6 @@ def __init__( self.agent_instances = [AgentInstance(i) for i in range(num_agents)] # Register controllers with the server - self.client.register_controllers( - [instance.namespace for instance in self.agent_instances] - ) if inventory is None: inventory = {} @@ -206,17 +203,16 @@ def __init__( self.first_namespace.score() except Exception: # Invalidate cache if there is an error - self.client.register_controllers( - [instance.namespace for instance in self.agent_instances], - invalidate_cache=True, - ) - self.initialise(fast) + self.initialise(fast, invalidate_cache=True) self.initial_score, goal = self.first_namespace.score() # Register the cleanup method to be called on exit (only once per process) if not FactorioInstance._cleanup_registered: atexit.register(self.cleanup) FactorioInstance._cleanup_registered = True + + def reconnect_client(self): + self.client.ensure_rcon_client() @property def namespace(self): @@ -230,6 +226,9 @@ def first_namespace( self, ) -> Optional[FactorioNamespace]: # Add this property if used return self.agent_instances[0].namespace if self.agent_instances else None + + def save(self, save_name: str) -> None: + self.client.send_command(f"/save {save_name}") @property def is_multiagent(self): @@ -345,7 +344,13 @@ def reset(self, game_state: Optional[GameState] = None): with self.client.transaction() as t: t.add_command("/sc rendering.clear()", raw=True) - def initialise(self, fast=True): + def initialise(self, fast=True, invalidate_cache=False): + self.reconnect_client() + self.client.register_controllers( + [instance.namespace for instance in self.agent_instances], + invalidate_cache=invalidate_cache, + ) + with self.client.transaction() as t: t.add_command("/sc global.alerts = {}", raw=True) t.add_command("/sc global.elapsed_ticks = 0", raw=True) diff --git a/fle/env/game/tools/tool.py b/fle/env/game/tools/tool.py index 677aa6e75..f27c499b5 100644 --- a/fle/env/game/tools/tool.py +++ b/fle/env/game/tools/tool.py @@ -10,11 +10,11 @@ class Tool(Controller): def __init__( self, factorio_server: "FactorioClient", - game_state: "FactorioNamespace", + namespace: "FactorioNamespace", *args, **kwargs, ): - super().__init__(factorio_server, game_state) + super().__init__(factorio_server, namespace) self.load() def get_position(self, position_or_entity: Union[Tuple, Position, Entity]): diff --git a/fle/env/session.py b/fle/env/session.py index 299716d23..c3491868c 100644 --- a/fle/env/session.py +++ b/fle/env/session.py @@ -24,7 +24,10 @@ ) from fle.env.utils.profits import get_achievements from fle.services.docker.config import DockerConfig -from fle.services.docker.docker_manager import FactorioHeadlessClusterManager, FactorioHeadlessServer +from fle.services.docker.docker_manager import ( + FactorioHeadlessClusterManager, + FactorioHeadlessServer, +) from fle.services.db.db_client import DBClient, create_db_client @@ -42,7 +45,9 @@ class AgentSession: db_client: DBClient class EvalResults: - def __init__(self, result: str, initial_score: float, score: float, eval_time: float): + def __init__( + self, result: str, initial_score: float, score: float, eval_time: float + ): self.result = result self.initial_score = initial_score self.score = score @@ -50,13 +55,14 @@ def __init__(self, result: str, initial_score: float, score: float, eval_time: f @property def error_occurred(self) -> bool: - return "error" in self.result.lower() or "exception: " in self.result.lower() - + return ( + "error" in self.result.lower() or "exception: " in self.result.lower() + ) + @property def current_flows(self) -> ProductionFlows: return ProductionFlows.from_dict(self.namespace._get_production_stats()) - def __init__( self, agent_idx: int, agent_instance: AgentInstance, db_client: DBClient ): @@ -66,18 +72,16 @@ def __init__( self.namespace = agent_instance.namespace self.db_client = db_client self.reset() - + @property def version(self) -> int: return self.db_client.get_largest_version() - + @property def version_description(self) -> str: return self.db_client.get_version_description(self.version) - def get_observation( - self, response: Optional[Response] = None - ) -> Observation: + def get_observation(self, response: Optional[Response] = None) -> Observation: """Convert the current game state into an observation""" # Get entity observations entities = self.namespace.get_entities() @@ -153,7 +157,9 @@ def get_observation( return observation - async def _initialize_trajectory_state(self, version: int, process_id: int) -> Tuple[GameState, List[int]]: + async def _initialize_trajectory_state( + self, version: int, process_id: int + ) -> Tuple[GameState, List[int]]: """Initialize trajectory state, either from resume or fresh start Returns: @@ -163,7 +169,7 @@ async def _initialize_trajectory_state(self, version: int, process_id: int) -> T if version and self.db_client is None: return None, 0 - + ( current_state, agent_conversation, @@ -181,28 +187,28 @@ async def _initialize_trajectory_state(self, version: int, process_id: int) -> T current_state = self.config.task.starting_game_state return current_state, agent_conversation - + def action_from_code(self, code: str) -> Action: """Create an Action object from a Policy""" return Action( + agent_idx=self.agent_idx, code=code, game_state=self.last_observation.state, ) - + def eval(self, code: str) -> EvalResults: if self.last_observation.state: self.reset_instance(self.last_observation.state) # Use last post_production_flows as pre_production_flows if available start_production_flows = ProductionFlows.from_dict( - self._last_production_flows.get(self.agent_idx) or self.namespace._get_production_stats() + self._last_production_flows.get(self.agent_idx) + or self.namespace._get_production_stats() ) initial_score, _ = self.namespace.score() # Execute the action - score, eval_time, result = self.agent_instance.eval( - code, timeout=60 - ) + score, eval_time, result = self.agent_instance.eval(code, timeout=60) # return result, initial_score, score, eval_time return self.EvalResults(result, initial_score, score, eval_time) @@ -228,34 +234,50 @@ class GameSession: server: FactorioHeadlessServer instance: FactorioInstance | A2AFactorioInstance - agent_sessions: List[AgentSession] + agent_sessions: Dict[int, AgentSession] game_state: Optional[GameState] def __init__( self, instance_id: int, + instance: FactorioInstance, server: FactorioHeadlessServer, ): self.instance_id = instance_id + self.instance = instance self.server = server - self.agent_sessions = self.create_agent_sessions() + self.agent_sessions = self._make_agent_sessions() self.game_state = None - def set_speed(self, speed: int) -> None: - self.instance.set_speed(speed) - - def save(self, save_name: str) -> None: - # Factorio console save command (no /sc) - self.client.send_command(f"/save {save_name}") + def _make_agent_sessions(self) -> Dict[int, AgentSession]: + return { + i: AgentSession(i, self.instance.agent_instances[i], self.db_client) + for i in range(self.instance.num_agents) + } + + def reinitialize_instance(self) -> None: + if isinstance(self.instance, A2AFactorioInstance): + self.instance: A2AFactorioInstance + self.instance.initialise() + else: + self.instance: FactorioInstance + self.instance.initialise() + + async def restart_from_latest_save(self) -> None: + await self.server.restart() + self.reinitialize_instance() - async def restart_with_save(self, save_name: str) -> None: - # Ensure the save exists and restart the bound container - await self._manager.server_manager.restart_with_save( - self.instance_id, save_name - ) + async def restart_from_save(self, save_name: str) -> None: + await self.server.restart(save_name) + self.reinitialize_instance() + + @property + def speed(self) -> int: + return self.instance.get_speed() - def cleanup(self) -> None: - self.instance.cleanup() + @speed.setter + def speed(self, value: int) -> None: + self.instance.set_speed(value) def reset_instance(self, state: Optional[GameState] = None) -> None: """Reset the Factorio instance to a given state or initial state. @@ -288,86 +310,5 @@ def reset( observation = self.agent_sessions[0].get_observation().to_dict() return observation, {} # Return observation for first agent - -class GameSessionManager: - """Creates and manages `GameSession`s and headless servers. - - Responsibilities: - - Own the Docker-backed headless cluster lifecycle - - Construct `FactorioClient` endpoints for a given `instance_id` - - Create `FactorioInstance`/`A2AFactorioInstance` bound to that client - - Provide restart hooks for save-based flow - """ - - game_config: GameConfig - docker_config: DockerConfig - server_manager: FactorioHeadlessClusterManager - - def __init__( - self, - game_config: GameConfig, - docker_config: DockerConfig, - server_manager: FactorioHeadlessClusterManager, - ): - self.game_config = game_config - self.docker_config = docker_config - self.server_manager = server_manager - - def _make_client(self, instance_id: int) -> FactorioClient: - address = self.docker_config.address - rcon_port = self.docker_config.rcon_port + instance_id - rcon_password = self.docker_config.factorio_password - return FactorioClient( - instance_id=instance_id, - rcon_port=rcon_port, - address=address, - rcon_password=rcon_password, - cache_scripts=True, - ) - - async def start_cluster(self) -> None: - await self.server_manager.start() - - async def stop_cluster(self) -> None: - await self.server_manager.stop() - - async def restart_cluster(self) -> None: - await self.server_manager.restart() - - async def create_session( - self, - instance_id: int, - num_agents: int = 1, - agent_cards: Optional[List[AgentCard]] = None, - ) -> GameSession: - client = self._make_client(instance_id) - - common_kwargs = dict( - client=client, - fast=self.game_config.fast_mode, - all_technologies_researched=self.game_config.all_technologies_researched, - peaceful=self.game_config.peaceful, - num_agents=num_agents, - ) - - if num_agents > 1: - instance = await A2AFactorioInstance.create( - **common_kwargs, agent_cards=agent_cards - ) - else: - instance = FactorioInstance(**common_kwargs) - - # Default speed for evaluation - instance.set_speed(10) - - return GameSession( - manager=self, - instance_id=instance_id, - instance=instance, - client=client, - ) - - async def restart_session_with_save( - self, session: GameSession, save_name: str - ) -> None: - await session.restart_with_save(save_name) + def cleanup(self) -> None: + self.instance.cleanup() diff --git a/fle/env/session_logger.py b/fle/env/session_logger.py new file mode 100644 index 000000000..e69de29bb diff --git a/fle/env/session_manager.py b/fle/env/session_manager.py new file mode 100644 index 000000000..a34e448a2 --- /dev/null +++ b/fle/env/session_manager.py @@ -0,0 +1,98 @@ +from typing import Dict, List, Optional + +from fle.env.a2a_instance import A2AFactorioInstance +from fle.env.game.config import GameConfig +from fle.env.game.factorio_client import FactorioClient +from fle.env.game.instance import FactorioInstance +from fle.env.session import AgentSession, GameSession +from fle.services.docker.config import DockerConfig +from fle.services.docker.docker_manager import FactorioHeadlessClusterManager + + +class GameSessionManager: + """A manager class for creating and managing Factorio game sessions and headless servers. + + This class is responsible for: + 1. Managing the lifecycle of Docker-backed headless server clusters + 2. Creating FactorioClient endpoints for specific instance IDs + 3. Instantiating FactorioInstance or A2AFactorioInstance objects bound to clients + 4. Providing hooks for restarting sessions from saved game states + + Attributes: + game_config (GameConfig): Configuration for the Factorio game instance + docker_config (DockerConfig): Configuration for Docker containers + server_manager (FactorioHeadlessClusterManager): Manager for headless Factorio servers + sessions (Dict[int, GameSession]): Dictionary mapping instance IDs to GameSession objects + """ + + game_config: GameConfig + docker_config: DockerConfig + server_manager: FactorioHeadlessClusterManager + sessions: Dict[int, GameSession] + + def __init__( + self, + game_config: GameConfig, + docker_config: DockerConfig, + server_manager: FactorioHeadlessClusterManager, + ): + self.game_config = game_config + self.docker_config = docker_config + self.server_manager = server_manager + + @property + def multiagent(self) -> bool: + return self.game_config.num_agents > 1 + + def get_agent_sessions(self, instance_id: int) -> Dict[int, AgentSession]: + return self.sessions[instance_id].agent_sessions + + def get_agent_session(self, instance_id: int, agent_idx: int) -> AgentSession: + return self.sessions[instance_id].agent_sessions[agent_idx] + + def _make_client(self, instance_id: int) -> FactorioClient: + server = self.server_manager.servers[instance_id] + return FactorioClient( + instance_id=instance_id, + rcon_port=server.rcon_port, + address=server.address, + rcon_password=server.rcon_password, + cache_scripts=True, + ) + + def _make_instance( + self, instance_id: int + ) -> FactorioInstance | A2AFactorioInstance: + client = self._make_client(instance_id) + if self.multiagent: + return A2AFactorioInstance( + client=client, + fast=self.game_config.fast_mode, + ) + else: + return FactorioInstance( + client=client, + fast=self.game_config.fast_mode, + ) + + def _make_session(self, instance_id: int) -> GameSession: + instance = self._make_instance(instance_id) + return GameSession( + instance_id=instance_id, + instance=instance, + server=self.server_manager.servers[instance_id], + ) + + async def start_cluster(self) -> None: + await self.server_manager.start() + + async def stop_cluster(self) -> None: + await self.server_manager.stop() + + async def restart_cluster(self) -> None: + await self.server_manager.restart() + + async def restart_session_with_save( + self, session: GameSession, save_name: str + ) -> None: + await session.restart_with_save(save_name) diff --git a/fle/env/trajectory_runner.py b/fle/env/trajectory_runner.py index 7b8b3d902..b7ed0c00f 100644 --- a/fle/env/trajectory_runner.py +++ b/fle/env/trajectory_runner.py @@ -12,7 +12,8 @@ from fle.env.gym_env.trajectory_logger import TrajectoryLogger from fle.env.models.action import Action from fle.env.models.observation import Observation -from fle.env.game_session import GameSession, AgentSession +from fle.env.session import GameSession, AgentSession +from fle.env.session_manager import GameSessionManager class GymTrajectoryRunner: @@ -20,14 +21,14 @@ class GymTrajectoryRunner: config: GymEvalConfig gym_env: FactorioGymEnv process_id: int - game_session: GameSession + session_manager: GameSessionManager def __init__( self, config: GymEvalConfig, gym_env: FactorioGymEnv, process_id: int, - game_session: GameSession, + session_manager: GameSessionManager, log_dir: Optional[str] = None, ): self.config = config @@ -35,7 +36,7 @@ def __init__( self.gym_env = gym_env self.process_id = process_id self.start_time = time.time() - self.game_session = game_session + self.session_manager = session_manager self.logger = TrajectoryLogger( start_time=self.start_time, trajectory_length=self.config.task.trajectory_length, @@ -102,7 +103,7 @@ async def run(self): # Initialize state based on resume or fresh start max_steps = self.config.task.trajectory_length - await self._initialize_trajectory_state(self.game_session.agent_sessions[0]) + await self._initialize_trajectory_state(self.session_manager.sessions[0].agent_sessions[0]) # Save system prompts for all agents at the start for agent_idx, agent in enumerate(self.agents): @@ -111,7 +112,7 @@ async def run(self): # Run trajectory for _, agent_idx in product(range(max_steps), range(len(self.agents))): agent = self.agents[agent_idx] - agent_session = self.game_session.agent_sessions[agent_idx] + agent_session = self.session_manager.sessions[0].agent_sessions[agent_idx] iteration_start = time.time() agent_completed = False try: diff --git a/fle/services/docker/docker_manager.py b/fle/services/docker/docker_manager.py index 0bf81ba30..6cb098a59 100644 --- a/fle/services/docker/docker_manager.py +++ b/fle/services/docker/docker_manager.py @@ -2,18 +2,89 @@ import asyncio import aiodocker from aiodocker.exceptions import DockerError -from typing import List +from typing import List, Dict from fle.services.docker.config import DockerConfig, Scenario, Mode + class FactorioHeadlessServer: - def __init__(self, config: DockerConfig, docker_platform: str, num_instances: int, dry_run: bool = False): - self.docker_platform = docker_platform - self.config = config - self.num = num_instances - self.dry_run = dry_run + instance_id: int + config: DockerConfig + docker: aiodocker.Docker + + def __init__( + self, + instance_id: int, + docker_config: DockerConfig, + ): + self.instance_id = instance_id + self.config = docker_config + + @property + def rcon_port(self) -> int: + return self.config.rcon_port + self.instance_id + + @property + def udp_port(self) -> int: + return self.config.udp_port + self.instance_id + + @property + def address(self) -> str: + return self.config.address + + @property + def container_name(self) -> str: + return f"{self.config.name_prefix}{self.instance_id}" + + @property + def rcon_password(self) -> str: + return self.config.factorio_password + + def connect(self): self.docker = aiodocker.Docker() + def _get_ports(self, instance_index: int) -> dict: + ports = { + f"{self.config.udp_port}/udp": [ + {"HostPort": str(self.config.udp_port + instance_index)} + ], + f"{self.config.rcon_port}/tcp": [ + {"HostPort": str(self.config.rcon_port + instance_index)} + ], + } + return ports + + def _get_save_volume(self) -> str: + return { + self.config.mods_path: {"bind": "/factorio/mods", "mode": "rw"}, + str(self.config.saves_path / str(self.instance_id)): { + "bind": "/factorio/saves", + "mode": "rw", + }, + } + + async def restart(self, save_name: str = None): + ctr = await self.docker.containers.list( + filters={"name": [f"/{self.config.name_prefix}{self.instance_id}"]} + ) + if len(ctr) == 0: + raise Exception( + f"Container {self.config.name_prefix}{self.instance_id} not found" + ) + ctr = ctr[0] + if save_name: + self._make_save_latest(save_name) + await ctr.restart() + + def _make_save_latest(self, save_name: str): + save_dir = self.config.saves_path / str(self.instance_id) + save_dir.mkdir(parents=True, exist_ok=True) + save_path = save_dir / f"{save_name}.zip" + if not save_path.exists(): + raise Exception(f"Save {save_name} not found") + save_path.touch(exist_ok=True) + + class FactorioHeadlessClusterManager: def __init__( self, @@ -29,6 +100,12 @@ def __init__( self.docker = aiodocker.Docker() self.docker_configs_raw = {} + @property + def servers(self) -> Dict[int, FactorioHeadlessServer]: + if not hasattr(self, '_servers'): + self._servers = {i: FactorioHeadlessServer(i, self.config) for i in range(self.num)} + return self._servers + async def _ensure_image(self): try: await self.docker.images.inspect(self.config.image_name) @@ -74,16 +151,6 @@ def _get_volumes(self, instance_index: int, for_server: bool = True) -> list: } # HostConfig.Binds expects ["host:container:mode", ...] return [f"{host}:{b['bind']}:{b['mode']}" for host, b in vols.items()] - - def _make_save_latest(self, instance_index: int, save_name: str): - save_dir = self.config.saves_path / str(instance_index) - save_dir.mkdir(parents=True, exist_ok=True) - save_zips = list(save_dir.glob(f"{save_name}*.zip")) - if len(save_zips) > 0: - save_zips[0].touch(exist_ok=True) - - return save_zips[0] - def _get_environment(self) -> list: env = { @@ -192,76 +259,100 @@ async def start(self): async def stop(self): # Stop & remove any container whose name starts with "factorio_" - ctrs = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) + ctrs = await self.docker.containers.list( + filters={"name": [f"/{self.config.name_prefix}"]} + ) tasks_stop = [ctr.stop() for ctr in ctrs] tasks_delete = [ctr.delete() for ctr in ctrs] await asyncio.gather(*tasks_stop) await asyncio.gather(*tasks_delete) async def restart(self): - ctrs = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) + ctrs = await self.docker.containers.list( + filters={"name": [f"/{self.config.name_prefix}"]} + ) tasks = [ctr.restart() for ctr in ctrs] await asyncio.gather(*tasks) - async def restart_with_save(self, instance_index: int, save_name: str): - ctr = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}{instance_index}"]}) - if len(ctr) == 0: - raise ValueError(f"Container {self.config.name_prefix}{instance_index} not found") - ctr = ctr[0] - self._make_save_latest(instance_index, save_name) - await ctr.restart() - async def hot_reload_scenario(self): # Sync scenario files into the server's temp directory for hot-reload - containers = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) - + containers = await self.docker.containers.list( + filters={"name": [f"/{self.config.name_prefix}"]} + ) + async def sync_container(ctr): cmd = ( f"docker exec -u root {ctr.id} sh -c " f"'cp -a /factorio/scenarios/{self.config.scenario_name}/. {self.config.temp_playing_dir} && " f"chown -R factorio:factorio {self.config.temp_playing_dir}'" ) - + proc = await asyncio.create_subprocess_shell( - cmd, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE + cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) stdout, stderr = await proc.communicate() - + if stdout: print(f"stdout: {stdout.decode()}") if stderr: print(f"stderr: {stderr.decode()}") print(f"Container {ctr.id} sync complete (exit code: {proc.returncode})") - + # Run all container syncs concurrently await asyncio.gather(*[sync_container(ctr) for ctr in containers]) print("Hot-reload sync complete.") - + async def attach_docker_configs(self): """Attach docker config to FactorioClusterManager""" - containers = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) + containers = await self.docker.containers.list( + filters={"name": [f"/{self.config.name_prefix}"]} + ) container_ids = [ctr.id for ctr in containers] if not container_ids or container_ids[0] == "": print("No running Factorio containers found") return tasks = [ctr.show() for ctr in containers] container_infos = await asyncio.gather(*tasks) - info_to_keep = ["Id", "Name", "State", "Image", "HostConfig", "Config", "LogPath", "RestartCount", "Mounts"] - hostconfig_to_keep = ["PortBindings", "Binds", "Memory", "RestartPolicy", "NetworkMode"] + info_to_keep = [ + "Id", + "Name", + "State", + "Image", + "HostConfig", + "Config", + "LogPath", + "RestartCount", + "Mounts", + ] + hostconfig_to_keep = [ + "PortBindings", + "Binds", + "Memory", + "RestartPolicy", + "NetworkMode", + ] config_to_keep = ["Env", "Entrypoint", "Cmd", "WorkingDir"] network_to_keep = ["Ports", "IPAddress", "Gateway"] for container_info in container_infos: - self.docker_configs_raw[container_info["Name"]] = {k: container_info[k] for k in info_to_keep} - self.docker_configs_raw[container_info["Name"]]["Config"] = {k: container_info["Config"][k] for k in config_to_keep} - self.docker_configs_raw[container_info["Name"]]["NetworkSettings"] = {k: container_info["NetworkSettings"][k] for k in network_to_keep} - self.docker_configs_raw[container_info["Name"]]["HostConfig"] = {k: container_info["HostConfig"][k] for k in hostconfig_to_keep} + self.docker_configs_raw[container_info["Name"]] = { + k: container_info[k] for k in info_to_keep + } + self.docker_configs_raw[container_info["Name"]]["Config"] = { + k: container_info["Config"][k] for k in config_to_keep + } + self.docker_configs_raw[container_info["Name"]]["NetworkSettings"] = { + k: container_info["NetworkSettings"][k] for k in network_to_keep + } + self.docker_configs_raw[container_info["Name"]]["HostConfig"] = { + k: container_info["HostConfig"][k] for k in hostconfig_to_keep + } async def get_local_container_ips(self) -> tuple[List[str], List[int], List[int]]: """Get IP addresses of running Factorio containers in the local Docker setup.""" # Get container IDs for factorio containers - containers = await self.docker.containers.list(filters={"name": [f"/{self.config.name_prefix}"]}) + containers = await self.docker.containers.list( + filters={"name": [f"/{self.config.name_prefix}"]} + ) container_ids = [ctr.id for ctr in containers] if not container_ids or container_ids[0] == "": @@ -273,7 +364,7 @@ async def get_local_container_ips(self) -> tuple[List[str], List[int], List[int] tcp_ports = [] tasks = [ctr.show() for ctr in containers] container_infos = await asyncio.gather(*tasks) - + for container_info in container_infos: ports = container_info["NetworkSettings"]["Ports"] @@ -371,6 +462,5 @@ async def main(): await mgr.docker.close() - if __name__ == "__main__": asyncio.run(main()) From 8796a7040e9abf3fc5100244ac804950827f2cd3 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 12 Aug 2025 22:57:42 +0530 Subject: [PATCH 41/53] renaming factorio_server & namespace --- fle/env/game/tools/admin/render/client.py | 2 +- .../tools/agent/connect_entities/client.py | 18 +++++++++--------- fle/env/game/tools/agent/craft_item/client.py | 4 ++-- .../tools/agent/harvest_resource/client.py | 12 ++++++------ fle/env/game/tools/agent/move_to/client.py | 12 ++++++------ .../game/tools/agent/place_entity/client.py | 4 ++-- .../tools/agent/place_entity_next_to/client.py | 2 +- .../game/tools/agent/send_message/client.py | 2 +- fle/env/game/tools/controller.py | 16 ++++++++-------- fle/env/game/tools/init.py | 2 +- fle/env/game/tools/tool.py | 6 +++--- 11 files changed, 40 insertions(+), 40 deletions(-) diff --git a/fle/env/game/tools/admin/render/client.py b/fle/env/game/tools/admin/render/client.py index 0c25e0564..bc8c86057 100644 --- a/fle/env/game/tools/admin/render/client.py +++ b/fle/env/game/tools/admin/render/client.py @@ -99,7 +99,7 @@ def __call__( if position is None and bounding_box is None: # Get player position from game state position = Position(0, 0) # Default fallback - player_data = self.game_state.get("player", {}) + player_data = self.namespace.get("player", {}) if "position" in player_data: position = Position( player_data["position"]["x"], player_data["position"]["y"] diff --git a/fle/env/game/tools/agent/connect_entities/client.py b/fle/env/game/tools/agent/connect_entities/client.py index 9b3b05033..f102919c7 100644 --- a/fle/env/game/tools/agent/connect_entities/client.py +++ b/fle/env/game/tools/agent/connect_entities/client.py @@ -57,18 +57,18 @@ def __init__(self, connection, game_state): self._setup_resolvers() def _setup_actions(self): - self.request_path = RequestPath(self.factorio_server, self.game_state) - self.get_path = GetPath(self.factorio_server, self.game_state) - self.rotate_entity = RotateEntity(self.factorio_server, self.game_state) - self.pickup_entity = PickupEntity(self.factorio_server, self.game_state) - self.inspect_inventory = InspectInventory(self.factorio_server, self.game_state) - self.get_entities = GetEntities(self.factorio_server, self.game_state) - self.get_entity = GetEntity(self.factorio_server, self.game_state) + self.request_path = RequestPath(self.factorio_client, self.namespace) + self.get_path = GetPath(self.factorio_client, self.namespace) + self.rotate_entity = RotateEntity(self.factorio_client, self.namespace) + self.pickup_entity = PickupEntity(self.factorio_client, self.namespace) + self.inspect_inventory = InspectInventory(self.factorio_client, self.namespace) + self.get_entities = GetEntities(self.factorio_client, self.namespace) + self.get_entity = GetEntity(self.factorio_client, self.namespace) self._extend_collision_boxes = ExtendCollisionBoxes( - self.factorio_server, self.game_state + self.factorio_client, self.namespace ) self._clear_collision_boxes = ClearCollisionBoxes( - self.factorio_server, self.game_state + self.factorio_client, self.namespace ) def _setup_resolvers(self): diff --git a/fle/env/game/tools/agent/craft_item/client.py b/fle/env/game/tools/agent/craft_item/client.py index 3bf6f1cd0..235a62673 100644 --- a/fle/env/game/tools/agent/craft_item/client.py +++ b/fle/env/game/tools/agent/craft_item/client.py @@ -24,7 +24,7 @@ def __call__(self, entity: Prototype, quantity: int = 1) -> int: name = entity count_in_inventory = 0 - if not self.game_state.instance.fast: + if not self.namespace.instance.fast: count_in_inventory = self.inspect_inventory()[entity] success, elapsed = self.execute(self.player_index, name, quantity) @@ -37,7 +37,7 @@ def __call__(self, entity: Prototype, quantity: int = 1) -> int: result = self.get_error_message(success) raise Exception(result) - if not self.game_state.instance.fast: + if not self.namespace.instance.fast: sleep(0.5) attempt = 0 max_attempts = 10 diff --git a/fle/env/game/tools/agent/harvest_resource/client.py b/fle/env/game/tools/agent/harvest_resource/client.py index 97741538f..a9a2d263d 100644 --- a/fle/env/game/tools/agent/harvest_resource/client.py +++ b/fle/env/game/tools/agent/harvest_resource/client.py @@ -33,7 +33,7 @@ def __call__(self, position: Position, quantity=1, radius=10) -> int: # If not fast mode, we need to identify what resource is at the x, y position # Because if the first pass of the harvest doesn't get the necessary resource_to_harvest = Resource.IronOre - if not self.game_state.instance.fast: + if not self.namespace.instance.fast: resource_to_harvest = self.get_resource_type_at_position(position) # Now we attempt to harvest. @@ -45,15 +45,15 @@ def __call__(self, position: Position, quantity=1, radius=10) -> int: raise Exception(f"Could not harvest. {msg}") # If `fast` is turned off - we need to long poll the game state to ensure the player has moved - if not self.game_state.instance.fast: - remaining_steps = self.factorio_server.run_rcon_print( + if not self.namespace.instance.fast: + remaining_steps = self.factorio_client.run_rcon_print( f"global.actions.get_harvest_queue_length({self.player_index})" ) attempt = 0 max_attempts = 10 while remaining_steps != "0" and attempt < max_attempts: sleep(0.5) - remaining_steps = self.factorio_server.run_rcon_print( + remaining_steps = self.factorio_client.run_rcon_print( f"global.actions.get_harvest_queue_length({self.player_index})" ) @@ -62,7 +62,7 @@ def __call__(self, position: Position, quantity=1, radius=10) -> int: while int(response) < quantity and attempt < max_attempts: nearest_resource = self.nearest(resource_to_harvest) - if not nearest_resource.is_close(self.game_state.player_location, 2): + if not nearest_resource.is_close(self.namespace.player_location, 2): self.move_to(nearest_resource) try: @@ -80,7 +80,7 @@ def __call__(self, position: Position, quantity=1, radius=10) -> int: def get_resource_type_at_position(self, position: Position): x, y = self.get_position(position) - entity_at_position = self.factorio_server.run_rcon_print( + entity_at_position = self.factorio_client.run_rcon_print( f"global.actions.get_resource_name_at_position({self.player_index}, {x}, {y})" ) if entity_at_position.startswith("tree"): diff --git a/fle/env/game/tools/agent/move_to/client.py b/fle/env/game/tools/agent/move_to/client.py index 80649e612..b9e7ab805 100644 --- a/fle/env/game/tools/agent/move_to/client.py +++ b/fle/env/game/tools/agent/move_to/client.py @@ -36,7 +36,7 @@ def __call__( path_handle = self.request_path( start=Position( - x=self.game_state.player_location.x, y=self.game_state.player_location.y + x=self.namespace.player_location.x, y=self.namespace.player_location.y ), finish=nposition, allow_paths_through_own_entities=True, @@ -66,21 +66,21 @@ def __call__( raise Exception("Could not lay entity, perhaps a typo?") if response and isinstance(response, dict): - self.game_state.player_location = Position( + self.namespace.player_location = Position( x=response["x"], y=response["y"] ) # If `fast` is turned off - we need to long poll the game state to ensure the player has moved - if not self.game_state.instance.fast: - remaining_steps = self.factorio_server.run_rcon_print( + if not self.namespace.instance.fast: + remaining_steps = self.factorio_client.run_rcon_print( f"global.actions.get_walking_queue_length({self.player_index})" ) while remaining_steps != "0": sleep(0.5) - remaining_steps = self.factorio_server.run_rcon_print( + remaining_steps = self.factorio_client.run_rcon_print( f"global.actions.get_walking_queue_length({self.player_index})" ) - self.game_state.player_location = Position(x=position.x, y=position.y) + self.namespace.player_location = Position(x=position.x, y=position.y) return Position(x=response["x"], y=response["y"]) # , execution_time except Exception as e: diff --git a/fle/env/game/tools/agent/place_entity/client.py b/fle/env/game/tools/agent/place_entity/client.py index 94e606208..a064f1355 100644 --- a/fle/env/game/tools/agent/place_entity/client.py +++ b/fle/env/game/tools/agent/place_entity/client.py @@ -69,7 +69,7 @@ def __call__( raise Exception(f"Could not place {name} at ({x}, {y})", e) # If we are in `slow` mode, there is a delay between placing the entity and the entity being created - if not self.game_state.instance.fast: + if not self.namespace.instance.fast: sleep(1) return self.get_entity(entity, position) else: @@ -90,7 +90,7 @@ def __call__( try: object = metaclass( - prototype=entity.name, game=self.factorio_server, **cleaned_response + prototype=entity.name, game=self.factorio_client, **cleaned_response ) except Exception as e: raise Exception( diff --git a/fle/env/game/tools/agent/place_entity_next_to/client.py b/fle/env/game/tools/agent/place_entity_next_to/client.py index f6a9b79fe..6c9ecede7 100644 --- a/fle/env/game/tools/agent/place_entity_next_to/client.py +++ b/fle/env/game/tools/agent/place_entity_next_to/client.py @@ -54,7 +54,7 @@ def __call__( try: object = metaclass( - prototype=name, game=self.factorio_server, **cleaned_response + prototype=name, game=self.factorio_client, **cleaned_response ) except Exception as e: raise Exception( diff --git a/fle/env/game/tools/agent/send_message/client.py b/fle/env/game/tools/agent/send_message/client.py index b6840af05..2f58ec8c6 100644 --- a/fle/env/game/tools/agent/send_message/client.py +++ b/fle/env/game/tools/agent/send_message/client.py @@ -32,7 +32,7 @@ def __call__( :return: True if message was sent successfully, False otherwise (e.g. on timeout) """ - if not self.game_state.instance.is_multiagent: + if not self.namespace.instance.is_multiagent: logging.info("SendMessage: Skipping message in single agent mode") return True diff --git a/fle/env/game/tools/controller.py b/fle/env/game/tools/controller.py index ad057e014..f85d592ee 100644 --- a/fle/env/game/tools/controller.py +++ b/fle/env/game/tools/controller.py @@ -16,16 +16,16 @@ class Controller: def __init__( self, - factorio_server: "FactorioClient", - game_state: "FactorioNamespace", + factorio_client: "FactorioClient", + namespace: "FactorioNamespace", *args, **kwargs, ): - self.factorio_server = factorio_server - self.game_state = game_state + self.factorio_client = factorio_client + self.namespace = namespace self.name = self.camel_to_snake(self.__class__.__name__) self.player_index = ( - game_state.agent_index + 1 + namespace.agent_index + 1 ) # +1 because Factorio is 1-indexed def clean_response(self, response): @@ -136,7 +136,7 @@ def execute(self, *args) -> Tuple[Dict, Any]: parameters = [lua.encode(arg) for arg in args] invocation = f"pcall(global.actions.{self.name}{(', ' if parameters else '') + ','.join(parameters)})" wrapped = f"{COMMAND} a, b = {invocation}; rcon.print(dump({{a=a, b=b}}))" - lua_response = self.factorio_server.send_command(wrapped) + lua_response = self.factorio_client.send_command(wrapped) parsed, elapsed = _lua2python(invocation, lua_response, start=start) if parsed is None: @@ -165,7 +165,7 @@ def execute2(self, *args) -> Tuple[Dict, Any]: parameters = [lua.encode(arg) for arg in args] invocation = f"pcall(global.actions.{self.name}{(', ' if parameters else '') + ','.join(parameters)})" wrapped = f"{COMMAND} a, b = {invocation}; rcon.print(dump({{a=a, b=b}}))" - lua_response = self.factorio_server.rcon_client.send_command(wrapped) + lua_response = self.factorio_client.rcon_client.send_command(wrapped) parsed, elapsed = _lua2python(invocation, lua_response, start=start) if not parsed["a"] and "b" in parsed and isinstance(parsed["b"], str): parts = lua_response.split('["b"] = ') @@ -191,6 +191,6 @@ def execute2(self, *args) -> Tuple[Dict, Any]: def send(self, command, *parameters, trace=False) -> List[str]: start = timer() script = self._get_command(command, parameters=list(parameters), measured=False) - lua_response = self.factorio_server.send_command(script) + lua_response = self.factorio_client.send_command(script) # print(lua_response) return _lua2python(command, lua_response, start=start) diff --git a/fle/env/game/tools/init.py b/fle/env/game/tools/init.py index 5f4fe890f..6c9f2d805 100644 --- a/fle/env/game/tools/init.py +++ b/fle/env/game/tools/init.py @@ -14,4 +14,4 @@ def __init__( self.load() def load(self): - self.factorio_server.load_init_into_game(self.name) + self.factorio_client.load_init_into_game(self.name) diff --git a/fle/env/game/tools/tool.py b/fle/env/game/tools/tool.py index f27c499b5..ce94a29f6 100644 --- a/fle/env/game/tools/tool.py +++ b/fle/env/game/tools/tool.py @@ -9,12 +9,12 @@ class Tool(Controller): def __init__( self, - factorio_server: "FactorioClient", + factorio_client: "FactorioClient", namespace: "FactorioNamespace", *args, **kwargs, ): - super().__init__(factorio_server, namespace) + super().__init__(factorio_client, namespace) self.load() def get_position(self, position_or_entity: Union[Tuple, Position, Entity]): @@ -43,4 +43,4 @@ def get_error_message(self, response): return response def load(self): - self.factorio_server.load_tool_into_game(self.name) + self.factorio_client.load_tool_into_game(self.name) From 35aa145aa3a85ebf8409b6da8bd27eeadd47c96a Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 13 Aug 2025 14:13:41 +0530 Subject: [PATCH 42/53] snapshot driven eval --- fle/env/environment.py | 82 ++++------ fle/env/game/instance.py | 4 +- fle/env/game/namespace.py | 1 + fle/env/game/tools/controller.py | 4 +- fle/env/models/observation.py | 17 ++- fle/env/session.py | 251 +++++++++++++++++++++++-------- 6 files changed, 237 insertions(+), 122 deletions(-) diff --git a/fle/env/environment.py b/fle/env/environment.py index a608d9f5a..8e08812f9 100644 --- a/fle/env/environment.py +++ b/fle/env/environment.py @@ -11,6 +11,7 @@ from fle.agents import Response, TaskResponse from fle.commons.models.achievements import ProductionFlows from fle.env.game.game_state import GameState + # from fle.env.game import FactorioInstance from fle.env.models.action import Action from fle.env.models.observation import ( @@ -22,7 +23,7 @@ ) from fle.env.tasks import TaskABC from fle.env.utils.profits import get_achievements -from fle.env.game_session import AgentSession, GameSession +from fle.env.session import AgentSession, GameSession # need to do this since gym doesn't work with numpy>=2.0 otherwise. np.bool8 = np.dtype(np.bool) @@ -203,16 +204,13 @@ class FactorioGymEnv(gym.Env): def __init__( self, - # instance: FactorioInstance, game_session: GameSession, - task: Optional[TaskABC] = None, value_accrual_time: int = 10, error_penalty: float = 10.0, ): super().__init__() self.game_session = game_session - self.task = task self.value_accrual_time = value_accrual_time self.error_penalty = error_penalty @@ -272,70 +270,48 @@ def step( info: Additional information """ assert isinstance(action, Action) - agent_session = self.game_session.agent_sessions[action.agent_idx] - eval_results = agent_session.eval(code=action.code) + agent_idx = action.agent_idx + game_result = self.game_session.eval_agent_with_snapshot( + agent_idx=agent_idx, + code=action.code, + value_accrual_time=self.value_accrual_time, + ) # Calculate reward - if eval_results.error_occurred: - reward = -self.error_penalty - else: - # Wait for value accrual - time.sleep(self.value_accrual_time) - reward = eval_results.score - eval_results.initial_score - + reward = ( + -self.error_penalty + if game_result.error_occurred + else game_result.score_delta + ) reward = float(reward) # Ensure reward is always a float # Get task verification if task exists - task_response = task_success = None + task_response = None terminated = truncated = False - output_game_state = GameState.from_instance(self.instance) - if self.task: - # First get the raw verification - task_success = self.task.verify(reward, self.instance, step_statistics={}) - # Then enhance the response with task output - task_response = self.task.enhance_response_with_task_output( - eval_results.result, task_success - ) - terminated = task_success.success - - # Get post-execution flows and calculate achievements - current_flows = eval_results.current_flows - achievements = agent_session.get_achievements(eval_results) - # Store for next step - self.agent_session.last_production_flows = current_flows.__dict__ - - # Create response object for observation - response = Response( - code=f"```python\n{action.code}\n```", - created_at=datetime.datetime.now(), - score=reward, - achievements=achievements, - step=agent_session.steps, - ticks=self.instance.get_elapsed_ticks(), - flows=eval_results.start_production_flows.get_new_flows(current_flows), - response=task_response if task_response else eval_results.result, - task=task_success if task_success else TaskResponse(success=False, meta={}), - error=eval_results.error_occurred, - program_id=None, - ) + + if self.game_session.task: + game_result = self.game_session.verify_task(reward, game_result) + terminated = task_response.success # Get observation for the acting agent - observation = agent_session.get_observation(response) + observation = game_result.partial_observation.add_response( + game_result.result, reward, task_response + ) # Get additional info info = { - "error_occurred": eval_results.error_occurred, - "result": eval_results.result, - "ticks": self.instance.get_elapsed_ticks(), - "flows": response.flows, - "agent_idx": agent_session.agent_idx, - "last_message_timestamp": agent_session.last_message_timestamp, + "error_occurred": game_result.agent_result.error_occurred, + "result": game_result.result, + "ticks": game_result.post.game_info.tick, + "flows": game_result.agent_result.flows_delta, + "agent_idx": agent_idx, + "last_message_timestamp": observation.messages[-1].timestamp, "task_verification": task_response, - "output_game_state": output_game_state, + "output_game_state": game_result.post.game_state, } return observation.to_dict(), reward, terminated, truncated, info def close(self): """Clean up resources""" - self.instance.cleanup() + self.game_session.cleanup() diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index 36807afc6..921a2fd0e 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -91,7 +91,7 @@ def color(self) -> str: def initial_position(self) -> str: return f'{{x=0,y=({self.agent_idx-1})*2}}' - def get_system_prompt(self) -> str: + def get_system_prompt(self, num_agents: int = 1) -> str: """ Get the system prompt for the Factorio environment. This includes all the available actions, objects, and entities that the agent can interact with. @@ -101,7 +101,7 @@ def get_system_prompt(self) -> str: """ execution_path = Path(os.path.dirname(os.path.realpath(__file__))) generator = SystemPromptGenerator(str(execution_path)) - return generator.generate(self.num_agents, self.agent_idx) + return generator.generate(num_agents, self.agent_idx) def eval_with_error(self, expr, timeout=60): """Evaluate an expression with a timeout, and return the result without error handling""" diff --git a/fle/env/game/namespace.py b/fle/env/game/namespace.py index a462e507f..51184adb5 100644 --- a/fle/env/game/namespace.py +++ b/fle/env/game/namespace.py @@ -65,6 +65,7 @@ def __init__(self, instance, agent_index): self.log_counter = 0 self.player_location = ent.Position(x=0, y=0) self.agent_index = agent_index + assert agent_index >= 0, "Agent index must be non-negative" self.agent_id = str(agent_index) self.loop_context = LoopContext() diff --git a/fle/env/game/tools/controller.py b/fle/env/game/tools/controller.py index f85d592ee..59db47b00 100644 --- a/fle/env/game/tools/controller.py +++ b/fle/env/game/tools/controller.py @@ -16,8 +16,8 @@ class Controller: def __init__( self, - factorio_client: "FactorioClient", - namespace: "FactorioNamespace", + factorio_client: FactorioClient, + namespace: FactorioNamespace, *args, **kwargs, ): diff --git a/fle/env/models/observation.py b/fle/env/models/observation.py index 04e6a8527..413ae86bd 100644 --- a/fle/env/models/observation.py +++ b/fle/env/models/observation.py @@ -5,8 +5,9 @@ from fle.commons.models.technology_state import TechnologyState from fle.commons.models.research_state import ResearchState from fle.commons.models.achievements import ProductionFlows -from fle.agents import TaskResponse +from fle.agents import TaskResponse, Response from fle.env.game.entities import Inventory +# from fle.env.game.response import Response @dataclass @@ -231,3 +232,17 @@ def to_dict(self) -> Dict[str, Any]: ], "serialized_functions": self.serialized_functions, } + + def add_response(self, raw_text: str, score: float, task_verification: Optional[TaskResponse]) -> "Observation": + return Observation( + raw_text=raw_text, + entities=self.entities, + inventory=self.inventory, + research=self.research, + game_info=self.game_info, + score=score, + flows=self.flows, + task_verification=task_verification, + messages=self.messages, + serialized_functions=self.serialized_functions + ) \ No newline at end of file diff --git a/fle/env/session.py b/fle/env/session.py index c3491868c..7c86d753f 100644 --- a/fle/env/session.py +++ b/fle/env/session.py @@ -1,4 +1,5 @@ import datetime +from dataclasses import dataclass import pickle import time from typing import Any, Dict, List, Optional, Tuple @@ -23,35 +24,39 @@ TaskResponse, ) from fle.env.utils.profits import get_achievements -from fle.services.docker.config import DockerConfig -from fle.services.docker.docker_manager import ( - FactorioHeadlessClusterManager, - FactorioHeadlessServer, -) -from fle.services.db.db_client import DBClient, create_db_client +from fle.services.docker.docker_manager import FactorioHeadlessServer +from fle.services.db.db_client import DBClient +from fle.env.tasks import TaskABC class AgentSession: """High-level facade over a single Factorio agent instance.""" - steps: int - namespace: FactorioNamespace | A2AFactorioNamespace - agent_instance: AgentInstance - last_message_timestamp: float - last_observation: Optional[Observation] - _last_production_flow: Optional[ProductionFlows] - version: int - version_description: str - db_client: DBClient + @dataclass + class Snapshot: + score: float + flows: ProductionFlows + tick: int + timestamp: float + observation: Optional[Observation] = None + + class AgentResult: + pre: "AgentSession.Snapshot" + post: Optional["AgentSession.Snapshot"] + result: str - class EvalResults: def __init__( - self, result: str, initial_score: float, score: float, eval_time: float + self, + pre: "AgentSession.Snapshot", + post: Optional["AgentSession.Snapshot"], + result: str, ): + self.pre = pre + self.post = post self.result = result - self.initial_score = initial_score - self.score = score - self.eval_time = eval_time + + def set_post(self, post: "AgentSession.Snapshot") -> None: + self.post = post @property def error_occurred(self) -> bool: @@ -60,11 +65,28 @@ def error_occurred(self) -> bool: ) @property - def current_flows(self) -> ProductionFlows: - return ProductionFlows.from_dict(self.namespace._get_production_stats()) + def score_delta(self) -> float: + return self.post.score - self.pre.score + + @property + def flows_delta(self) -> ProductionFlows: + return self.post.flows.get_new_flows(self.pre.flows) + + steps: int + namespace: FactorioNamespace | A2AFactorioNamespace + agent_instance: AgentInstance + last_message_timestamp: float + last_observation: Optional[Observation] + _last_production_flow: Optional[ProductionFlows] + version: int + version_description: str + db_client: DBClient def __init__( - self, agent_idx: int, agent_instance: AgentInstance, db_client: DBClient + self, + agent_idx: int, + agent_instance: AgentInstance, + db_client: DBClient, ): self.agent_idx = agent_idx self.steps = 0 @@ -77,11 +99,10 @@ def __init__( def version(self) -> int: return self.db_client.get_largest_version() - @property - def version_description(self) -> str: - return self.db_client.get_version_description(self.version) - - def get_observation(self, response: Optional[Response] = None) -> Observation: + def get_partial_observation( + self, + game_info: GameInfo, + ) -> Observation: """Convert the current game state into an observation""" # Get entity observations entities = self.namespace.get_entities() @@ -93,13 +114,6 @@ def get_observation(self, response: Optional[Response] = None) -> Observation: # Get research observations research_obs = self.namespace._save_research_state() - # Get game info - game_info = GameInfo( - tick=self.namespace.instance.get_elapsed_ticks(), - time=self.namespace.instance.get_elapsed_ticks() / 60, - speed=self.namespace.instance._speed, - ) - # Get flows flows = self.namespace._get_production_stats() flows_obs = ProductionFlows.from_dict(flows) @@ -124,14 +138,6 @@ def get_observation(self, response: Optional[Response] = None) -> Observation: if messages_obs: self.last_message_timestamp = latest_timestamp - # Get task verification if available - task_verification = None - if response and hasattr(response, "task"): - task_verification = TaskResponse( - success=response.task.success, - meta=response.task.meta if hasattr(response.task, "meta") else {}, - ) - # Get serialized functions serialized_functions = [] for func in self.namespace.get_functions(): @@ -140,14 +146,14 @@ def get_observation(self, response: Optional[Response] = None) -> Observation: ) observation = Observation( - raw_text=response.response if response else "", + raw_text="", entities=entity_obs, # Convert entities to strings inventory=inventory_obs, research=research_obs, game_info=game_info, - score=response.score if response else 0.0, + score=0.0, flows=flows_obs, - task_verification=task_verification, + task_verification=None, messages=messages_obs, serialized_functions=serialized_functions, ) @@ -196,23 +202,49 @@ def action_from_code(self, code: str) -> Action: game_state=self.last_observation.state, ) - def eval(self, code: str) -> EvalResults: + @property + def current_score(self) -> float: + return self.namespace.score()[0] + + @property + def current_production_flows(self) -> ProductionFlows: + """Get the current production flows""" + return ProductionFlows.from_dict(self.namespace._get_production_stats()) + + def _snapshot(self, observation: Optional[Observation] = None) -> Snapshot: + return self.Snapshot( + score=self.current_score, + flows=self.current_production_flows, + tick=self.namespace.instance.get_elapsed_ticks(), + timestamp=time.time(), + observation=observation, + ) + + def eval_with_snapshot(self, code: str, value_accrual_time: float = 0) -> AgentResult: if self.last_observation.state: self.reset_instance(self.last_observation.state) - # Use last post_production_flows as pre_production_flows if available - start_production_flows = ProductionFlows.from_dict( - self._last_production_flows.get(self.agent_idx) - or self.namespace._get_production_stats() - ) - initial_score, _ = self.namespace.score() + initial_snapshot = self._snapshot() # Execute the action score, eval_time, result = self.agent_instance.eval(code, timeout=60) - # return result, initial_score, score, eval_time - return self.EvalResults(result, initial_score, score, eval_time) - def get_achievements(self, eval_results: EvalResults) -> List[str]: + eval_results = self.AgentResult( + pre=initial_snapshot, + result=result, + ) + + if eval_results.error_occurred: + eval_results.set_post(initial_snapshot) + return eval_results + + time.sleep(value_accrual_time) + + eval_results.set_post(self._snapshot()) + + return eval_results + + def get_achievements(self, eval_results: AgentResult) -> List[str]: return get_achievements( self.start_production_flows.__dict__, eval_results.current_flows.__dict__ ) @@ -221,7 +253,6 @@ def reset(self) -> None: self.namespace.reset() self.last_observation = None self.last_message_timestamp = 0.0 - self._last_production_flow = None class GameSession: @@ -232,29 +263,121 @@ class GameSession: usage behind a consistent API suitable for gym runners and registries. """ + config: GameConfig server: FactorioHeadlessServer instance: FactorioInstance | A2AFactorioInstance agent_sessions: Dict[int, AgentSession] - game_state: Optional[GameState] + current_game_state: Optional[GameState] + task: Optional[TaskABC] + + @dataclass + class Snapshot: + game_state: GameState + game_info: GameInfo + + @dataclass + class GameResult: + post: "GameSession.Snapshot" + agent_result: AgentSession.AgentResult + partial_observation: Observation + + def __init__( + self, + post: "GameSession.Snapshot", + result: AgentSession.AgentResult, + partial_observation: Observation, + ): + self.post = post + self.agent_result = result + self.partial_observation = partial_observation + + @property + def result(self) -> str: + return self.agent_result.result + + @result.setter + def result(self, value: str) -> None: + self.agent_result.result = value def __init__( self, instance_id: int, instance: FactorioInstance, server: FactorioHeadlessServer, + config: GameConfig, + task: Optional[TaskABC] = None, ): self.instance_id = instance_id self.instance = instance self.server = server self.agent_sessions = self._make_agent_sessions() - self.game_state = None + self.current_game_state = None + self.config = config + self.task = task + + @property + def num_agents(self) -> int: + return self.config.num_agents + + @property + def elapsed_ticks(self) -> int: + return self.instance.get_elapsed_ticks() + + @property + def current_game_state(self) -> GameState: + return GameState.from_instance(self.instance) + + @property + def current_game_info(self) -> GameInfo: + return GameInfo( + tick=self.elapsed_ticks, + time=self.elapsed_ticks / 60, + speed=self.speed, + ) + + def _snapshot(self) -> "GameSession.Snapshot": + return self.Snapshot( + self.current_game_state, + self.current_game_info, + ) + + def eval_agent_with_snapshot( + self, agent_idx: int, code: str, value_accrual_time: int + ): + agent_session = self.agent_sessions[agent_idx] + agent_eval_results = agent_session.eval_with_snapshot( + code=code, value_accrual_time=value_accrual_time + ) + post_snapshot = self._snapshot() + partial_observation = agent_session.get_partial_observation(post_snapshot.game_info) + + return self.GameResult( + post=post_snapshot, + agent_result=agent_eval_results, + partial_observation=partial_observation, + ) + + def verify_task( + self, reward: float, game_result: GameResult, step_statistics: Dict[str, Any] = {} + ) -> TaskResponse: + if not self.task: + print(f"[WARN] No task to verify, instance: {self.instance_id}") + return None + # First get the raw verification + task_success = self.task.verify(reward, self.instance, step_statistics) + # Then enhance the response with task output + task_response = self.task.enhance_response_with_task_output( + game_result.result, task_success + ) + game_result.result = task_response + return game_result def _make_agent_sessions(self) -> Dict[int, AgentSession]: return { i: AgentSession(i, self.instance.agent_instances[i], self.db_client) for i in range(self.instance.num_agents) } - + def reinitialize_instance(self) -> None: if isinstance(self.instance, A2AFactorioInstance): self.instance: A2AFactorioInstance @@ -262,7 +385,7 @@ def reinitialize_instance(self) -> None: else: self.instance: FactorioInstance self.instance.initialise() - + async def restart_from_latest_save(self) -> None: await self.server.restart() self.reinitialize_instance() @@ -270,12 +393,12 @@ async def restart_from_latest_save(self) -> None: async def restart_from_save(self, save_name: str) -> None: await self.server.restart(save_name) self.reinitialize_instance() - + @property def speed(self) -> int: return self.instance.get_speed() - @speed.setter + @speed.setter def speed(self, value: int) -> None: self.instance.set_speed(value) @@ -307,7 +430,7 @@ def reset( session.reset() # Convert observation to dictionary to match gym standards - observation = self.agent_sessions[0].get_observation().to_dict() + observation = self.agent_sessions[0].get_partial_observation().to_dict() return observation, {} # Return observation for first agent def cleanup(self) -> None: From 88768bc45c2c48996b5efebd0b6244d256241d44 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 13 Aug 2025 14:31:28 +0530 Subject: [PATCH 43/53] minor changes --- fle/env/environment.py | 10 +++++----- fle/env/models/observation.py | 4 ++++ fle/env/session.py | 18 ++++++++++++++++-- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/fle/env/environment.py b/fle/env/environment.py index 8e08812f9..21fa927e3 100644 --- a/fle/env/environment.py +++ b/fle/env/environment.py @@ -300,12 +300,12 @@ def step( # Get additional info info = { - "error_occurred": game_result.agent_result.error_occurred, - "result": game_result.result, - "ticks": game_result.post.game_info.tick, - "flows": game_result.agent_result.flows_delta, "agent_idx": agent_idx, - "last_message_timestamp": observation.messages[-1].timestamp, + "ticks": game_result.post.game_info.tick, + "error_occurred": game_result.error_occurred, + "result": game_result.result, + "flows": game_result.flows_delta, + "last_message_timestamp": observation.last_message_timestamp, "task_verification": task_response, "output_game_state": game_result.post.game_state, } diff --git a/fle/env/models/observation.py b/fle/env/models/observation.py index 413ae86bd..f3e61ed5f 100644 --- a/fle/env/models/observation.py +++ b/fle/env/models/observation.py @@ -42,6 +42,10 @@ class Observation: task_verification: Optional[TaskResponse] messages: List[AgentMessage] serialized_functions: List[Dict[str, Any]] # List of serialized functions + + @property + def last_message_timestamp(self) -> Optional[float]: + return self.messages[-1].timestamp if self.messages else None @classmethod def from_dict(cls, obs_dict: Dict[str, Any]) -> "Observation": diff --git a/fle/env/session.py b/fle/env/session.py index 7c86d753f..16176ae16 100644 --- a/fle/env/session.py +++ b/fle/env/session.py @@ -221,8 +221,6 @@ def _snapshot(self, observation: Optional[Observation] = None) -> Snapshot: ) def eval_with_snapshot(self, code: str, value_accrual_time: float = 0) -> AgentResult: - if self.last_observation.state: - self.reset_instance(self.last_observation.state) initial_snapshot = self._snapshot() @@ -298,6 +296,18 @@ def result(self) -> str: @result.setter def result(self, value: str) -> None: self.agent_result.result = value + + @property + def score_delta(self) -> float: + return self.agent_result.score_delta + + @property + def error_occurred(self) -> bool: + return self.agent_result.error_occurred + + @property + def flows_delta(self) -> ProductionFlows: + return self.agent_result.flows_delta def __init__( self, @@ -345,6 +355,9 @@ def eval_agent_with_snapshot( self, agent_idx: int, code: str, value_accrual_time: int ): agent_session = self.agent_sessions[agent_idx] + if agent_session.last_observation.state: + self.reset_instance(agent_session.last_observation.state) + agent_eval_results = agent_session.eval_with_snapshot( code=code, value_accrual_time=value_accrual_time ) @@ -380,6 +393,7 @@ def _make_agent_sessions(self) -> Dict[int, AgentSession]: def reinitialize_instance(self) -> None: if isinstance(self.instance, A2AFactorioInstance): + # TODO: Add a2a specific reinitialization, if any self.instance: A2AFactorioInstance self.instance.initialise() else: From 706e84341c261ca9b6ae6b967da91bda44efc13d Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 13 Aug 2025 14:32:04 +0530 Subject: [PATCH 44/53] minor changes --- fle/env/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fle/env/session.py b/fle/env/session.py index 16176ae16..8adb1f807 100644 --- a/fle/env/session.py +++ b/fle/env/session.py @@ -355,7 +355,7 @@ def eval_agent_with_snapshot( self, agent_idx: int, code: str, value_accrual_time: int ): agent_session = self.agent_sessions[agent_idx] - if agent_session.last_observation.state: + if agent_session.last_observation: self.reset_instance(agent_session.last_observation.state) agent_eval_results = agent_session.eval_with_snapshot( From fb050de14b9d74c97142f44ba3130fc2981292d8 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 13 Aug 2025 16:40:08 +0530 Subject: [PATCH 45/53] shifting code around --- fle/agents/gym_agent.py | 2 + fle/env/gym_env/config.py | 4 +- fle/env/session.py | 36 +++---- fle/env/trajectory_runner.py | 176 ++++++++++++++++++----------------- fle/run_eval.py | 4 +- 5 files changed, 113 insertions(+), 109 deletions(-) diff --git a/fle/agents/gym_agent.py b/fle/agents/gym_agent.py index cf83c1fe6..e7bfa1a5d 100644 --- a/fle/agents/gym_agent.py +++ b/fle/agents/gym_agent.py @@ -113,6 +113,8 @@ class GymAgent(AgentABC): + agent_completed: bool = False + def __init__( self, model: str, diff --git a/fle/env/gym_env/config.py b/fle/env/gym_env/config.py index 6e7f384fc..dd43d7021 100644 --- a/fle/env/gym_env/config.py +++ b/fle/env/gym_env/config.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, model_validator -from typing import List, Optional +from typing import Dict, List, Optional from fle.agents.gym_agent import GymAgent from fle.env.gym_env.observation_formatter import BasicObservationFormatter @@ -20,7 +20,7 @@ class GymRunConfig(BaseModel): class GymEvalConfig(BaseModel): """Configuration for gym evaluation""" - agents: List[GymAgent] + agents: Dict[int, GymAgent] version: int version_description: str exit_on_task_success: bool diff --git a/fle/env/session.py b/fle/env/session.py index 8adb1f807..6968f168f 100644 --- a/fle/env/session.py +++ b/fle/env/session.py @@ -7,6 +7,7 @@ from a2a.types import AgentCard from fle.agents.models import Response +from fle.commons.models.conversation import Conversation from fle.commons.models.program import Program from fle.env.a2a_instance import A2AFactorioInstance from fle.env.a2a_namespace import A2AFactorioNamespace @@ -163,36 +164,18 @@ def get_partial_observation( return observation - async def _initialize_trajectory_state( - self, version: int, process_id: int - ) -> Tuple[GameState, List[int]]: - """Initialize trajectory state, either from resume or fresh start - - Returns: - Tuple of (current_state, agent_steps) - """ - current_state = None - - if version and self.db_client is None: - return None, 0 - + async def get_resume_state(self) -> Tuple[GameState, Conversation]: ( current_state, agent_conversation, parent_id, depth, ) = await self.db_client.get_resume_state( - resume_version=version, - process_id=process_id, + resume_version=self.version, + process_id=self.process_id, agent_idx=self.agent_idx, ) - if current_state: - self.steps = depth - - if not current_state: - current_state = self.config.task.starting_game_state - - return current_state, agent_conversation + return current_state, agent_conversation, parent_id, depth def action_from_code(self, code: str) -> Action: """Create an Action object from a Policy""" @@ -351,6 +334,15 @@ def _snapshot(self) -> "GameSession.Snapshot": self.current_game_info, ) + async def get_agent_session_resume_state(self, agent_idx: int) -> Tuple[GameState, Conversation]: + agent_session = self.agent_sessions[agent_idx] + current_state, agent_conversation, parent_id, depth = await agent_session.get_resume_state() + if current_state: + agent_session.steps = depth + if not current_state and self.task: + current_state = self.task.starting_game_state + return current_state, agent_conversation + def eval_agent_with_snapshot( self, agent_idx: int, code: str, value_accrual_time: int ): diff --git a/fle/env/trajectory_runner.py b/fle/env/trajectory_runner.py index b7ed0c00f..467332869 100644 --- a/fle/env/trajectory_runner.py +++ b/fle/env/trajectory_runner.py @@ -1,6 +1,6 @@ import time from itertools import product -from typing import List, Optional, Tuple +from typing import Dict, List, Optional, Tuple from fle.agents import CompletionReason, CompletionResult from fle.agents.gym_agent import GymAgent @@ -22,6 +22,7 @@ class GymTrajectoryRunner: gym_env: FactorioGymEnv process_id: int session_manager: GameSessionManager + agents: Dict[int, GymAgent] def __init__( self, @@ -42,6 +43,7 @@ def __init__( trajectory_length=self.config.task.trajectory_length, log_dir=log_dir, ) + self.max_steps = self.config.task.trajectory_length def _log_trajectory_state( self, @@ -75,115 +77,123 @@ def _log_trajectory_state( agent, agent_idx, agent_step, observation, program ) - async def _initialize_trajectory_state(self, agent_session: AgentSession) -> Tuple[GameState, List[int]]: + async def _initialize_trajectory_state(self) -> Tuple[GameState, List[int]]: """Initialize trajectory state, either from resume or fresh start Returns: Tuple of (current_state, agent_steps) """ - current_state, agent_conversation = await agent_session.get_resume_state() - if current_state: - self.agents[agent_idx].reset(agent_conversation) - else: - current_state = self.config.task.starting_game_state + game_session = self.gym_env.game_session + first_idx = list(game_session.agent_sessions.keys())[0] + current_state, agent_conversation = await game_session.get_agent_session_resume_state(first_idx) + if agent_conversation: + self.agents[first_idx].reset(agent_conversation) self.gym_env.reset(options={"game_state": current_state}) # Initialize agent conversations - for agent_idx, agent in enumerate(self.agents): + for agent_idx in self.agents: + agent = self.agents[agent_idx] + agent_session = game_session.agent_sessions[agent_idx] conversation = Conversation() - initial_obs = self.gym_env.unwrapped.get_observation(agent_idx) + initial_obs = agent_session.get_partial_observation(game_session.current_game_info) formatted_obs = agent.observation_formatter.format(initial_obs).raw_str conversation.add_user_message(formatted_obs) agent.reset(conversation) return current_state + + async def _run_trajectory_step(self, agent: GymAgent, agent_session: AgentSession): + iteration_start = time.time() + agent_idx = agent_session.agent_idx + # Loop while the agent is not completed yet + while not agent.agent_completed and agent_session.steps < self.max_steps: + # Generate policy using agent's method + policy = await agent.generate_policy() + agent_session.steps += 1 + if not policy: + print( + f"Policy generation failed for agent {agent_idx} at iteration {agent_session.steps}" + ) + break + + # Execute step in the environment + action = agent_session.action_from_code(policy.code) + obs_dict, reward, terminated, truncated, info = self.gym_env.step( + action, + agent_session, + ) + observation = Observation.from_dict(obs_dict) + output_game_state = info["output_game_state"] + done = terminated or truncated + + # Create program from policy with environment results + program = await Program.from_policy( + agent_idx=agent_idx, + policy=policy, + reward=reward, + response=obs_dict["raw_text"], + error_occurred=info["error_occurred"], + game_state=output_game_state, + process_id=self.process_id, + model=agent.model, + version=agent_session.version, + version_description=agent_session.version_description, + ) + + # Update agent's conversation with the program and its results + await agent.update_conversation( + observation, previous_program=program + ) + + # Consolidate all trajectory logging operations + self._log_trajectory_state( + iteration_start, + agent, + agent_idx, + agent_session.steps, + program, + observation, + ) + + # Get the agent_completed flag from the agent + agent.agent_completed, update_state = agent.check_step_completion( + observation + ) + if update_state: + current_state = output_game_state + + # Check if done and exit if configured + if done and self.config.exit_on_task_success: + completion_result = CompletionResult( + step=agent_session.steps, reason=CompletionReason.SUCCESS + ) + for agent in self.agents: + await agent.end(completion_result) + return + async def run(self): """Run a single trajectory""" # Initialize state based on resume or fresh start - max_steps = self.config.task.trajectory_length - await self._initialize_trajectory_state(self.session_manager.sessions[0].agent_sessions[0]) + game_session = self.gym_env.game_session + await self._initialize_trajectory_state() # Save system prompts for all agents at the start for agent_idx, agent in enumerate(self.agents): self.logger.save_system_prompt(agent, agent_idx) # Run trajectory - for _, agent_idx in product(range(max_steps), range(len(self.agents))): - agent = self.agents[agent_idx] - agent_session = self.session_manager.sessions[0].agent_sessions[agent_idx] - iteration_start = time.time() - agent_completed = False + # Example: if max_steps=3 and self.agents has 2 agents (keys 0,1), this will run: + # (step=0,agent=0), (step=0,agent=1), (step=1,agent=0), (step=1,agent=1), (step=2,agent=0), (step=2,agent=1) + # Total iterations = max_steps * num_agents = 3 * 2 = 6 iterations + for _, agent_idx in product(range(self.max_steps), self.agents.keys()): try: - # Loop while the agent is not completed yet - while not agent_completed and agent_session.steps < max_steps: - # Generate policy using agent's method - policy = await agent.generate_policy() - agent_session.steps += 1 - if not policy: - print( - f"Policy generation failed for agent {agent_idx} at iteration {agent_session.steps}" - ) - break - - # Execute step in the environment - action = agent_session.action_from_code(policy.code) - obs_dict, reward, terminated, truncated, info = self.gym_env.step( - action, - agent_session, - ) - observation = Observation.from_dict(obs_dict) - output_game_state = info["output_game_state"] - done = terminated or truncated - - # Create program from policy with environment results - program = await Program.from_policy( - agent_idx=agent_idx, - policy=policy, - reward=reward, - response=obs_dict["raw_text"], - error_occurred=info["error_occurred"], - game_state=output_game_state, - process_id=self.process_id, - model=agent.model, - version=agent_session.version, - version_description=agent_session.version_description, - ) - - # Update agent's conversation with the program and its results - await agent.update_conversation( - observation, previous_program=program - ) - - # Consolidate all trajectory logging operations - self._log_trajectory_state( - iteration_start, - agent, - agent_idx, - agent_session.steps, - program, - observation, - ) - - # Get the agent_completed flag from the agent - agent_completed, update_state = agent.check_step_completion( - observation - ) - if update_state: - current_state = output_game_state - - # Check if done and exit if configured - if done and self.config.exit_on_task_success: - completion_result = CompletionResult( - step=agent_session.steps, reason=CompletionReason.SUCCESS - ) - for agent in self.agents: - await agent.end(completion_result) - return - + agent = self.agents[agent_idx] + agent_session = game_session.agent_sessions[agent_idx] + await self._run_trajectory_step(agent, agent_session) except Exception as e: print( f"Error in trajectory runner iteration {agent_session.steps}: {e}" ) - continue + continue \ No newline at end of file diff --git a/fle/run_eval.py b/fle/run_eval.py index 3c95295fd..7e3db5f5f 100644 --- a/fle/run_eval.py +++ b/fle/run_eval.py @@ -108,7 +108,7 @@ async def main(): instance = gym_env.unwrapped.instance # Create agents and their agent cards - agents = [] + agents = dict() agent_cards = [] for agent_idx in range(run_config.num_agents): system_prompt = instance.get_system_prompt(agent_idx) @@ -120,7 +120,7 @@ async def main(): observation_formatter=BasicObservationFormatter(include_research=False), system_prompt_formatter=SystemPromptFormatter(), ) - agents.append(agent) + agents[agent_idx] = agent # Create agent card for a2a support agent_card = agent.get_agent_card() From 929fb6308df1cacddde38e45bb635011f1b46150 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 13 Aug 2025 17:30:11 +0530 Subject: [PATCH 46/53] minor changes --- fle/commons/models/program.py | 9 ++++---- fle/env/session.py | 10 ++++++--- fle/env/trajectory_runner.py | 41 ++++++++++++++++++----------------- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/fle/commons/models/program.py b/fle/commons/models/program.py index c8a13db04..70045c3ce 100644 --- a/fle/commons/models/program.py +++ b/fle/commons/models/program.py @@ -85,7 +85,7 @@ def from_row(cls, row: Dict): ) @classmethod - async def from_policy( + def from_policy( cls, policy, agent_idx: int, @@ -97,7 +97,6 @@ async def from_policy( model: str, version: int, version_description: str, - saved_program_id: Optional[int] = None, ): """Create a Program object from a Policy and environment results @@ -135,7 +134,7 @@ async def from_policy( }, depth=depth, ) - if saved_program_id is not None: - program.id = saved_program_id - return program + + def update_program_id(self, program_id: int) -> None: + self.id = program_id diff --git a/fle/env/session.py b/fle/env/session.py index 6968f168f..b0d1e06b0 100644 --- a/fle/env/session.py +++ b/fle/env/session.py @@ -163,6 +163,11 @@ def get_partial_observation( self.last_observation = observation return observation + + async def save_program(self, program: Program) -> Program: + program = await self.db_client.create_program(program) + program.update_program_id(program.id) + return program async def get_resume_state(self) -> Tuple[GameState, Conversation]: ( @@ -177,12 +182,12 @@ async def get_resume_state(self) -> Tuple[GameState, Conversation]: ) return current_state, agent_conversation, parent_id, depth - def action_from_code(self, code: str) -> Action: + def action_from_code(self, code: str, game_state: GameState) -> Action: """Create an Action object from a Policy""" return Action( agent_idx=self.agent_idx, code=code, - game_state=self.last_observation.state, + game_state=game_state, ) @property @@ -198,7 +203,6 @@ def _snapshot(self, observation: Optional[Observation] = None) -> Snapshot: return self.Snapshot( score=self.current_score, flows=self.current_production_flows, - tick=self.namespace.instance.get_elapsed_ticks(), timestamp=time.time(), observation=observation, ) diff --git a/fle/env/trajectory_runner.py b/fle/env/trajectory_runner.py index 467332869..88ac455ff 100644 --- a/fle/env/trajectory_runner.py +++ b/fle/env/trajectory_runner.py @@ -18,12 +18,13 @@ class GymTrajectoryRunner: """Handles program generation and evaluation for a single trajectory in the gym environment""" + config: GymEvalConfig gym_env: FactorioGymEnv process_id: int session_manager: GameSessionManager agents: Dict[int, GymAgent] - + def __init__( self, config: GymEvalConfig, @@ -76,7 +77,7 @@ def _log_trajectory_state( self.logger.log_observation_and_program( agent, agent_idx, agent_step, observation, program ) - + async def _initialize_trajectory_state(self) -> Tuple[GameState, List[int]]: """Initialize trajectory state, either from resume or fresh start @@ -85,7 +86,9 @@ async def _initialize_trajectory_state(self) -> Tuple[GameState, List[int]]: """ game_session = self.gym_env.game_session first_idx = list(game_session.agent_sessions.keys())[0] - current_state, agent_conversation = await game_session.get_agent_session_resume_state(first_idx) + current_state, agent_conversation = ( + await game_session.get_agent_session_resume_state(first_idx) + ) if agent_conversation: self.agents[first_idx].reset(agent_conversation) @@ -95,14 +98,16 @@ async def _initialize_trajectory_state(self) -> Tuple[GameState, List[int]]: agent = self.agents[agent_idx] agent_session = game_session.agent_sessions[agent_idx] conversation = Conversation() - initial_obs = agent_session.get_partial_observation(game_session.current_game_info) + initial_obs = agent_session.get_partial_observation( + game_session.current_game_info + ) formatted_obs = agent.observation_formatter.format(initial_obs).raw_str conversation.add_user_message(formatted_obs) agent.reset(conversation) return current_state - - async def _run_trajectory_step(self, agent: GymAgent, agent_session: AgentSession): + + async def _run_trajectory_step(self, agent: GymAgent, agent_session: AgentSession, current_state: GameState): iteration_start = time.time() agent_idx = agent_session.agent_idx # Loop while the agent is not completed yet @@ -117,17 +122,14 @@ async def _run_trajectory_step(self, agent: GymAgent, agent_session: AgentSessio break # Execute step in the environment - action = agent_session.action_from_code(policy.code) - obs_dict, reward, terminated, truncated, info = self.gym_env.step( - action, - agent_session, - ) + action = agent_session.action_from_code(policy.code, current_state) + obs_dict, reward, terminated, truncated, info = self.gym_env.step(action) observation = Observation.from_dict(obs_dict) - output_game_state = info["output_game_state"] + output_game_state: GameState = info["output_game_state"] done = terminated or truncated # Create program from policy with environment results - program = await Program.from_policy( + program = Program.from_policy( agent_idx=agent_idx, policy=policy, reward=reward, @@ -140,10 +142,10 @@ async def _run_trajectory_step(self, agent: GymAgent, agent_session: AgentSessio version_description=agent_session.version_description, ) + program = await agent_session.save_program(program) + # Update agent's conversation with the program and its results - await agent.update_conversation( - observation, previous_program=program - ) + await agent.update_conversation(observation, previous_program=program) # Consolidate all trajectory logging operations self._log_trajectory_state( @@ -171,13 +173,12 @@ async def _run_trajectory_step(self, agent: GymAgent, agent_session: AgentSessio await agent.end(completion_result) return - async def run(self): """Run a single trajectory""" # Initialize state based on resume or fresh start game_session = self.gym_env.game_session - await self._initialize_trajectory_state() + current_state, _ = await self._initialize_trajectory_state() # Save system prompts for all agents at the start for agent_idx, agent in enumerate(self.agents): @@ -191,9 +192,9 @@ async def run(self): try: agent = self.agents[agent_idx] agent_session = game_session.agent_sessions[agent_idx] - await self._run_trajectory_step(agent, agent_session) + await self._run_trajectory_step(agent, agent_session, current_state) except Exception as e: print( f"Error in trajectory runner iteration {agent_session.steps}: {e}" ) - continue \ No newline at end of file + continue From 226e6b7a711d567e2fce24d8eb885773cb7c27fc Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 13 Aug 2025 17:47:35 +0530 Subject: [PATCH 47/53] explicit semantics --- fle/env/session.py | 8 -------- fle/env/trajectory_runner.py | 7 ++++--- fle/run_eval.py | 4 +--- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/fle/env/session.py b/fle/env/session.py index b0d1e06b0..85248a6d5 100644 --- a/fle/env/session.py +++ b/fle/env/session.py @@ -182,14 +182,6 @@ async def get_resume_state(self) -> Tuple[GameState, Conversation]: ) return current_state, agent_conversation, parent_id, depth - def action_from_code(self, code: str, game_state: GameState) -> Action: - """Create an Action object from a Policy""" - return Action( - agent_idx=self.agent_idx, - code=code, - game_state=game_state, - ) - @property def current_score(self) -> float: return self.namespace.score()[0] diff --git a/fle/env/trajectory_runner.py b/fle/env/trajectory_runner.py index 88ac455ff..6f4d91d4d 100644 --- a/fle/env/trajectory_runner.py +++ b/fle/env/trajectory_runner.py @@ -122,7 +122,7 @@ async def _run_trajectory_step(self, agent: GymAgent, agent_session: AgentSessio break # Execute step in the environment - action = agent_session.action_from_code(policy.code, current_state) + action = Action(agent_idx=agent_idx, code=policy.code, game_state=current_state) obs_dict, reward, terminated, truncated, info = self.gym_env.step(action) observation = Observation.from_dict(obs_dict) output_game_state: GameState = info["output_game_state"] @@ -173,8 +173,8 @@ async def _run_trajectory_step(self, agent: GymAgent, agent_session: AgentSessio await agent.end(completion_result) return - async def run(self): - """Run a single trajectory""" + async def run_sequential(self): + """Run a single sequential trajectory""" # Initialize state based on resume or fresh start game_session = self.gym_env.game_session @@ -188,6 +188,7 @@ async def run(self): # Example: if max_steps=3 and self.agents has 2 agents (keys 0,1), this will run: # (step=0,agent=0), (step=0,agent=1), (step=1,agent=0), (step=1,agent=1), (step=2,agent=0), (step=2,agent=1) # Total iterations = max_steps * num_agents = 3 * 2 = 6 iterations + # Note: This leads to sequential execution of multiple agents. (Not parallel) for _, agent_idx in product(range(self.max_steps), self.agents.keys()): try: agent = self.agents[agent_idx] diff --git a/fle/run_eval.py b/fle/run_eval.py index 7e3db5f5f..3202d003a 100644 --- a/fle/run_eval.py +++ b/fle/run_eval.py @@ -61,7 +61,6 @@ def run_process(run_idx: int, config: GymEvalConfig): async def run_trajectory(run_idx: int, config: GymEvalConfig): """Run a single gym evaluation process""" - db_client = await create_db_client() # Create gym environment using gym.make() gym_env = gym.make(config.env_id) @@ -73,8 +72,7 @@ async def run_trajectory(run_idx: int, config: GymEvalConfig): log_dir=log_dir, process_id=run_idx, ) - await runner.run() - await db_client.cleanup() + await runner.run_sequential() async def main(): From a102069a24293dca41078149e8d03f8f9d84e0fe Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 13 Aug 2025 19:11:22 +0530 Subject: [PATCH 48/53] make it run --- fle/agents/gym_agent.py | 2 +- fle/env/game/instance.py | 1 - fle/env/gym_env/config.py | 4 +- fle/env/registry.py | 92 ++++++++++---- fle/env/session_manager.py | 5 +- .../independent/gym_run_config.json | 6 +- fle/run.py | 76 +++++++---- fle/run_eval.py | 15 ++- fle/services/docker/config.py | 9 +- fle/services/docker/docker_manager.py | 2 +- .../factorio/config/map-gen-settings.json | 78 ++++++++++++ .../docker/factorio/config/map-settings.json | 120 ++++++++++++++++++ fle/services/docker/factorio/config/rconpw | 1 + .../factorio/config/server-settings.json | 72 +++++++++++ fle/services/rcon.py | 8 +- 15 files changed, 429 insertions(+), 62 deletions(-) create mode 100644 fle/services/docker/factorio/config/map-gen-settings.json create mode 100644 fle/services/docker/factorio/config/map-settings.json create mode 100644 fle/services/docker/factorio/config/rconpw create mode 100644 fle/services/docker/factorio/config/server-settings.json diff --git a/fle/agents/gym_agent.py b/fle/agents/gym_agent.py index e7bfa1a5d..3b499de38 100644 --- a/fle/agents/gym_agent.py +++ b/fle/agents/gym_agent.py @@ -9,7 +9,7 @@ from fle.commons.models.conversation import Conversation from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.program import Program -from fle.env.gym_env.observation import Observation +from fle.env.models.observation import Observation from fle.env.gym_env.observation_formatter import BasicObservationFormatter from fle.env.gym_env.system_prompt_formatter import SystemPromptFormatter from fle.env.tasks import TaskABC diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index 921a2fd0e..745c9c1b6 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -23,7 +23,6 @@ SystemPromptGenerator, ) from fle.services.rcon import _lua2python -from fle.services.docker.docker_manager import ServerSettings CHUNK_SIZE = 32 MAX_SAMPLES = 5000 diff --git a/fle/env/gym_env/config.py b/fle/env/gym_env/config.py index dd43d7021..410dc96a7 100644 --- a/fle/env/gym_env/config.py +++ b/fle/env/gym_env/config.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, model_validator +from pydantic import BaseModel, ConfigDict, model_validator from typing import Dict, List, Optional from fle.agents.gym_agent import GymAgent @@ -9,6 +9,7 @@ class GymRunConfig(BaseModel): """Configuration for a single gym environment evaluation run""" + model_config = ConfigDict(arbitrary_types_allowed=True) env_id: str # Gym environment ID from registry (e.g., "Factorio-iron_ore_throughput_16-v0") model: str version: Optional[int] = None @@ -20,6 +21,7 @@ class GymRunConfig(BaseModel): class GymEvalConfig(BaseModel): """Configuration for gym evaluation""" + model_config = ConfigDict(arbitrary_types_allowed=True) agents: Dict[int, GymAgent] version: int version_description: str diff --git a/fle/env/registry.py b/fle/env/registry.py index d17283bd6..90541abb6 100644 --- a/fle/env/registry.py +++ b/fle/env/registry.py @@ -4,10 +4,14 @@ import gym -from fle.commons.cluster_ips import get_local_container_ips -from fle.env.game import FactorioInstance +from fle.env.session_manager import GameSessionManager +from fle.env.session import GameSession +from fle.env.game.config import GameConfig +from fle.services.docker.config import DockerConfig + from fle.env.environment import FactorioGymEnv from fle.env.tasks import TaskFactory +from fle.services.docker.docker_manager import FactorioHeadlessClusterManager @dataclass @@ -22,7 +26,9 @@ class GymEnvironmentSpec: model: str = "gpt-4" version: Optional[int] = None exit_on_task_success: bool = True - + # Configs are populated by the registry defaults unless explicitly provided + game_config: Optional[GameConfig] = None + docker_config: Optional[DockerConfig] = None class FactorioGymRegistry: """Registry for Factorio gym environments""" @@ -34,6 +40,25 @@ def __init__(self): self._task_definitions_path = TASK_FOLDER self._discovered = False + # Defaults that can be overridden by callers (e.g., run_eval) + self._default_game_config: GameConfig = GameConfig() + self._default_docker_config: DockerConfig = DockerConfig() + + def set_defaults( + self, + game_config: Optional[GameConfig] = None, + docker_config: Optional[DockerConfig] = None, + ) -> None: + if game_config is not None: + self._default_game_config = game_config + if docker_config is not None: + self._default_docker_config = docker_config + # Apply to already-registered specs that didn't customize + for spec in self._environments.values(): + if spec.game_config is None: + spec.game_config = self._default_game_config + if spec.docker_config is None: + spec.docker_config = self._default_docker_config def discover_tasks(self) -> None: """Automatically discover all task definitions and register them as gym environments""" @@ -81,6 +106,8 @@ def register_environment( model: str = "gpt-4", version: Optional[int] = None, exit_on_task_success: bool = True, + game_config: Optional[GameConfig] = None, + docker_config: Optional[DockerConfig] = None, ) -> None: """Register a new gym environment""" @@ -93,6 +120,8 @@ def register_environment( model=model, version=version, exit_on_task_success=exit_on_task_success, + game_config=game_config or self._default_game_config, + docker_config=docker_config or self._default_docker_config, ) self._environments[env_id] = spec @@ -100,7 +129,7 @@ def register_environment( # Register with gym gym.register( id=env_id, - entry_point="fle.env.gym_env.registry:make_factorio_env", + entry_point="fle.env.registry:make_factorio_env", kwargs={"env_spec": spec}, ) @@ -126,35 +155,34 @@ def get_all_specs(self) -> Dict[str, GymEnvironmentSpec]: def make_factorio_env(env_spec: GymEnvironmentSpec) -> FactorioGymEnv: """Factory function to create a Factorio gym environment""" - # Create task from the task definition task = TaskFactory.create_task(env_spec.task_config_path) - # Create Factorio instance - # Note: This assumes you have containers available - try: - ips, udp_ports, tcp_ports = get_local_container_ips() - if len(tcp_ports) == 0: - raise RuntimeError("No Factorio containers available") - - # Use the first available container - instance = FactorioInstance( - address=ips[0], - container_id=0, # Use first container - num_agents=env_spec.num_agents, - ) - instance.set_speed(10) + # Resolve configs from spec or defaults + game_config = env_spec.game_config or _registry._default_game_config + docker_config = env_spec.docker_config or _registry._default_docker_config - # Setup the task - task.setup(instance) + # Create a lightweight cluster manager pointing at expected ports/volumes + docker_platform = "linux/arm64" if docker_config.arch in ("arm64", "aarch64") else "linux/amd64" + cluster = FactorioHeadlessClusterManager( + config=docker_config, + docker_platform=docker_platform, + num_instances=docker_config.num_instances, + dry_run=False, + ) - # Create and return the gym environment - env = FactorioGymEnv(instance=instance, task=task) + # Create session manager bound to configs/cluster + session_manager = GameSessionManager( + game_config=game_config, + docker_config=docker_config, + server_manager=cluster, + ) - return env + # Create a single GameSession targeting instance 0 + game_session: GameSession = session_manager._make_session(instance_id=0, task=task) - except Exception as e: - raise RuntimeError(f"Failed to create Factorio environment: {e}") + # Return the gym environment + return FactorioGymEnv(game_session=game_session) def register_all_environments() -> None: @@ -195,6 +223,18 @@ def make(env_id: str, **kwargs) -> FactorioGymEnv: return gym.make(env_id, **kwargs) +def configure_registry( + game_config: Optional[GameConfig] = None, + docker_config: Optional[DockerConfig] = None, +) -> None: + """Configure default Game/Docker configs for all gym environments. + + Call this early from entrypoints (e.g., run_eval) to set defaults that + will be attached to environments created via gym.make(). + """ + _registry.set_defaults(game_config=game_config, docker_config=docker_config) + + # Example usage and documentation if __name__ == "__main__": # List all available environments diff --git a/fle/env/session_manager.py b/fle/env/session_manager.py index a34e448a2..2d972adbf 100644 --- a/fle/env/session_manager.py +++ b/fle/env/session_manager.py @@ -5,6 +5,7 @@ from fle.env.game.factorio_client import FactorioClient from fle.env.game.instance import FactorioInstance from fle.env.session import AgentSession, GameSession +from fle.env.tasks import TaskABC from fle.services.docker.config import DockerConfig from fle.services.docker.docker_manager import FactorioHeadlessClusterManager @@ -75,12 +76,14 @@ def _make_instance( fast=self.game_config.fast_mode, ) - def _make_session(self, instance_id: int) -> GameSession: + def _make_session(self, instance_id: int, task: Optional[TaskABC] = None) -> GameSession: instance = self._make_instance(instance_id) return GameSession( instance_id=instance_id, instance=instance, server=self.server_manager.servers[instance_id], + config=self.game_config, + task=task, ) async def start_cluster(self) -> None: diff --git a/fle/eval/algorithms/independent/gym_run_config.json b/fle/eval/algorithms/independent/gym_run_config.json index 173777af1..ee71ddfba 100644 --- a/fle/eval/algorithms/independent/gym_run_config.json +++ b/fle/eval/algorithms/independent/gym_run_config.json @@ -1,6 +1,8 @@ [ { - "env_id": "Factorio-iron_ore_throughput_16-v0", - "model": "claude-3-5-sonnet-latest" + "env_id": "iron_ore_throughput", + "model": "claude-3-5-sonnet-latest", + "num_agents": 1, + "exit_on_task_success": true } ] \ No newline at end of file diff --git a/fle/run.py b/fle/run.py index 05a0e9cf3..f2aac8caa 100644 --- a/fle/run.py +++ b/fle/run.py @@ -31,34 +31,64 @@ def fle_init(): def fle_cluster(args): - script = Path(__file__).parent / "cluster" / "local" / "run-envs.sh" - if not script.exists(): - print(f"Cluster script not found: {script}", file=sys.stderr) - sys.exit(1) - cmd = [str(script)] - if args: - if args.cluster_command: - cmd.append(args.cluster_command) - if args.n: - cmd.extend(["-n", str(args.n)]) - if args.s: - cmd.extend(["-s", args.s]) + """Manage local Factorio headless servers using the Docker manager API.""" + from fle.services.docker.config import DockerConfig, Scenario, Mode + from fle.services.docker.docker_manager import FactorioHeadlessClusterManager + try: - subprocess.run(cmd, check=True) - except subprocess.CalledProcessError as e: - print(f"Error running cluster script: {e}", file=sys.stderr) - sys.exit(e.returncode) + config = DockerConfig() + # Map args to config if provided + if args and args.s: + if args.s == "open_world": + config.scenario_name = Scenario.OPEN_WORLD.value + elif args.s == "default_lab_scenario": + config.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value + num = args.n if args and args.n else 1 + docker_platform = "linux/arm64" if config.arch in ("arm64", "aarch64") else "linux/amd64" + mgr = FactorioHeadlessClusterManager(config, docker_platform, num) + + async def run(): + if args and args.cluster_command == "stop": + await mgr.stop() + elif args and args.cluster_command == "restart": + await mgr.restart() + else: + await mgr.start() + await mgr.attach_docker_configs() + await mgr.docker.close() + + asyncio.run(run()) + except Exception as e: + print(f"Error managing cluster: {e}", file=sys.stderr) + sys.exit(1) def fle_eval(args, env): if not env: return - # Check if Factorio server is running on port 34197 - probe = Path(__file__).parent / "cluster" / "docker" / "probe.sh" - result = subprocess.run(["sh", str(probe)]) - if result.returncode != 0: - print("Server not running, starting cluster...") - fle_cluster(None) + # Ensure servers are started via Docker manager if not already running + # Attempt to start 1 instance as a convenience if containers are missing + try: + from fle.services.docker.docker_manager import FactorioHeadlessClusterManager + from fle.services.docker.config import DockerConfig + + async def ensure_running(): + # Instantiate inside loop to avoid 'no running event loop' + config = DockerConfig() + docker_platform = "linux/arm64" if config.arch in ("arm64", "aarch64") else "linux/amd64" + mgr = FactorioHeadlessClusterManager(config, docker_platform, 1) + # Try to get IPs; if none/invalid response, start cluster + ips, udp_ports, tcp_ports = await mgr.get_local_container_ips() + if not tcp_ports: + print("Server not running, starting cluster...") + await mgr.start() + ips, udp_ports, tcp_ports = await mgr.get_local_container_ips() + await mgr.attach_docker_configs() + await mgr.docker.close() + + asyncio.run(ensure_running()) + except Exception as e: + print(f"Warning: could not ensure cluster running automatically ({e}). Continuing...") config_path = Path(args.config) if not config_path.exists(): print(f"Error: Config file '{args.config}' not found.", file=sys.stderr) @@ -87,7 +117,7 @@ def main(): ) subparsers = parser.add_subparsers(dest="command") parser_cluster = subparsers.add_parser( - "cluster", help="Setup Docker containers (run run-envs.sh)" + "cluster", help="Manage Docker containers for local Factorio servers" ) parser_cluster.add_argument( "cluster_command", diff --git a/fle/run_eval.py b/fle/run_eval.py index 3202d003a..eb87d7080 100644 --- a/fle/run_eval.py +++ b/fle/run_eval.py @@ -14,7 +14,13 @@ from fle.env.gym_env.observation_formatter import BasicObservationFormatter from fle.env.gym_env.system_prompt_formatter import SystemPromptFormatter from fle.env.trajectory_runner import GymTrajectoryRunner -from fle.env.registry import get_environment_info, list_available_environments +from fle.env.registry import ( + get_environment_info, + list_available_environments, + configure_registry, +) +from fle.env.game.config import GameConfig +from fle.services.docker.config import DockerConfig from fle.services.db.db_client import create_db_client, get_next_version load_dotenv() @@ -88,6 +94,13 @@ async def main(): # Read and validate run configurations run_configs = get_validated_run_configs(args.run_config) + # Configure gym registry defaults for Game/Docker configs + # Note: We validated that all runs have the same num_agents above + num_agents = run_configs[0].num_agents if len(run_configs) > 0 else 1 + game_config = GameConfig(num_agents=num_agents) + docker_config = DockerConfig() + configure_registry(game_config=game_config, docker_config=docker_config) + # Get starting version number for new runs base_version = await get_next_version() version_offset = 0 diff --git a/fle/services/docker/config.py b/fle/services/docker/config.py index 9d865a103..40d51b29d 100644 --- a/fle/services/docker/config.py +++ b/fle/services/docker/config.py @@ -51,6 +51,7 @@ class DockerConfig(BaseModel): arch: str = Field(default_factory=platform.machine) address: str = "localhost" os_name: str = Field(default_factory=platform.system) + num_instances: int = 1 saves_path: Path = Field(default_factory=lambda: ROOT_DIR / ".fle" / "saves") screenshots_dir: Path = Field( @@ -69,7 +70,7 @@ class DockerConfig(BaseModel): / "scenarios" ) server_config_dir: Path = Field( - default_factory=lambda: Path(__file__).parent.resolve() / "factorio" / "config" + default_factory=lambda: ROOT_DIR / "env" / "game" / "factorio" / "config" ) mode: str = Mode.SAVE_BASED.value @@ -78,10 +79,14 @@ class DockerConfig(BaseModel): factorio_password: str = Field( default_factory=lambda: ( - Path(__file__).parent.resolve() / "factorio" / "config" / "rconpw" + ROOT_DIR / "env" / "game" / "factorio" / "config" / "rconpw" ) .read_text() .strip() ) model_config = {"extra": "forbid", "frozen": True} + +config = DockerConfig() + +print(config.factorio_password) \ No newline at end of file diff --git a/fle/services/docker/docker_manager.py b/fle/services/docker/docker_manager.py index 6cb098a59..29a7d53fb 100644 --- a/fle/services/docker/docker_manager.py +++ b/fle/services/docker/docker_manager.py @@ -357,7 +357,7 @@ async def get_local_container_ips(self) -> tuple[List[str], List[int], List[int] if not container_ids or container_ids[0] == "": print("No running Factorio containers found") - return [] + return ([], [], []) ips = [] udp_ports = [] diff --git a/fle/services/docker/factorio/config/map-gen-settings.json b/fle/services/docker/factorio/config/map-gen-settings.json new file mode 100644 index 000000000..7a2b41281 --- /dev/null +++ b/fle/services/docker/factorio/config/map-gen-settings.json @@ -0,0 +1,78 @@ +{ + "_terrain_segmentation_comment": "The inverse of 'water scale' in the map generator GUI.", + "terrain_segmentation": 1, + + "_water_comment": + [ + "The equivalent to 'water coverage' in the map generator GUI. Higher coverage means more water in larger oceans.", + "Water level = 10 * log2(this value)" + ], + "water": 1, + + "_comment_width+height": "Width and height of map, in tiles; 0 means infinite", + "width": 0, + "height": 0, + + "_starting_area_comment": "Multiplier for 'biter free zone radius'", + "starting_area": 1, + + "peaceful_mode": false, + "autoplace_controls": + { + "coal": {"frequency": 1, "size": 1, "richness": 1}, + "stone": {"frequency": 1, "size": 1, "richness": 1}, + "copper-ore": {"frequency": 1, "size": 1,"richness": 1}, + "iron-ore": {"frequency": 1, "size": 1, "richness": 1}, + "uranium-ore": {"frequency": 1, "size": 1, "richness": 1}, + "crude-oil": {"frequency": 1, "size": 1, "richness": 1}, + "trees": {"frequency": 1, "size": 1, "richness": 1}, + "enemy-base": {"frequency": 1, "size": 1, "richness": 1} + }, + + "cliff_settings": + { + "_name_comment": "Name of the cliff prototype", + "name": "cliff", + + "_cliff_elevation_0_comment": "Elevation of first row of cliffs", + "cliff_elevation_0": 10, + + "_cliff_elevation_interval_comment": + [ + "Elevation difference between successive rows of cliffs.", + "This is inversely proportional to 'frequency' in the map generation GUI. Specifically, when set from the GUI the value is 40 / frequency." + ], + "cliff_elevation_interval": 40, + + "_richness_comment": "Called 'cliff continuity' in the map generator GUI. 0 will result in no cliffs, 10 will make all cliff rows completely solid", + "richness": 1 + }, + + "_property_expression_names_comment": + [ + "Overrides for property value generators (map type)", + "Leave 'elevation' blank to get 'normal' terrain.", + "Use 'elevation': '0_16-elevation' to reproduce terrain from 0.16.", + "Use 'elevation': '0_17-island' to get an island.", + "Moisture and terrain type are also controlled via this.", + "'control-setting:moisture:frequency:multiplier' is the inverse of the 'moisture scale' in the map generator GUI.", + "'control-setting:moisture:bias' is the 'moisture bias' in the map generator GUI.", + "'control-setting:aux:frequency:multiplier' is the inverse of the 'terrain type scale' in the map generator GUI.", + "'control-setting:aux:bias' is the 'terrain type bias' in the map generator GUI." + ], + "property_expression_names": + { + "control-setting:moisture:frequency:multiplier": "1", + "control-setting:moisture:bias": "0", + "control-setting:aux:frequency:multiplier": "1", + "control-setting:aux:bias": "0" + }, + + "starting_points": + [ + { "x": 0, "y": 0} + ], + + "_seed_comment": "Use null for a random seed, number for a specific seed.", + "seed": null +} diff --git a/fle/services/docker/factorio/config/map-settings.json b/fle/services/docker/factorio/config/map-settings.json new file mode 100644 index 000000000..62798d92d --- /dev/null +++ b/fle/services/docker/factorio/config/map-settings.json @@ -0,0 +1,120 @@ +{ + "difficulty_settings": + { + "recipe_difficulty": 0, + "technology_difficulty": 0, + "technology_price_multiplier": 1, + "research_queue_setting": "after-victory" + }, + "pollution": + { + "enabled": true, + "_comment_min_to_diffuse_1": "these are values for 60 ticks (1 simulated second)", + "_comment_min_to_diffuse_2": "amount that is diffused to neighboring chunk", + "diffusion_ratio": 0.02, + "min_to_diffuse": 15, + "ageing": 1, + "expected_max_per_chunk": 150, + "min_to_show_per_chunk": 50, + "min_pollution_to_damage_trees": 60, + "pollution_with_max_forest_damage": 150, + "pollution_per_tree_damage": 50, + "pollution_restored_per_tree_damage": 10, + "max_pollution_to_restore_trees": 20, + "enemy_attack_pollution_consumption_modifier": 1 + }, + "enemy_evolution": + { + "enabled": true, + "time_factor": 0.000004, + "destroy_factor": 0.002, + "pollution_factor": 0.0000009 + }, + "enemy_expansion": + { + "enabled": true, + "min_base_spacing": 3, + "max_expansion_distance": 7, + "friendly_base_influence_radius": 2, + "enemy_building_influence_radius": 2, + "building_coefficient": 0.1, + "other_base_coefficient": 2.0, + "neighbouring_chunk_coefficient": 0.5, + "neighbouring_base_chunk_coefficient": 0.4, + "max_colliding_tiles_coefficient": 0.9, + "settler_group_min_size": 5, + "settler_group_max_size": 20, + "min_expansion_cooldown": 14400, + "max_expansion_cooldown": 216000 + }, + "unit_group": + { + "min_group_gathering_time": 3600, + "max_group_gathering_time": 36000, + "max_wait_time_for_late_members": 7200, + "max_group_radius": 30.0, + "min_group_radius": 5.0, + "max_member_speedup_when_behind": 1.4, + "max_member_slowdown_when_ahead": 0.6, + "max_group_slowdown_factor": 0.3, + "max_group_member_fallback_factor": 3, + "member_disown_distance": 10, + "tick_tolerance_when_member_arrives": 60, + "max_gathering_unit_groups": 30, + "max_unit_group_size": 200 + }, + "steering": + { + "default": + { + "radius": 1.2, + "separation_force": 0.005, + "separation_factor": 1.2, + "force_unit_fuzzy_goto_behavior": false + }, + "moving": + { + "radius": 3, + "separation_force": 0.01, + "separation_factor": 3, + "force_unit_fuzzy_goto_behavior": false + } + }, + "path_finder": + { + "fwd2bwd_ratio": 5, + "goal_pressure_ratio": 2, + "max_steps_worked_per_tick": 100, + "max_work_done_per_tick": 8000, + "use_path_cache": true, + "short_cache_size": 5, + "long_cache_size": 25, + "short_cache_min_cacheable_distance": 10, + "short_cache_min_algo_steps_to_cache": 50, + "long_cache_min_cacheable_distance": 30, + "cache_max_connect_to_cache_steps_multiplier": 100, + "cache_accept_path_start_distance_ratio": 0.2, + "cache_accept_path_end_distance_ratio": 0.15, + "negative_cache_accept_path_start_distance_ratio": 0.3, + "negative_cache_accept_path_end_distance_ratio": 0.3, + "cache_path_start_distance_rating_multiplier": 10, + "cache_path_end_distance_rating_multiplier": 20, + "stale_enemy_with_same_destination_collision_penalty": 30, + "ignore_moving_enemy_collision_distance": 5, + "enemy_with_different_destination_collision_penalty": 30, + "general_entity_collision_penalty": 10, + "general_entity_subsequent_collision_penalty": 3, + "extended_collision_penalty": 3, + "max_clients_to_accept_any_new_request": 10, + "max_clients_to_accept_short_new_request": 100, + "direct_distance_to_consider_short_request": 100, + "short_request_max_steps": 1000, + "short_request_ratio": 0.5, + "min_steps_to_check_path_find_termination": 2000, + "start_to_goal_cost_multiplier_to_terminate_path_find": 500.0, + "overload_levels": [0, 100, 500], + "overload_multipliers": [2, 3, 4], + "negative_path_cache_delay_interval": 20 + }, + "max_failed_behavior_count": 3 +} diff --git a/fle/services/docker/factorio/config/rconpw b/fle/services/docker/factorio/config/rconpw new file mode 100644 index 000000000..06432d896 --- /dev/null +++ b/fle/services/docker/factorio/config/rconpw @@ -0,0 +1 @@ +Phoh2chaiphai1V diff --git a/fle/services/docker/factorio/config/server-settings.json b/fle/services/docker/factorio/config/server-settings.json new file mode 100644 index 000000000..3f581fdc7 --- /dev/null +++ b/fle/services/docker/factorio/config/server-settings.json @@ -0,0 +1,72 @@ +{ + "name": "Name of the game as it will appear in the game listing", + "description": "Description of the game that will appear in the listing", + "tags": ["game", "tags"], + + "_comment_max_players": "Maximum number of players allowed, admins can join even a full server. 0 means unlimited.", + "max_players": 0, + + "_comment_visibility": ["public: Game will be published on the official Factorio matching server", + "lan: Game will be broadcast on LAN"], + "visibility": + { + "public": true, + "lan": true + }, + + "_comment_credentials": "Your factorio.com login credentials. Required for games with visibility public", + "username": "", + "password": "", + + "_comment_token": "Authentication token. May be used instead of 'password' above.", + "token": "", + + "game_password": "", + + "_comment_require_user_verification": "When set to true, the server will only allow clients that have a valid Factorio.com account", + "require_user_verification": true, + + "_comment_max_upload_in_kilobytes_per_second" : "optional, default value is 0. 0 means unlimited.", + "max_upload_in_kilobytes_per_second": 0, + + "_comment_max_upload_slots" : "optional, default value is 5. 0 means unlimited.", + "max_upload_slots": 5, + + "_comment_minimum_latency_in_ticks": "optional one tick is 16ms in default speed, default value is 0. 0 means no minimum.", + "minimum_latency_in_ticks": 0, + + "_comment_max_heartbeats_per_second": "Network tick rate. Maximum rate game updates packets are sent at before bundling them together. Minimum value is 6, maximum value is 240.", + "max_heartbeats_per_second": 60, + + "_comment_ignore_player_limit_for_returning_players": "Players that played on this map already can join even when the max player limit was reached.", + "ignore_player_limit_for_returning_players": false, + + "_comment_allow_commands": "possible values are, true, false and admins-only", + "allow_commands": "admins-only", + + "_comment_autosave_interval": "Autosave interval in minutes", + "autosave_interval": 10, + + "_comment_autosave_slots": "server autosave slots, it is cycled through when the server autosaves.", + "autosave_slots": 5, + + "_comment_afk_autokick_interval": "How many minutes until someone is kicked when doing nothing, 0 for never.", + "afk_autokick_interval": 0, + + "_comment_auto_pause": "Whether should the server be paused when no players are present.", + "auto_pause": true, + + "only_admins_can_pause_the_game": true, + + "_comment_autosave_only_on_server": "Whether autosaves should be saved only on server or also on all connected clients. Default is true.", + "autosave_only_on_server": true, + + "_comment_non_blocking_saving": "Highly experimental feature, enable only at your own risk of losing your saves. On UNIX systems, server will fork itself to create an autosave. Autosaving on connected Windows clients will be disabled regardless of autosave_only_on_server option.", + "non_blocking_saving": false, + + "_comment_segment_sizes": "Long network messages are split into segments that are sent over multiple ticks. Their size depends on the number of peers currently connected. Increasing the segment size will increase upload bandwidth requirement for the server and download bandwidth requirement for clients. This setting only affects server outbound messages. Changing these settings can have a negative impact on connection stability for some clients.", + "minimum_segment_size": 25, + "minimum_segment_size_peer_count": 20, + "maximum_segment_size": 100, + "maximum_segment_size_peer_count": 10 +} diff --git a/fle/services/rcon.py b/fle/services/rcon.py index a9e508c25..aa331f6dd 100644 --- a/fle/services/rcon.py +++ b/fle/services/rcon.py @@ -12,7 +12,7 @@ import io import contextlib -ROOT_DIR = Path(__file__).resolve().parent.parent.parent +ROOT_DIR = Path(__file__).resolve().parent.parent print(ROOT_DIR) @@ -33,12 +33,14 @@ def _load_scripts(scripts): def _get_mods_dir(): # get local execution path - return str(ROOT_DIR / "env" / "factorio" / "mods") + path = (ROOT_DIR / "env" / "game" / "factorio" / "mods") + print(path, path.exists()) + return str(path) def _get_dir(name="tools"): # get local execution path - return str(ROOT_DIR / "env" / "factorio" / name) + return str(ROOT_DIR / "env" / "game" / "factorio" / name) def _get_tool_names(name="tools") -> List[str]: From 7607d7ccb8a9bf22dbf6fc5753b3802b6306346a Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 13 Aug 2025 19:12:43 +0530 Subject: [PATCH 49/53] rm --- .../factorio/config/map-gen-settings.json | 78 ------------ .../docker/factorio/config/map-settings.json | 120 ------------------ fle/services/docker/factorio/config/rconpw | 1 - .../factorio/config/server-settings.json | 72 ----------- 4 files changed, 271 deletions(-) delete mode 100644 fle/services/docker/factorio/config/map-gen-settings.json delete mode 100644 fle/services/docker/factorio/config/map-settings.json delete mode 100644 fle/services/docker/factorio/config/rconpw delete mode 100644 fle/services/docker/factorio/config/server-settings.json diff --git a/fle/services/docker/factorio/config/map-gen-settings.json b/fle/services/docker/factorio/config/map-gen-settings.json deleted file mode 100644 index 7a2b41281..000000000 --- a/fle/services/docker/factorio/config/map-gen-settings.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "_terrain_segmentation_comment": "The inverse of 'water scale' in the map generator GUI.", - "terrain_segmentation": 1, - - "_water_comment": - [ - "The equivalent to 'water coverage' in the map generator GUI. Higher coverage means more water in larger oceans.", - "Water level = 10 * log2(this value)" - ], - "water": 1, - - "_comment_width+height": "Width and height of map, in tiles; 0 means infinite", - "width": 0, - "height": 0, - - "_starting_area_comment": "Multiplier for 'biter free zone radius'", - "starting_area": 1, - - "peaceful_mode": false, - "autoplace_controls": - { - "coal": {"frequency": 1, "size": 1, "richness": 1}, - "stone": {"frequency": 1, "size": 1, "richness": 1}, - "copper-ore": {"frequency": 1, "size": 1,"richness": 1}, - "iron-ore": {"frequency": 1, "size": 1, "richness": 1}, - "uranium-ore": {"frequency": 1, "size": 1, "richness": 1}, - "crude-oil": {"frequency": 1, "size": 1, "richness": 1}, - "trees": {"frequency": 1, "size": 1, "richness": 1}, - "enemy-base": {"frequency": 1, "size": 1, "richness": 1} - }, - - "cliff_settings": - { - "_name_comment": "Name of the cliff prototype", - "name": "cliff", - - "_cliff_elevation_0_comment": "Elevation of first row of cliffs", - "cliff_elevation_0": 10, - - "_cliff_elevation_interval_comment": - [ - "Elevation difference between successive rows of cliffs.", - "This is inversely proportional to 'frequency' in the map generation GUI. Specifically, when set from the GUI the value is 40 / frequency." - ], - "cliff_elevation_interval": 40, - - "_richness_comment": "Called 'cliff continuity' in the map generator GUI. 0 will result in no cliffs, 10 will make all cliff rows completely solid", - "richness": 1 - }, - - "_property_expression_names_comment": - [ - "Overrides for property value generators (map type)", - "Leave 'elevation' blank to get 'normal' terrain.", - "Use 'elevation': '0_16-elevation' to reproduce terrain from 0.16.", - "Use 'elevation': '0_17-island' to get an island.", - "Moisture and terrain type are also controlled via this.", - "'control-setting:moisture:frequency:multiplier' is the inverse of the 'moisture scale' in the map generator GUI.", - "'control-setting:moisture:bias' is the 'moisture bias' in the map generator GUI.", - "'control-setting:aux:frequency:multiplier' is the inverse of the 'terrain type scale' in the map generator GUI.", - "'control-setting:aux:bias' is the 'terrain type bias' in the map generator GUI." - ], - "property_expression_names": - { - "control-setting:moisture:frequency:multiplier": "1", - "control-setting:moisture:bias": "0", - "control-setting:aux:frequency:multiplier": "1", - "control-setting:aux:bias": "0" - }, - - "starting_points": - [ - { "x": 0, "y": 0} - ], - - "_seed_comment": "Use null for a random seed, number for a specific seed.", - "seed": null -} diff --git a/fle/services/docker/factorio/config/map-settings.json b/fle/services/docker/factorio/config/map-settings.json deleted file mode 100644 index 62798d92d..000000000 --- a/fle/services/docker/factorio/config/map-settings.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "difficulty_settings": - { - "recipe_difficulty": 0, - "technology_difficulty": 0, - "technology_price_multiplier": 1, - "research_queue_setting": "after-victory" - }, - "pollution": - { - "enabled": true, - "_comment_min_to_diffuse_1": "these are values for 60 ticks (1 simulated second)", - "_comment_min_to_diffuse_2": "amount that is diffused to neighboring chunk", - "diffusion_ratio": 0.02, - "min_to_diffuse": 15, - "ageing": 1, - "expected_max_per_chunk": 150, - "min_to_show_per_chunk": 50, - "min_pollution_to_damage_trees": 60, - "pollution_with_max_forest_damage": 150, - "pollution_per_tree_damage": 50, - "pollution_restored_per_tree_damage": 10, - "max_pollution_to_restore_trees": 20, - "enemy_attack_pollution_consumption_modifier": 1 - }, - "enemy_evolution": - { - "enabled": true, - "time_factor": 0.000004, - "destroy_factor": 0.002, - "pollution_factor": 0.0000009 - }, - "enemy_expansion": - { - "enabled": true, - "min_base_spacing": 3, - "max_expansion_distance": 7, - "friendly_base_influence_radius": 2, - "enemy_building_influence_radius": 2, - "building_coefficient": 0.1, - "other_base_coefficient": 2.0, - "neighbouring_chunk_coefficient": 0.5, - "neighbouring_base_chunk_coefficient": 0.4, - "max_colliding_tiles_coefficient": 0.9, - "settler_group_min_size": 5, - "settler_group_max_size": 20, - "min_expansion_cooldown": 14400, - "max_expansion_cooldown": 216000 - }, - "unit_group": - { - "min_group_gathering_time": 3600, - "max_group_gathering_time": 36000, - "max_wait_time_for_late_members": 7200, - "max_group_radius": 30.0, - "min_group_radius": 5.0, - "max_member_speedup_when_behind": 1.4, - "max_member_slowdown_when_ahead": 0.6, - "max_group_slowdown_factor": 0.3, - "max_group_member_fallback_factor": 3, - "member_disown_distance": 10, - "tick_tolerance_when_member_arrives": 60, - "max_gathering_unit_groups": 30, - "max_unit_group_size": 200 - }, - "steering": - { - "default": - { - "radius": 1.2, - "separation_force": 0.005, - "separation_factor": 1.2, - "force_unit_fuzzy_goto_behavior": false - }, - "moving": - { - "radius": 3, - "separation_force": 0.01, - "separation_factor": 3, - "force_unit_fuzzy_goto_behavior": false - } - }, - "path_finder": - { - "fwd2bwd_ratio": 5, - "goal_pressure_ratio": 2, - "max_steps_worked_per_tick": 100, - "max_work_done_per_tick": 8000, - "use_path_cache": true, - "short_cache_size": 5, - "long_cache_size": 25, - "short_cache_min_cacheable_distance": 10, - "short_cache_min_algo_steps_to_cache": 50, - "long_cache_min_cacheable_distance": 30, - "cache_max_connect_to_cache_steps_multiplier": 100, - "cache_accept_path_start_distance_ratio": 0.2, - "cache_accept_path_end_distance_ratio": 0.15, - "negative_cache_accept_path_start_distance_ratio": 0.3, - "negative_cache_accept_path_end_distance_ratio": 0.3, - "cache_path_start_distance_rating_multiplier": 10, - "cache_path_end_distance_rating_multiplier": 20, - "stale_enemy_with_same_destination_collision_penalty": 30, - "ignore_moving_enemy_collision_distance": 5, - "enemy_with_different_destination_collision_penalty": 30, - "general_entity_collision_penalty": 10, - "general_entity_subsequent_collision_penalty": 3, - "extended_collision_penalty": 3, - "max_clients_to_accept_any_new_request": 10, - "max_clients_to_accept_short_new_request": 100, - "direct_distance_to_consider_short_request": 100, - "short_request_max_steps": 1000, - "short_request_ratio": 0.5, - "min_steps_to_check_path_find_termination": 2000, - "start_to_goal_cost_multiplier_to_terminate_path_find": 500.0, - "overload_levels": [0, 100, 500], - "overload_multipliers": [2, 3, 4], - "negative_path_cache_delay_interval": 20 - }, - "max_failed_behavior_count": 3 -} diff --git a/fle/services/docker/factorio/config/rconpw b/fle/services/docker/factorio/config/rconpw deleted file mode 100644 index 06432d896..000000000 --- a/fle/services/docker/factorio/config/rconpw +++ /dev/null @@ -1 +0,0 @@ -Phoh2chaiphai1V diff --git a/fle/services/docker/factorio/config/server-settings.json b/fle/services/docker/factorio/config/server-settings.json deleted file mode 100644 index 3f581fdc7..000000000 --- a/fle/services/docker/factorio/config/server-settings.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "name": "Name of the game as it will appear in the game listing", - "description": "Description of the game that will appear in the listing", - "tags": ["game", "tags"], - - "_comment_max_players": "Maximum number of players allowed, admins can join even a full server. 0 means unlimited.", - "max_players": 0, - - "_comment_visibility": ["public: Game will be published on the official Factorio matching server", - "lan: Game will be broadcast on LAN"], - "visibility": - { - "public": true, - "lan": true - }, - - "_comment_credentials": "Your factorio.com login credentials. Required for games with visibility public", - "username": "", - "password": "", - - "_comment_token": "Authentication token. May be used instead of 'password' above.", - "token": "", - - "game_password": "", - - "_comment_require_user_verification": "When set to true, the server will only allow clients that have a valid Factorio.com account", - "require_user_verification": true, - - "_comment_max_upload_in_kilobytes_per_second" : "optional, default value is 0. 0 means unlimited.", - "max_upload_in_kilobytes_per_second": 0, - - "_comment_max_upload_slots" : "optional, default value is 5. 0 means unlimited.", - "max_upload_slots": 5, - - "_comment_minimum_latency_in_ticks": "optional one tick is 16ms in default speed, default value is 0. 0 means no minimum.", - "minimum_latency_in_ticks": 0, - - "_comment_max_heartbeats_per_second": "Network tick rate. Maximum rate game updates packets are sent at before bundling them together. Minimum value is 6, maximum value is 240.", - "max_heartbeats_per_second": 60, - - "_comment_ignore_player_limit_for_returning_players": "Players that played on this map already can join even when the max player limit was reached.", - "ignore_player_limit_for_returning_players": false, - - "_comment_allow_commands": "possible values are, true, false and admins-only", - "allow_commands": "admins-only", - - "_comment_autosave_interval": "Autosave interval in minutes", - "autosave_interval": 10, - - "_comment_autosave_slots": "server autosave slots, it is cycled through when the server autosaves.", - "autosave_slots": 5, - - "_comment_afk_autokick_interval": "How many minutes until someone is kicked when doing nothing, 0 for never.", - "afk_autokick_interval": 0, - - "_comment_auto_pause": "Whether should the server be paused when no players are present.", - "auto_pause": true, - - "only_admins_can_pause_the_game": true, - - "_comment_autosave_only_on_server": "Whether autosaves should be saved only on server or also on all connected clients. Default is true.", - "autosave_only_on_server": true, - - "_comment_non_blocking_saving": "Highly experimental feature, enable only at your own risk of losing your saves. On UNIX systems, server will fork itself to create an autosave. Autosaving on connected Windows clients will be disabled regardless of autosave_only_on_server option.", - "non_blocking_saving": false, - - "_comment_segment_sizes": "Long network messages are split into segments that are sent over multiple ticks. Their size depends on the number of peers currently connected. Increasing the segment size will increase upload bandwidth requirement for the server and download bandwidth requirement for clients. This setting only affects server outbound messages. Changing these settings can have a negative impact on connection stability for some clients.", - "minimum_segment_size": 25, - "minimum_segment_size_peer_count": 20, - "maximum_segment_size": 100, - "maximum_segment_size_peer_count": 10 -} From c5e3eb5ce685a9a8f2ea80172effdce38cd88d47 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 14 Aug 2025 00:25:06 +0530 Subject: [PATCH 50/53] fixes to be able to run --- fle/env/game/instance.py | 17 +++++++------- fle/env/game/lua_manager.py | 5 ++--- fle/env/game/namespace.py | 2 +- fle/env/game/tools/agent/move_to/client.py | 2 +- fle/env/session.py | 22 ++++++++++--------- fle/env/trajectory_runner.py | 10 ++++----- .../system_prompt_generator.py | 1 + fle/run.py | 2 ++ fle/run_eval.py | 13 ++++++----- fle/services/rcon.py | 4 +--- 10 files changed, 41 insertions(+), 37 deletions(-) diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index 745c9c1b6..8c1944d63 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -68,8 +68,9 @@ class AgentInstance: last_message_timestamp: float # _last_production_flow: Optional[ProductionFlows] - def __init__(self, agent_idx: int): + def __init__(self, agent_idx: int, namespace: FactorioNamespace): self.agent_idx = agent_idx + self.namespace = namespace @property def color(self) -> str: @@ -165,7 +166,7 @@ def _agent_character_command(self) -> List[str]: class FactorioInstance: namespace_class = FactorioNamespace _cleanup_registered = False # Only register cleanup once per process - agent_instances: List[AgentInstance] + agent_instances: Dict[int, AgentInstance] client: FactorioClient def __init__( @@ -189,7 +190,7 @@ def __init__( self._is_initialised = False self.peaceful = peaceful - self.agent_instances = [AgentInstance(i) for i in range(num_agents)] + self.agent_instances = {i: AgentInstance(i, FactorioNamespace(self, i)) for i in range(num_agents)} # Register controllers with the server @@ -277,7 +278,7 @@ def _reset(self, inventories: List[Dict[str, Any]]): "/sc global.agent_characters[1].force.research_all_technologies()", raw=True, ) - for instance in self.agent_instances: + for instance in self.agent_instances.values(): instance._reset(self.client, inventories[instance.agent_idx]) # self.clear_entities() self._reset_static_achievement_counters() @@ -289,7 +290,7 @@ def reset(self, game_state: Optional[GameState] = None): not game_state or len(game_state.inventories) == self.num_agents ), "Game state must have the same number of inventories as num_agents" - for instance in self.agent_instances: + for instance in self.agent_instances.values(): instance.namespace.reset() if not game_state: @@ -323,7 +324,7 @@ def reset(self, game_state: Optional[GameState] = None): if game_state.agent_messages: for i in range(self.num_agents): if i < len(game_state.agent_messages): - self.agent_instances[i].namespace.load_messages( + self.agent_instances[i].namespace.load_messages( # type: ignore game_state.agent_messages[i] ) @@ -346,7 +347,7 @@ def reset(self, game_state: Optional[GameState] = None): def initialise(self, fast=True, invalidate_cache=False): self.reconnect_client() self.client.register_controllers( - [instance.namespace for instance in self.agent_instances], + [instance.namespace for instance in self.agent_instances.values()], invalidate_cache=invalidate_cache, ) @@ -389,7 +390,7 @@ def _create_agent_game_characters(self): f'/sc global.agent_characters = {{}};', raw=True, ) - for instance in self.agent_instances: + for instance in self.agent_instances.values(): for command in instance._agent_character_command(): t.add_command(command, raw=True) t.add_command("/sc player = global.agent_characters[1]", raw=True) diff --git a/fle/env/game/lua_manager.py b/fle/env/game/lua_manager.py index 2282b58bb..5458d4a11 100644 --- a/fle/env/game/lua_manager.py +++ b/fle/env/game/lua_manager.py @@ -76,8 +76,9 @@ def __init__( # self.action_directory = _get_action_dir() self.lib_directory = _get_mods_dir() + self.game_checksums = {} if cache_scripts: - self.init_action_checksums() + res = self.init_action_checksums() self.game_checksums = self._get_game_checksums() self.tool_scripts = self.get_tools_to_load() @@ -152,7 +153,6 @@ def load_tool_into_game(self, name): print(f"{self.rcon_client.port}: Loading action {script_name} into game") self.rcon_client.send_command("/sc " + script) - pass def load_init_into_game(self, name): if name not in self.lib_scripts: @@ -208,7 +208,6 @@ def get_libs_to_load(self): name, content = _load_script(filename) if self.cache_scripts: checksum = self.calculate_checksum(content) - if ( name not in self.game_checksums or self.game_checksums[name] != checksum diff --git a/fle/env/game/namespace.py b/fle/env/game/namespace.py index 51184adb5..49e71fcb1 100644 --- a/fle/env/game/namespace.py +++ b/fle/env/game/namespace.py @@ -59,7 +59,7 @@ def __init__(self, instance, agent_index): self.line_value = 0 self.persistent_vars = {} self.instance = instance - self.tcp_port = instance.tcp_port + self.tcp_port = instance.client.rcon_port self.max_sequential_exception_count = 1 self._sequential_exception_count = 0 self.log_counter = 0 diff --git a/fle/env/game/tools/agent/move_to/client.py b/fle/env/game/tools/agent/move_to/client.py index b9e7ab805..2fee5bc4c 100644 --- a/fle/env/game/tools/agent/move_to/client.py +++ b/fle/env/game/tools/agent/move_to/client.py @@ -7,7 +7,7 @@ from fle.env.game.tools.admin.get_path.client import GetPath from fle.env.game.tools.admin.request_path.client import RequestPath from fle.env.game.tools import Tool -from fle.env.lua_manager import LuaScriptManager +from fle.env.game.lua_manager import LuaScriptManager class MoveTo(Tool): diff --git a/fle/env/session.py b/fle/env/session.py index 85248a6d5..2604c40d9 100644 --- a/fle/env/session.py +++ b/fle/env/session.py @@ -28,6 +28,7 @@ from fle.services.docker.docker_manager import FactorioHeadlessServer from fle.services.db.db_client import DBClient from fle.env.tasks import TaskABC +from fle.services.db.db_client import create_db_client class AgentSession: @@ -169,7 +170,7 @@ async def save_program(self, program: Program) -> Program: program.update_program_id(program.id) return program - async def get_resume_state(self) -> Tuple[GameState, Conversation]: + async def get_resume_state(self, process_id: int) -> Tuple[GameState, Conversation]: ( current_state, agent_conversation, @@ -177,7 +178,7 @@ async def get_resume_state(self) -> Tuple[GameState, Conversation]: depth, ) = await self.db_client.get_resume_state( resume_version=self.version, - process_id=self.process_id, + process_id=process_id, agent_idx=self.agent_idx, ) return current_state, agent_conversation, parent_id, depth @@ -295,15 +296,16 @@ def __init__( server: FactorioHeadlessServer, config: GameConfig, task: Optional[TaskABC] = None, - ): + ) -> None: self.instance_id = instance_id self.instance = instance self.server = server - self.agent_sessions = self._make_agent_sessions() - self.current_game_state = None self.config = config self.task = task + async def initialise(self) -> None: + self.agent_sessions = await self._make_agent_sessions() + @property def num_agents(self) -> int: return self.config.num_agents @@ -330,9 +332,9 @@ def _snapshot(self) -> "GameSession.Snapshot": self.current_game_info, ) - async def get_agent_session_resume_state(self, agent_idx: int) -> Tuple[GameState, Conversation]: + async def get_agent_session_resume_state(self, agent_idx: int, process_id: int) -> Tuple[GameState, Conversation]: agent_session = self.agent_sessions[agent_idx] - current_state, agent_conversation, parent_id, depth = await agent_session.get_resume_state() + current_state, agent_conversation, parent_id, depth = await agent_session.get_resume_state(process_id=process_id) if current_state: agent_session.steps = depth if not current_state and self.task: @@ -372,10 +374,10 @@ def verify_task( ) game_result.result = task_response return game_result - - def _make_agent_sessions(self) -> Dict[int, AgentSession]: + + async def _make_agent_sessions(self) -> Dict[int, AgentSession]: return { - i: AgentSession(i, self.instance.agent_instances[i], self.db_client) + i: AgentSession(i, self.instance.agent_instances[i], await create_db_client()) for i in range(self.instance.num_agents) } diff --git a/fle/env/trajectory_runner.py b/fle/env/trajectory_runner.py index 6f4d91d4d..dbc69a0a3 100644 --- a/fle/env/trajectory_runner.py +++ b/fle/env/trajectory_runner.py @@ -22,7 +22,6 @@ class GymTrajectoryRunner: config: GymEvalConfig gym_env: FactorioGymEnv process_id: int - session_manager: GameSessionManager agents: Dict[int, GymAgent] def __init__( @@ -30,7 +29,6 @@ def __init__( config: GymEvalConfig, gym_env: FactorioGymEnv, process_id: int, - session_manager: GameSessionManager, log_dir: Optional[str] = None, ): self.config = config @@ -38,7 +36,6 @@ def __init__( self.gym_env = gym_env self.process_id = process_id self.start_time = time.time() - self.session_manager = session_manager self.logger = TrajectoryLogger( start_time=self.start_time, trajectory_length=self.config.task.trajectory_length, @@ -85,9 +82,10 @@ async def _initialize_trajectory_state(self) -> Tuple[GameState, List[int]]: Tuple of (current_state, agent_steps) """ game_session = self.gym_env.game_session + await game_session.initialise() first_idx = list(game_session.agent_sessions.keys())[0] current_state, agent_conversation = ( - await game_session.get_agent_session_resume_state(first_idx) + await game_session.get_agent_session_resume_state(first_idx, self.process_id) ) if agent_conversation: self.agents[first_idx].reset(agent_conversation) @@ -178,10 +176,10 @@ async def run_sequential(self): # Initialize state based on resume or fresh start game_session = self.gym_env.game_session - current_state, _ = await self._initialize_trajectory_state() + current_state = await self._initialize_trajectory_state() # Save system prompts for all agents at the start - for agent_idx, agent in enumerate(self.agents): + for agent_idx, agent in self.agents.items(): self.logger.save_system_prompt(agent, agent_idx) # Run trajectory diff --git a/fle/env/utils/controller_loader/system_prompt_generator.py b/fle/env/utils/controller_loader/system_prompt_generator.py index 33973f931..fa49e923c 100644 --- a/fle/env/utils/controller_loader/system_prompt_generator.py +++ b/fle/env/utils/controller_loader/system_prompt_generator.py @@ -33,6 +33,7 @@ def generate(self, num_agents: int = 1, agent_idx: int = 0) -> str: # Load and process the manuals (agent.md files) manual_defs = ManualGenerator.generate_manual(str(self.base_path / "tools")) + multiagent_str = "" if num_agents > 1: player_idx = agent_idx + 1 diff --git a/fle/run.py b/fle/run.py index f2aac8caa..71afb7e11 100644 --- a/fle/run.py +++ b/fle/run.py @@ -89,6 +89,7 @@ async def ensure_running(): asyncio.run(ensure_running()) except Exception as e: print(f"Warning: could not ensure cluster running automatically ({e}). Continuing...") + raise e config_path = Path(args.config) if not config_path.exists(): print(f"Error: Config file '{args.config}' not found.", file=sys.stderr) @@ -100,6 +101,7 @@ async def ensure_running(): print("\nInterrupted by user.", file=sys.stderr) sys.exit(1) except Exception as e: + raise e print(f"Error: {e}", file=sys.stderr) sys.exit(1) diff --git a/fle/run_eval.py b/fle/run_eval.py index eb87d7080..d5caaaaac 100644 --- a/fle/run_eval.py +++ b/fle/run_eval.py @@ -10,6 +10,7 @@ from fle.agents.gym_agent import GymAgent from fle.commons.cluster_ips import get_local_container_ips +from fle.env.environment import FactorioGymEnv from fle.env.gym_env.config import GymEvalConfig, GymRunConfig from fle.env.gym_env.observation_formatter import BasicObservationFormatter from fle.env.gym_env.system_prompt_formatter import SystemPromptFormatter @@ -114,15 +115,17 @@ async def main(): raise ValueError(f"Could not get environment info for {run_config.env_id}") # Create gym environment to get task and instance - gym_env = gym.make(run_config.env_id) - task = gym_env.unwrapped.task - instance = gym_env.unwrapped.instance + gym_env: FactorioGymEnv = gym.make(run_config.env_id) + await gym_env.game_session.initialise() + task = gym_env.game_session.task + instance = gym_env.game_session.instance # Create agents and their agent cards agents = dict() agent_cards = [] - for agent_idx in range(run_config.num_agents): - system_prompt = instance.get_system_prompt(agent_idx) + num_agents = run_config.num_agents + for agent_idx in range(num_agents): + system_prompt = instance.agent_instances[agent_idx].get_system_prompt(num_agents=num_agents) agent = GymAgent( model=run_config.model, system_prompt=system_prompt, diff --git a/fle/services/rcon.py b/fle/services/rcon.py index aa331f6dd..a82b1f9b6 100644 --- a/fle/services/rcon.py +++ b/fle/services/rcon.py @@ -13,7 +13,6 @@ import contextlib ROOT_DIR = Path(__file__).resolve().parent.parent -print(ROOT_DIR) def _load_script(filename): @@ -34,13 +33,12 @@ def _load_scripts(scripts): def _get_mods_dir(): # get local execution path path = (ROOT_DIR / "env" / "game" / "factorio" / "mods") - print(path, path.exists()) return str(path) def _get_dir(name="tools"): # get local execution path - return str(ROOT_DIR / "env" / "game" / "factorio" / name) + return str(ROOT_DIR / "env" / "game" / name) def _get_tool_names(name="tools") -> List[str]: From 2d5000fe23fc34034c296355829ff443a03f5bb6 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 14 Aug 2025 13:07:57 +0530 Subject: [PATCH 51/53] redo fixtures --- tests/conftest.py | 123 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 32 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 1b58e2284..9e25be4ad 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,8 +4,9 @@ import pytest -from fle.commons.cluster_ips import get_local_container_ips from fle.env.game import FactorioInstance +from fle.env.game.factorio_client import FactorioClient +from fle.services.docker.config import DockerConfig # Add the src directory to the Python path src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -21,39 +22,97 @@ # if str(project_root / 'src') not in sys.path: # sys.path.insert(0, str(project_root / 'src')) - -@pytest.fixture() # scope="session") +@pytest.fixture() def instance(): - # from gym import FactorioInstance - ips, udp_ports, tcp_ports = get_local_container_ips() + """Create a connected FactorioInstance using the new FactorioClient API. + + Requires a running local Factorio headless container started via the docker manager. + If none are running, the tests using this fixture will be skipped. + """ + config = DockerConfig() + + # Read RCON password used by the local headless server + client = FactorioClient( + instance_id=0, + rcon_port=config.rcon_port, + address=config.address, + rcon_password=config.factorio_password, + cache_scripts=True, + ) + + inventory = { + "coal": 50, + "copper-plate": 50, + "iron-plate": 50, + "iron-chest": 2, + "burner-mining-drill": 3, + "electric-mining-drill": 1, + "assembling-machine-1": 1, + "stone-furnace": 9, + "transport-belt": 50, + "boiler": 1, + "burner-inserter": 32, + "pipe": 15, + "steam-engine": 1, + "small-electric-pole": 10, + } + + inst = None + try: + inst = FactorioInstance( + client=client, + fast=True, + inventory=inventory, + ) + yield inst + finally: + if inst is not None: + inst.cleanup() + + +@pytest.fixture() +def unresearched_instance(): + """Create a FactorioInstance with no technologies researched. + + Requires a running local Factorio headless container started via the docker manager. + If none are running, the tests using this fixture will be skipped. + """ + config = DockerConfig() + + client = FactorioClient( + instance_id=0, + rcon_port=config.rcon_port, + address=config.address, + rcon_password=config.factorio_password, + cache_scripts=True, + ) + + inventory = { + "coal": 50, + "copper-plate": 50, + "iron-plate": 50, + "iron-chest": 2, + "burner-mining-drill": 3, + "electric-mining-drill": 1, + "assembling-machine-1": 1, + "stone-furnace": 9, + "transport-belt": 50, + "boiler": 1, + "burner-inserter": 32, + "pipe": 15, + "steam-engine": 1, + "small-electric-pole": 10, + } + + inst = None try: - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=tcp_ports[-1], # 27019, - cache_scripts=False, + inst = FactorioInstance( + client=client, fast=True, - inventory={ - "coal": 50, - "copper-plate": 50, - "iron-plate": 50, - "iron-chest": 2, - "burner-mining-drill": 3, - "electric-mining-drill": 1, - "assembling-machine-1": 1, - "stone-furnace": 9, - "transport-belt": 50, - "boiler": 1, - "burner-inserter": 32, - "pipe": 15, - "steam-engine": 1, - "small-electric-pole": 10, - }, + inventory=inventory, + all_technologies_researched=False, ) - yield instance - except Exception as e: - raise e + yield inst finally: - # Cleanup RCON connections to prevent connection leaks - if "instance" in locals(): - instance.cleanup() + if inst is not None: + inst.cleanup() From bd58368f795f6b54141638c0a3cd1058704d4255 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 14 Aug 2025 13:51:19 +0530 Subject: [PATCH 52/53] update tests --- tests/actions/test_can_place.py | 3 +- .../actions/test_connect_entities_dry_run.py | 1 + tests/actions/test_craft.py | 1 + tests/actions/test_extract_item.py | 1 + tests/actions/test_get_entities.py | 3 +- tests/actions/test_get_entity.py | 1 + tests/actions/test_get_prototype_recipe.py | 1 + tests/actions/test_get_research_progress.py | 19 +- tests/actions/test_get_resource_patch.py | 1 + tests/actions/test_harvest_resource.py | 132 +++--- tests/actions/test_insert_item.py | 1 + tests/actions/test_inspect_inventory.py | 1 + tests/actions/test_move_to.py | 1 + tests/actions/test_nearest.py | 1 + tests/actions/test_nearest_buildable.py | 34 +- tests/actions/test_pickup_entity.py | 1 + tests/actions/test_place.py | 1 + tests/actions/test_place_entity_next_to.py | 1 + .../actions/test_place_next_to_and_rotate.py | 1 + tests/actions/test_print.py | 1 + tests/actions/test_render.py | 1 + tests/actions/test_request_path.py | 1 + tests/actions/test_rotate.py | 1 + tests/actions/test_score.py | 1 + tests/actions/test_set_entity_recipe.py | 1 + tests/actions/test_set_research.py | 21 +- tests/actions/test_sleep.py | 1 + tests/benchmarks/test_demo_video.py | 1 + tests/benchmarks/test_elapsed_ticks.py | 1 + tests/benchmarks/test_script_caching.py | 1 + .../test_blueprint_based_policies.py | 1 + tests/blueprints/test_save_load.py | 1 + tests/complex/test_edge_cases.py | 1 + tests/complex/test_slow.py | 1 + tests/connect/test_connect_pipes.py | 1 + tests/connect/test_connect_poles.py | 1 + tests/connect/test_connect_transport_belts.py | 1 + .../connect/test_connect_underground_belts.py | 1 + .../connect/test_connect_underground_pipes.py | 1 + tests/connect/test_connect_walls.py | 1 + tests/connect/test_fluid_handlers.py | 1 + tests/entities/test_assemblers.py | 1 + tests/entities/test_drill.py | 1 + tests/entities/test_fluid_processors.py | 1 + tests/entities/test_inserters.py | 1 + tests/entities/test_nuclear_reactor.py | 1 + tests/entities/test_power_generators.py | 1 + tests/entities/test_rockets.py | 1 + tests/eval/samplers/test_kld_mean_sampler.py | 1 + tests/eval/samplers/test_python_parser.py | 1 + .../samplers/test_weighted_reward_sampler.py | 1 + tests/eval/test_achievements.py | 1 + tests/eval/test_conversation_formatter.py | 1 + tests/eval/test_game_state.py | 1 + tests/eval/test_mcts_chunker.py | 1 + tests/eval/test_production_divergence.py | 1 + tests/eval/test_profits.py | 24 +- tests/eval/test_python_parser.py | 1 + tests/eval/test_recursive_formatter.py | 1 + tests/eval/test_save_load_python_namespace.py | 391 +++++++----------- ...test_auto_fueling_iron_smelting_factory.py | 1 + tests/functional/test_auto_refilling_coal.py | 1 + tests/functional/test_chemical_plants.py | 1 + tests/functional/test_defence.py | 1 + tests/functional/test_electricity_unit.py | 1 + tests/functional/test_factory_unit.py | 1 + tests/functional/test_full_oil_chain.py | 1 + .../test_multi_drill_multi_furnace.py | 1 + .../test_multiple_drills_multiple_furnaces.py | 1 + tests/functional/test_objectives.py | 1 + tests/functional/test_research.py | 17 +- tests/functional/test_small_iron_factory.py | 1 + tests/gym_env/test_interface.py | 1 + tests/gym_env/test_observation.py | 1 + tests/gym_env/test_observation_formatter.py | 1 + tests/gym_env/test_registry.py | 1 + tests/multiagent/test_actions_multiagent.py | 1 + tests/multiagent/test_agent_instructions.py | 1 + tests/multiagent/test_messages.py | 1 + tests/status/test_pipes_status.py | 1 + tests/status/test_poles_status.py | 1 + tests/status/test_pump_status.py | 1 + tests/test_eval.py | 172 ++------ tests/test_functions.py | 1 + tests/test_inventory_deduction.py | 1 + tests/test_production_stats.py | 1 + tests/test_rcon_utils.py | 1 + tests/test_variables.py | 1 + tests/test_warnings.py | 37 +- 89 files changed, 386 insertions(+), 545 deletions(-) diff --git a/tests/actions/test_can_place.py b/tests/actions/test_can_place.py index c3c2711a9..50ff68892 100644 --- a/tests/actions/test_can_place.py +++ b/tests/actions/test_can_place.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Direction @@ -11,7 +12,7 @@ def game(instance: FactorioInstance): instance.reset() -def test_can_place(instance): +def test_can_place(game): """ Place a boiler at (0, 0) :param game: diff --git a/tests/actions/test_connect_entities_dry_run.py b/tests/actions/test_connect_entities_dry_run.py index 28db28140..2e40f2dd0 100644 --- a/tests/actions/test_connect_entities_dry_run.py +++ b/tests/actions/test_connect_entities_dry_run.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/actions/test_craft.py b/tests/actions/test_craft.py index 2d9a05b73..9c664d40d 100644 --- a/tests/actions/test_craft.py +++ b/tests/actions/test_craft.py @@ -1,5 +1,6 @@ import pytest +from fle.env.game import FactorioInstance from fle.env.game.game_types import Prototype, Resource diff --git a/tests/actions/test_extract_item.py b/tests/actions/test_extract_item.py index 58950f2bd..74a98f207 100644 --- a/tests/actions/test_extract_item.py +++ b/tests/actions/test_extract_item.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/actions/test_get_entities.py b/tests/actions/test_get_entities.py index 845e5791c..9e29c9d2e 100644 --- a/tests/actions/test_get_entities.py +++ b/tests/actions/test_get_entities.py @@ -1,5 +1,6 @@ +from fle.env.game import FactorioInstance import pytest - +from fle.env.game import FactorioInstance from fle.env.game import entities as ent from fle.env.game.game_types import Prototype, Resource diff --git a/tests/actions/test_get_entity.py b/tests/actions/test_get_entity.py index 6b7c5636d..78c90b3eb 100644 --- a/tests/actions/test_get_entity.py +++ b/tests/actions/test_get_entity.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import DirectionInternal diff --git a/tests/actions/test_get_prototype_recipe.py b/tests/actions/test_get_prototype_recipe.py index 6eaead2cf..f8fb5aa9b 100644 --- a/tests/actions/test_get_prototype_recipe.py +++ b/tests/actions/test_get_prototype_recipe.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.game_types import Prototype diff --git a/tests/actions/test_get_research_progress.py b/tests/actions/test_get_research_progress.py index d869699d6..0916eb8d4 100644 --- a/tests/actions/test_get_research_progress.py +++ b/tests/actions/test_get_research_progress.py @@ -2,24 +2,13 @@ from fle.env.game import FactorioInstance from fle.env.game.game_types import Technology -from fle.commons.cluster_ips import get_local_container_ips @pytest.fixture() -def game(instance: FactorioInstance): - # game.initial_inventory = {'assembling-machine-1': 1} - ips, udp_ports, tcp_ports = get_local_container_ips() - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=tcp_ports[-1], # 27019, - all_technologies_researched=False, - fast=True, - inventory={}, - ) - instance.reset() - yield instance.namespace - instance.reset() +def game(unresearched_instance: FactorioInstance): + unresearched_instance.reset() + yield unresearched_instance.namespace + unresearched_instance.reset() def test_get_research_progress_automation(game): diff --git a/tests/actions/test_get_resource_patch.py b/tests/actions/test_get_resource_patch.py index 7cdb5f6dd..d8477650d 100644 --- a/tests/actions/test_get_resource_patch.py +++ b/tests/actions/test_get_resource_patch.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import ResourcePatch diff --git a/tests/actions/test_harvest_resource.py b/tests/actions/test_harvest_resource.py index 7a154ec64..0201e6b35 100644 --- a/tests/actions/test_harvest_resource.py +++ b/tests/actions/test_harvest_resource.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position @@ -7,23 +8,22 @@ @pytest.fixture() def game(instance: FactorioInstance): instance.reset() - yield instance.namespace + yield instance # instance.reset() -def test_harvest_resource_with_full_inventory(game): +def test_harvest_resource_with_full_inventory(game: FactorioInstance): """ Find the nearest coal resource patch and harvest 5 coal from it. :param game: :return: """ - game.instance.initial_inventory = {**game.instance.initial_inventory, "coal": 4000} - game.instance.reset() + game.set_inventory({"coal": 4000}) # Find nearest coal resource - nearest_coal = game.nearest(Resource.Coal) + nearest_coal = game.namespace.nearest(Resource.Coal) # Move to the coal resource - game.move_to(nearest_coal) + game.namespace.move_to(nearest_coal) try: # Harvest coal game.harvest_resource( @@ -34,7 +34,7 @@ def test_harvest_resource_with_full_inventory(game): assert True -def test_harvest_resource(game): +def test_harvest_resource(game: FactorioInstance): """ Find the nearest coal resource patch and harvest 5 coal from it. :param game: @@ -47,22 +47,22 @@ def test_harvest_resource(game): Resource.Stone, Resource.CopperOre, ]: - inventory = game.inspect_inventory() + inventory = game.namespace.inspect_inventory() # Check initial inventory initial_coal = inventory[resource] # Find nearest coal resource - nearest_coal = game.nearest(resource) + nearest_coal = game.namespace.nearest(resource) # Move to the coal resource - game.move_to(nearest_coal) + game.namespace.move_to(nearest_coal) try: # Harvest coal - game.harvest_resource( + game.namespace.harvest_resource( nearest_coal, quantity=quantity ) # Assuming there is a coal resource at (10, 10) except Exception as e: print(e) # Check the inventory after harvesting - final_coal = game.inspect_inventory()[resource] + final_coal = game.namespace.inspect_inventory()[resource] # Assert that the coal has been added to the inventory assert quantity <= final_coal - initial_coal @@ -75,12 +75,10 @@ def test_harvest_resource(game): """ -def test_harvest_stump(game): - instance = game.instance - - with instance.server.transaction() as t: +def test_harvest_stump(game: FactorioInstance): + with game.client.transaction() as t: t.add_command(f"/c {create_stump}", raw=True) - harvested = game.harvest_resource(Position(x=0, y=0), quantity=1) + harvested = game.namespace.harvest_resource(Position(x=0, y=0), quantity=1) assert harvested == 2 @@ -93,56 +91,54 @@ def test_harvest_stump(game): """ -def test_harvest_rock(game): - instance = game.instance - - with instance.server.transaction() as t: +def test_harvest_rock(game: FactorioInstance): + with game.client.transaction() as t: t.add_command(f"/c {create_rock}", raw=True) - harvested = game.harvest_resource(Position(x=0, y=0), quantity=1) + harvested = game.namespace.harvest_resource(Position(x=0, y=0), quantity=1) assert harvested == 20 -def test_harvest_trees(game): +def test_harvest_trees(game: FactorioInstance): """ Find the nearest tree resource patch and harvest 5 wood from it. :param game: :return: """ quantity = 50 - inventory = game.inspect_inventory() + inventory = game.namespace.inspect_inventory() # Check initial inventory initial_wood = inventory[Resource.Wood] # Find nearest wood resource - nearest_wood = game.nearest(Resource.Wood) + nearest_wood = game.namespace.nearest(Resource.Wood) # Move to the wood resource - game.move_to(nearest_wood) + game.namespace.move_to(nearest_wood) # Harvest coal - game.harvest_resource( + game.namespace.harvest_resource( nearest_wood, quantity=quantity, radius=50 ) # Assuming there is a wood resource here # Check the inventory after harvesting - final_wood = game.inspect_inventory()[Resource.Wood] + final_wood = game.namespace.inspect_inventory()[Resource.Wood] # Assert that the coal has been added to the inventory assert quantity < final_wood - initial_wood -def test_harvest_bug(game): +def test_harvest_bug(game: FactorioInstance): # Get stone for furnace - stone_pos = game.nearest(Resource.Stone) - game.move_to(stone_pos) - game.harvest_resource(stone_pos, 5) + stone_pos = game.namespace.nearest(Resource.Stone) + game.namespace.move_to(stone_pos) + game.namespace.harvest_resource(stone_pos, 5) # Craft stone furnace - game.craft_item(Prototype.StoneFurnace, 1) + game.namespace.craft_item(Prototype.StoneFurnace, 1) # Verify we have stone furnace in inventory - inventory = game.inspect_inventory() + inventory = game.namespace.inspect_inventory() assert inventory.get(Prototype.StoneFurnace) >= 1, "Failed to craft stone furnace" -def test_harvest_bug_2(game): +def test_harvest_bug_2(game: FactorioInstance): """ Planning: 1. Gather stone to craft a furnace @@ -158,16 +154,16 @@ def test_harvest_bug_2(game): Step 1: Gather stone to craft a furnace """ # Move to the nearest stone resource - stone_position = game.nearest(Resource.Stone) - game.move_to(stone_position) + stone_position = game.namespace.nearest(Resource.Stone) + game.namespace.move_to(stone_position) # Mine 5 stone (enough for one furnace) stone_needed = 5 - stone_mined = game.harvest_resource(stone_position, stone_needed) + stone_mined = game.namespace.harvest_resource(stone_position, stone_needed) print(f"Mined {stone_mined} stone") # Verify that we have enough stone - inventory = game.inspect_inventory() + inventory = game.namespace.inspect_inventory() assert inventory.get(Prototype.Stone) >= stone_needed, ( f"Failed to mine enough stone. Current inventory: {inventory}" ) @@ -175,11 +171,11 @@ def test_harvest_bug_2(game): """ Step 2: Craft a stone furnace """ - game.craft_item(Prototype.StoneFurnace, 1) + game.namespace.craft_item(Prototype.StoneFurnace, 1) print("Crafted 1 stone furnace") # Verify that we have a stone furnace in our inventory - inventory = game.inspect_inventory() + inventory = game.namespace.inspect_inventory() assert inventory.get(Prototype.StoneFurnace) >= 1, ( f"Failed to craft stone furnace. Current inventory: {inventory}" ) @@ -188,47 +184,47 @@ def test_harvest_bug_2(game): Step 3: Place the furnace """ origin = Position(x=0, y=0) - game.move_to(origin) - furnace = game.place_entity(Prototype.StoneFurnace, position=origin) + game.namespace.move_to(origin) + furnace = game.namespace.place_entity(Prototype.StoneFurnace, position=origin) print(f"Placed stone furnace at {furnace.position}") """ Step 4: Mine coal for fuel """ # Move to the nearest coal resource - coal_position = game.nearest(Resource.Coal) - game.move_to(coal_position) + coal_position = game.namespace.nearest(Resource.Coal) + game.namespace.move_to(coal_position) # Mine 10 coal coal_needed = 10 - coal_mined = game.harvest_resource(coal_position, coal_needed) + coal_mined = game.namespace.harvest_resource(coal_position, coal_needed) print(f"Mined {coal_mined} coal") # Verify that we have enough coal - inventory = game.inspect_inventory() + inventory = game.namespace.inspect_inventory() assert inventory.get(Prototype.Coal) >= coal_needed, ( f"Failed to mine enough coal. Current inventory: {inventory}" ) # Move back to the furnace and insert coal - game.move_to(furnace.position) - updated_furnace = game.insert_item(Prototype.Coal, furnace, 10) + game.namespace.move_to(furnace.position) + updated_furnace = game.namespace.insert_item(Prototype.Coal, furnace, 10) print("Inserted coal into the furnace") """ Step 5: Mine iron ore """ # Move to the nearest iron ore resource - iron_position = game.nearest(Resource.IronOre) - game.move_to(iron_position) + iron_position = game.namespace.nearest(Resource.IronOre) + game.namespace.move_to(iron_position) # Mine 10 iron ore iron_ore_needed = 10 - iron_ore_mined = game.harvest_resource(iron_position, iron_ore_needed) + iron_ore_mined = game.namespace.harvest_resource(iron_position, iron_ore_needed) print(f"Mined {iron_ore_mined} iron ore") # Verify that we have enough iron ore - inventory = game.inspect_inventory() + inventory = game.namespace.inspect_inventory() assert inventory.get(Prototype.IronOre) >= iron_ore_needed, ( f"Failed to mine enough iron ore. Current inventory: {inventory}" ) @@ -237,24 +233,24 @@ def test_harvest_bug_2(game): Step 6: Smelt iron ore into iron plates """ # Move back to the furnace - game.move_to(furnace.position) + game.namespace.move_to(furnace.position) # Insert iron ore into the furnace - updated_furnace = game.insert_item(Prototype.IronOre, updated_furnace, 10) + updated_furnace = game.namespace.insert_item(Prototype.IronOre, updated_furnace, 10) print("Inserted iron ore into the furnace") # Wait for the smelting process to complete smelting_time = 10 * 0.7 # 10 items * 0.7 seconds per item - game.sleep(int(smelting_time)) + game.namespace.sleep(int(smelting_time)) # Extract iron plates max_attempts = 5 for _ in range(max_attempts): - game.extract_item(Prototype.IronPlate, updated_furnace.position, 10) - inventory = game.inspect_inventory() + game.namespace.extract_item(Prototype.IronPlate, updated_furnace.position, 10) + inventory = game.namespace.inspect_inventory() if inventory.get(Prototype.IronPlate, 0) >= 10: break - game.sleep(5) + game.namespace.sleep(5) print("Extracted iron plates from the furnace") @@ -262,7 +258,7 @@ def test_harvest_bug_2(game): Step 7: Verify the production of iron plates """ # Check the inventory for iron plates - inventory = game.inspect_inventory() + inventory = game.namespace.inspect_inventory() iron_plates = inventory.get(Prototype.IronPlate, 0) print(f"Current inventory: {inventory}") assert iron_plates >= 10, ( @@ -272,19 +268,19 @@ def test_harvest_bug_2(game): print("Successfully produced 10 iron plates!") -def test_harvest_provides_score(game): +def test_harvest_provides_score(game: FactorioInstance): # Move to the nearest stone resource - stone_position = game.nearest(Resource.Stone) - game.move_to(stone_position) + stone_position = game.namespace.nearest(Resource.Stone) + game.namespace.move_to(stone_position) - stats = game._production_stats() # noqa - reward, _ = game.score() + stats = game.namespace._production_stats() # noqa + reward, _ = game.namespace.score() # Mine 5 stone (enough for one furnace) stone_needed = 5 - stone_mined = game.harvest_resource(stone_position, stone_needed) + stone_mined = game.namespace.harvest_resource(stone_position, stone_needed) print(f"Mined {stone_mined} stone") - nstats = game._production_stats() # noqa - nreward, _ = game.score() + nstats = game.namespace._production_stats() # noqa + nreward, _ = game.namespace.score() assert nreward > reward diff --git a/tests/actions/test_insert_item.py b/tests/actions/test_insert_item.py index 793776f54..6469e5910 100644 --- a/tests/actions/test_insert_item.py +++ b/tests/actions/test_insert_item.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, EntityStatus, Direction diff --git a/tests/actions/test_inspect_inventory.py b/tests/actions/test_inspect_inventory.py index 00a1d67c2..a1d3a3dbe 100644 --- a/tests/actions/test_inspect_inventory.py +++ b/tests/actions/test_inspect_inventory.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/actions/test_move_to.py b/tests/actions/test_move_to.py index df004b311..e3c4a01cb 100644 --- a/tests/actions/test_move_to.py +++ b/tests/actions/test_move_to.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/actions/test_nearest.py b/tests/actions/test_nearest.py index eff3d8c0a..b92aad7eb 100644 --- a/tests/actions/test_nearest.py +++ b/tests/actions/test_nearest.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/actions/test_nearest_buildable.py b/tests/actions/test_nearest_buildable.py index fc75e15e0..175d0ce14 100644 --- a/tests/actions/test_nearest_buildable.py +++ b/tests/actions/test_nearest_buildable.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.commons.cluster_ips import get_local_container_ips @@ -18,16 +19,9 @@ @pytest.fixture() -def game(): - ips, udp_ports, tcp_ports = get_local_container_ips() - - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=tcp_ports[-1], - cache_scripts=False, - fast=True, - inventory={ +def game(instance: FactorioInstance): + instance.reset() + instance.set_inventory({ "coal": 50, "copper-plate": 50, "iron-plate": 50, @@ -58,7 +52,7 @@ def game(): yield instance.namespace -def test_nearest_buildable_simple(game): +def test_nearest_buildable_simple(game: FactorioInstance): """ Test finding a buildable position for a simple entity like a wooden chest without a bounding box. @@ -75,7 +69,7 @@ def test_nearest_buildable_simple(game): assert can_build is True -def test_nearest_buildable_near_water(game): +def test_nearest_buildable_near_water(game: FactorioInstance): """ Test finding a buildable position for a simple entity like a wooden chest without a bounding box. @@ -100,7 +94,7 @@ def test_nearest_buildable_near_water(game): assert True, "The steam engine should be placeable due to the bounding box" -def test_nearest_buildable_prototype_dimensions(game): +def test_nearest_buildable_prototype_dimensions(game: FactorioInstance): """ Test finding a buildable position for an entity with prototype dimensions. """ @@ -110,7 +104,7 @@ def test_nearest_buildable_prototype_dimensions(game): assert True -def test_nearest_buildable_mining_drill(game): +def test_nearest_buildable_mining_drill(game: FactorioInstance): """ Test finding a buildable position for an electric mining drill with a bounding box over an ore patch. @@ -152,7 +146,7 @@ def test_nearest_buildable_mining_drill(game): ) -def test_nearest_buildable_invalid_position(game): +def test_nearest_buildable_invalid_position(game: FactorioInstance): """ Test that nearest_buildable raises an exception when no valid position is found within search radius. @@ -170,7 +164,7 @@ def test_nearest_buildable_invalid_position(game): assert "Could not find a buildable position" in str(exc_info.value) -def test_nearest_buildable_multiple_entities(game): +def test_nearest_buildable_multiple_entities(game: FactorioInstance): """ Test finding buildable positions for multiple entities of the same type ensuring they don't overlap. @@ -208,7 +202,7 @@ def test_nearest_buildable_multiple_entities(game): assert can_build is True -def test_nearest_buildable_relative_to_player(game): +def test_nearest_buildable_relative_to_player(game: FactorioInstance): """ Test that nearest_buildable finds positions relative to player location. """ @@ -229,7 +223,7 @@ def test_nearest_buildable_relative_to_player(game): assert distance <= 50 # Within max search radius -def test_nearest_buildable_with_obstacles(game): +def test_nearest_buildable_with_obstacles(game: FactorioInstance): """ Test finding buildable position when there are obstacles in the way. """ @@ -254,7 +248,7 @@ def test_nearest_buildable_with_obstacles(game): assert position is not obstacle_pos -def test_drill_groups(game): +def test_drill_groups(game: FactorioInstance): # Find iron ore patch iron_ore_pos = game.nearest(Resource.IronOre) print(f"Found iron ore patch at {iron_ore_pos}") @@ -285,7 +279,7 @@ def test_drill_groups(game): assert len(entities) == 3 -def test_nearest_buildable_pumpjack(game): +def test_nearest_buildable_pumpjack(game: FactorioInstance): # Find crude oil patch crude_oil_pos = game.nearest(Resource.CrudeOil) print(f"Found crude oil patch at {crude_oil_pos}") diff --git a/tests/actions/test_pickup_entity.py b/tests/actions/test_pickup_entity.py index 6eeb6d053..c49b9c0b5 100644 --- a/tests/actions/test_pickup_entity.py +++ b/tests/actions/test_pickup_entity.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/actions/test_place.py b/tests/actions/test_place.py index f4123340a..e54286cd1 100644 --- a/tests/actions/test_place.py +++ b/tests/actions/test_place.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Direction diff --git a/tests/actions/test_place_entity_next_to.py b/tests/actions/test_place_entity_next_to.py index 843a4458c..eb60c45bb 100644 --- a/tests/actions/test_place_entity_next_to.py +++ b/tests/actions/test_place_entity_next_to.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Direction diff --git a/tests/actions/test_place_next_to_and_rotate.py b/tests/actions/test_place_next_to_and_rotate.py index ad4c4b67a..5102f25bc 100644 --- a/tests/actions/test_place_next_to_and_rotate.py +++ b/tests/actions/test_place_next_to_and_rotate.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Direction diff --git a/tests/actions/test_print.py b/tests/actions/test_print.py index e0f204cd6..d829045a4 100644 --- a/tests/actions/test_print.py +++ b/tests/actions/test_print.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest diff --git a/tests/actions/test_render.py b/tests/actions/test_render.py index b397aadfa..8b62395ab 100644 --- a/tests/actions/test_render.py +++ b/tests/actions/test_render.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Layer diff --git a/tests/actions/test_request_path.py b/tests/actions/test_request_path.py index b1c40485a..f1ea3a0ba 100644 --- a/tests/actions/test_request_path.py +++ b/tests/actions/test_request_path.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/actions/test_rotate.py b/tests/actions/test_rotate.py index 536a1c4db..59bada31c 100644 --- a/tests/actions/test_rotate.py +++ b/tests/actions/test_rotate.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.game_types import Prototype, RecipeName diff --git a/tests/actions/test_score.py b/tests/actions/test_score.py index 2fc1d1d08..6afad4eb9 100644 --- a/tests/actions/test_score.py +++ b/tests/actions/test_score.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest diff --git a/tests/actions/test_set_entity_recipe.py b/tests/actions/test_set_entity_recipe.py index 618380242..c96fb9f03 100644 --- a/tests/actions/test_set_entity_recipe.py +++ b/tests/actions/test_set_entity_recipe.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/actions/test_set_research.py b/tests/actions/test_set_research.py index f03b5748f..61408d7d4 100644 --- a/tests/actions/test_set_research.py +++ b/tests/actions/test_set_research.py @@ -2,25 +2,12 @@ from fle.env.game import FactorioInstance from fle.env.game.game_types import Technology -from fle.commons.cluster_ips import get_local_container_ips - @pytest.fixture() -def game(instance: FactorioInstance): - # game.initial_inventory = {'assembling-machine-1': 1} - # from gym import FactorioInstance - ips, udp_ports, tcp_ports = get_local_container_ips() - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=tcp_ports[-1], # 27019, - all_technologies_researched=False, - fast=True, - inventory={}, - ) - instance.reset() - yield instance.namespace - instance.reset() +def game(unresearched_instance: FactorioInstance): + unresearched_instance.reset() + yield unresearched_instance.namespace + unresearched_instance.reset() def test_set_research(game): diff --git a/tests/actions/test_sleep.py b/tests/actions/test_sleep.py index fb20cf350..ba08ab994 100644 --- a/tests/actions/test_sleep.py +++ b/tests/actions/test_sleep.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import time import pytest diff --git a/tests/benchmarks/test_demo_video.py b/tests/benchmarks/test_demo_video.py index 1cc2b76bc..a34c52112 100644 --- a/tests/benchmarks/test_demo_video.py +++ b/tests/benchmarks/test_demo_video.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance # import json # import time diff --git a/tests/benchmarks/test_elapsed_ticks.py b/tests/benchmarks/test_elapsed_ticks.py index 7eb8884b0..5e2579d7d 100644 --- a/tests/benchmarks/test_elapsed_ticks.py +++ b/tests/benchmarks/test_elapsed_ticks.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/benchmarks/test_script_caching.py b/tests/benchmarks/test_script_caching.py index ccf028035..304e23b76 100644 --- a/tests/benchmarks/test_script_caching.py +++ b/tests/benchmarks/test_script_caching.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import time from statistics import fmean import cProfile diff --git a/tests/blueprints/test_blueprint_based_policies.py b/tests/blueprints/test_blueprint_based_policies.py index 3f62d99e4..afb5d6145 100644 --- a/tests/blueprints/test_blueprint_based_policies.py +++ b/tests/blueprints/test_blueprint_based_policies.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, BoundingBox # , Direction diff --git a/tests/blueprints/test_save_load.py b/tests/blueprints/test_save_load.py index d26472656..eeaa9a315 100644 --- a/tests/blueprints/test_save_load.py +++ b/tests/blueprints/test_save_load.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import base64 import json import time diff --git a/tests/complex/test_edge_cases.py b/tests/complex/test_edge_cases.py index a73ec4e0b..d767e3f20 100644 --- a/tests/complex/test_edge_cases.py +++ b/tests/complex/test_edge_cases.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from time import sleep from fle.env.game.entities import Position, ResourcePatch, Direction diff --git a/tests/complex/test_slow.py b/tests/complex/test_slow.py index 2b12d8193..970160fe9 100644 --- a/tests/complex/test_slow.py +++ b/tests/complex/test_slow.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.instance import FactorioInstance from fle.env.game.game_types import Resource diff --git a/tests/connect/test_connect_pipes.py b/tests/connect/test_connect_pipes.py index e853bc699..f4bbf0e1c 100644 --- a/tests/connect/test_connect_pipes.py +++ b/tests/connect/test_connect_pipes.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import math import pytest diff --git a/tests/connect/test_connect_poles.py b/tests/connect/test_connect_poles.py index 4b5a08fae..426c2f2ba 100644 --- a/tests/connect/test_connect_poles.py +++ b/tests/connect/test_connect_poles.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, EntityStatus, BuildingBox, Direction diff --git a/tests/connect/test_connect_transport_belts.py b/tests/connect/test_connect_transport_belts.py index ea88292e5..f5aca5072 100644 --- a/tests/connect/test_connect_transport_belts.py +++ b/tests/connect/test_connect_transport_belts.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance from time import sleep import pytest diff --git a/tests/connect/test_connect_underground_belts.py b/tests/connect/test_connect_underground_belts.py index 78381e9ec..fd469781a 100644 --- a/tests/connect/test_connect_underground_belts.py +++ b/tests/connect/test_connect_underground_belts.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, UndergroundBelt diff --git a/tests/connect/test_connect_underground_pipes.py b/tests/connect/test_connect_underground_pipes.py index 04d62ff51..6364e3e95 100644 --- a/tests/connect/test_connect_underground_pipes.py +++ b/tests/connect/test_connect_underground_pipes.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, UndergroundBelt diff --git a/tests/connect/test_connect_walls.py b/tests/connect/test_connect_walls.py index cfb2d476c..493c92951 100644 --- a/tests/connect/test_connect_walls.py +++ b/tests/connect/test_connect_walls.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position diff --git a/tests/connect/test_fluid_handlers.py b/tests/connect/test_fluid_handlers.py index 99d1f7010..4a936a266 100644 --- a/tests/connect/test_fluid_handlers.py +++ b/tests/connect/test_fluid_handlers.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, BuildingBox diff --git a/tests/entities/test_assemblers.py b/tests/entities/test_assemblers.py index 48502f405..426341f1b 100644 --- a/tests/entities/test_assemblers.py +++ b/tests/entities/test_assemblers.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import Direction, Position diff --git a/tests/entities/test_drill.py b/tests/entities/test_drill.py index 2f2c94c98..20270b981 100644 --- a/tests/entities/test_drill.py +++ b/tests/entities/test_drill.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import Direction from fle.env.game.game_types import Prototype, Resource diff --git a/tests/entities/test_fluid_processors.py b/tests/entities/test_fluid_processors.py index b2166256c..fd638d7a6 100644 --- a/tests/entities/test_fluid_processors.py +++ b/tests/entities/test_fluid_processors.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Direction, Entity diff --git a/tests/entities/test_inserters.py b/tests/entities/test_inserters.py index 2cfdd590a..962ef0e1b 100644 --- a/tests/entities/test_inserters.py +++ b/tests/entities/test_inserters.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import Direction, Position from fle.env.game.game_types import Prototype diff --git a/tests/entities/test_nuclear_reactor.py b/tests/entities/test_nuclear_reactor.py index d1b2b10d7..7c801c4a9 100644 --- a/tests/entities/test_nuclear_reactor.py +++ b/tests/entities/test_nuclear_reactor.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import Direction, EntityStatus, Position diff --git a/tests/entities/test_power_generators.py b/tests/entities/test_power_generators.py index d625df0cb..0f31b1a46 100644 --- a/tests/entities/test_power_generators.py +++ b/tests/entities/test_power_generators.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import Direction, EntityStatus diff --git a/tests/entities/test_rockets.py b/tests/entities/test_rockets.py index 66c22b46b..9b081ac2a 100644 --- a/tests/entities/test_rockets.py +++ b/tests/entities/test_rockets.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import json from fle.env.game.entities import Position, Direction, EntityStatus from fle.env.game.game_types import Resource, Prototype diff --git a/tests/eval/samplers/test_kld_mean_sampler.py b/tests/eval/samplers/test_kld_mean_sampler.py index 6cdc05508..59a64e3b6 100644 --- a/tests/eval/samplers/test_kld_mean_sampler.py +++ b/tests/eval/samplers/test_kld_mean_sampler.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import unittest from unittest.mock import Mock, patch from collections import Counter diff --git a/tests/eval/samplers/test_python_parser.py b/tests/eval/samplers/test_python_parser.py index 3db1ba583..67865ba5b 100644 --- a/tests/eval/samplers/test_python_parser.py +++ b/tests/eval/samplers/test_python_parser.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import unittest from fle.agents.llm.parsing import PythonParser diff --git a/tests/eval/samplers/test_weighted_reward_sampler.py b/tests/eval/samplers/test_weighted_reward_sampler.py index 95889bf45..604ac352b 100644 --- a/tests/eval/samplers/test_weighted_reward_sampler.py +++ b/tests/eval/samplers/test_weighted_reward_sampler.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import unittest from unittest.mock import Mock diff --git a/tests/eval/test_achievements.py b/tests/eval/test_achievements.py index ecf784570..faf5c4bd4 100644 --- a/tests/eval/test_achievements.py +++ b/tests/eval/test_achievements.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import unittest from fle.env.game import FactorioInstance diff --git a/tests/eval/test_conversation_formatter.py b/tests/eval/test_conversation_formatter.py index 7532e78e9..caff9e1fa 100644 --- a/tests/eval/test_conversation_formatter.py +++ b/tests/eval/test_conversation_formatter.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import unittest from fle.commons.models.conversation import Conversation from fle.commons.models.message import Message diff --git a/tests/eval/test_game_state.py b/tests/eval/test_game_state.py index b2100e93b..80cffbd79 100644 --- a/tests/eval/test_game_state.py +++ b/tests/eval/test_game_state.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance from pydantic import BaseModel from fle.env.game import FactorioInstance from fle.env.game.game_state import GameState diff --git a/tests/eval/test_mcts_chunker.py b/tests/eval/test_mcts_chunker.py index 7cdcf5f3f..f5482d990 100644 --- a/tests/eval/test_mcts_chunker.py +++ b/tests/eval/test_mcts_chunker.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import unittest from dataclasses import dataclass from typing import Optional diff --git a/tests/eval/test_production_divergence.py b/tests/eval/test_production_divergence.py index 9b162c8f1..323d4b0dd 100644 --- a/tests/eval/test_production_divergence.py +++ b/tests/eval/test_production_divergence.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import unittest from fle.env.game import FactorioInstance diff --git a/tests/eval/test_profits.py b/tests/eval/test_profits.py index a6c8e1ba0..2ce5edd59 100644 --- a/tests/eval/test_profits.py +++ b/tests/eval/test_profits.py @@ -1,32 +1,32 @@ +import pytest import math from fle.env.game import FactorioInstance from fle.env.utils.profits import eval_program_with_profits -def test_profits(): - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory={}, - ) +@pytest.fixture() +def game(instance: FactorioInstance): + instance.reset() + yield instance + instance.reset() + + +def test_profits(instance: FactorioInstance): instance.set_speed(10) profit_config = {"max_static_unit_profit_cap": 5, "dynamic_profit_multiplier": 10} test_string_1 = "pos = nearest(Resource.Stone)\nmove_to(pos)\nharvest_resource(pos, 10)\ncraft_item(Prototype.StoneFurnace, 2)\npos = nearest(Resource.Coal)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = nearest(Resource.IronOre)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = Position(x = 0, y = 0)\nmove_to(pos)\nfurnace = place_entity(Prototype.StoneFurnace, position = pos)\ninsert_item(Prototype.IronOre, furnace, 5)\ninsert_item(Prototype.Coal, furnace, 5)\nsleep(25)\nextract_item(Prototype.IronPlate, furnace.position, 10)" - _, _, _, profits = eval_program_with_profits(instance, test_string_1, profit_config) + _, _, _, profits = eval_program_with_profits(instance.namespace, test_string_1, profit_config) assert math.isclose(profits["static"], 2.8, rel_tol=1) assert math.isclose(profits["dynamic"], 96, rel_tol=1) assert math.isclose(profits["total"], 98.8, rel_tol=1) test_string = "pos = nearest(Resource.Stone)\nmove_to(pos)\nharvest_resource(pos, 10)\ncraft_item(Prototype.StoneFurnace, 1)\npos = nearest(Resource.Coal)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = nearest(Resource.CopperOre)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = Position(x = 0, y = 0)\nmove_to(pos)\nfurnace = place_entity(Prototype.StoneFurnace, position = pos)\ninsert_item(Prototype.CopperOre, furnace, 5)\ninsert_item(Prototype.Coal, furnace, 5)\nsleep(25)" - _, _, _, profits = eval_program_with_profits(instance, test_string, profit_config) + _, _, _, profits = eval_program_with_profits(instance.namespace, test_string, profit_config) assert math.isclose(profits["static"], 1.4, rel_tol=1) assert math.isclose(profits["dynamic"], 136, rel_tol=1) assert math.isclose(profits["total"], 137.4, rel_tol=1) test_string = "pos = nearest(Resource.Stone)\nmove_to(pos)\nharvest_resource(pos, 10)\ncraft_item(Prototype.StoneFurnace, 1)\npos = nearest(Resource.Coal)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = nearest(Resource.CopperOre)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = Position(x = 0, y = 0)\nmove_to(pos)\nfurnace = place_entity(Prototype.StoneFurnace, position = pos)\ninsert_item(Prototype.CopperOre, furnace, 5)\ninsert_item(Prototype.Coal, furnace, 5)\nsleep(25)" - _, _, _, profits = eval_program_with_profits(instance, test_string, profit_config) + _, _, _, profits = eval_program_with_profits(instance.namespace, test_string, profit_config) assert math.isclose(profits["static"], 1.4, rel_tol=1) assert math.isclose(profits["dynamic"], 106, rel_tol=1) assert math.isclose(profits["total"], 107.4, rel_tol=1) diff --git a/tests/eval/test_python_parser.py b/tests/eval/test_python_parser.py index 032338135..162fb1dac 100644 --- a/tests/eval/test_python_parser.py +++ b/tests/eval/test_python_parser.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from typing import NamedTuple from fle.agents.llm.parsing import PythonParser diff --git a/tests/eval/test_recursive_formatter.py b/tests/eval/test_recursive_formatter.py index 47d4a5078..b43ffb729 100644 --- a/tests/eval/test_recursive_formatter.py +++ b/tests/eval/test_recursive_formatter.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import unittest import tempfile import shutil diff --git a/tests/eval/test_save_load_python_namespace.py b/tests/eval/test_save_load_python_namespace.py index d23d75496..9776519d4 100644 --- a/tests/eval/test_save_load_python_namespace.py +++ b/tests/eval/test_save_load_python_namespace.py @@ -1,5 +1,4 @@ import pickle -import unittest from fle.eval.algorithms.mcts.chunked_mcts import ChunkedMCTS from fle.env.game.game_state import GameState @@ -103,89 +102,51 @@ """ -class TestSaveLoadPythonNamespace(unittest.TestCase): - """ - FactorioInstance exposes a Python namespace for the agent to persist variables. - These tests verify that the namespace can be saved and loaded correctly into new instances. - """ - - def setUp(self): - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={ - "boiler": 1, - "burner-mining-drill": 1, - "coal": 50, - "transport-belt": 10, - }, - ) - - def test_save_load_simple_variable_namespace(self): - self.instance.eval("x=2") - game_state = GameState.from_instance(self.instance) - - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={}, - ) - - self.instance.reset(game_state) - self.instance.eval("assert x == 2") - - def test_load_in_all_variable_values(self): - game_state_raw = { - "entities": "eJyrrgUAAXUA+Q==", - "inventory": {"coal": 50, "iron-ore": 50}, - "namespace": "800495d6090000000000007d94288c046576616c948c086275696c74696e73948c046576616c9493948c08456c6c6970736973948c086275696c74696e73948c08456c6c69707369739493948c0546616c736594898c044e6f6e65944e8c0e4e6f74496d706c656d656e7465649468068c0e4e6f74496d706c656d656e7465649493948c045472756594888c036162739468028c036162739493948c03616c6c9468028c03616c6c9493948c03616e799468028c03616e799493948c0561736369699468028c0561736369699493948c0362696e9468028c0362696e9493948c0a627265616b706f696e749468028c0a627265616b706f696e749493948c0863616c6c61626c659468028c0863616c6c61626c659493948c036368729468028c036368729493948c07636f6d70696c659468028c07636f6d70696c659493948c09636f70797269676874948c0d5f736974656275696c74696e73948c085f5072696e7465729493942981947d94288c0e5f5072696e7465725f5f6e616d6594682a8c0e5f5072696e7465725f5f64617461945833010000436f707972696768742028632920323030312d3230323120507974686f6e20536f66747761726520466f756e646174696f6e2e0a416c6c205269676874732052657365727665642e0a0a436f707972696768742028632920323030302042654f70656e2e636f6d2e0a416c6c205269676874732052657365727665642e0a0a436f707972696768742028632920313939352d3230303120436f72706f726174696f6e20666f72204e6174696f6e616c20526573656172636820496e6974696174697665732e0a416c6c205269676874732052657365727665642e0a0a436f707972696768742028632920313939312d3139393520537469636874696e67204d617468656d6174697363682043656e7472756d2c20416d7374657264616d2e0a416c6c205269676874732052657365727665642e948c0f5f5072696e7465725f5f6c696e6573944e8c135f5072696e7465725f5f66696c656e616d6573945d9475628c076372656469747394682d2981947d94286830683668318c9e202020205468616e6b7320746f204357492c20434e52492c2042654f70656e2e636f6d2c205a6f706520436f72706f726174696f6e20616e6420612063617374206f662074686f7573616e64730a20202020666f7220737570706f7274696e6720507974686f6e20646576656c6f706d656e742e2020536565207777772e707974686f6e2e6f726720666f72206d6f726520696e666f726d6174696f6e2e9468334e68345d9475628c0764656c617474729468028c0764656c617474729493948c036469729468028c036469729493948c066469766d6f649468028c066469766d6f649493948c04657865639468028c04657865639493948c046578697494682b8c07517569747465729493942981947d94288c046e616d659468478c03656f66948c114374726c2d442028692e652e20454f46299475628c06666f726d61749468028c06666f726d61749493948c07676574617474729468028c07676574617474729493948c07676c6f62616c739468028c07676c6f62616c739493948c07686173617474729468028c07686173617474729493948c04686173689468028c04686173689493948c0468656c7094682b8c075f48656c7065729493942981948c036865789468028c036865789493948c0269649468028c0269649493948c05696e7075749468028c05696e7075749493948c0a6973696e7374616e63659468028c0a6973696e7374616e63659493948c0a6973737562636c6173739468028c0a6973737562636c6173739493948c04697465729468028c04697465729493948c036c656e9468028c036c656e9493948c076c6963656e736594682d2981947d94286830687768318c275365652068747470733a2f2f7777772e707974686f6e2e6f72672f7073662f6c6963656e73652f9468334e68345d94288c722f4c6962726172792f446576656c6f7065722f436f6d6d616e644c696e65546f6f6c732f4c6962726172792f4672616d65776f726b732f507974686f6e332e6672616d65776f726b2f56657273696f6e732f332e392f6c69622f707974686f6e332e392f2e2e2f4c4943454e53452e747874948c6e2f4c6962726172792f446576656c6f7065722f436f6d6d616e644c696e65546f6f6c732f4c6962726172792f4672616d65776f726b732f507974686f6e332e6672616d65776f726b2f56657273696f6e732f332e392f6c69622f707974686f6e332e392f2e2e2f4c4943454e5345948c6f2f4c6962726172792f446576656c6f7065722f436f6d6d616e644c696e65546f6f6c732f4c6962726172792f4672616d65776f726b732f507974686f6e332e6672616d65776f726b2f56657273696f6e732f332e392f6c69622f707974686f6e332e392f4c4943454e53452e747874948c6b2f4c6962726172792f446576656c6f7065722f436f6d6d616e644c696e65546f6f6c732f4c6962726172792f4672616d65776f726b732f507974686f6e332e6672616d65776f726b2f56657273696f6e732f332e392f6c69622f707974686f6e332e392f4c4943454e5345948c0d2e2f4c4943454e53452e747874948c092e2f4c4943454e5345946575628c066c6f63616c739468028c066c6f63616c739493948c036d61789468028c036d61789493948c036d696e9468028c036d696e9493948c046e6578749468028c046e6578749493948c036f63749468028c036f63749493948c046f70656e948c02696f948c046f70656e9493948c036f72649468028c036f72649493948c03706f779468028c03706f779493948c057072696e749468028c057072696e749493948c04717569749468492981947d9428684c689e684d684e75628c04726570729468028c04726570729493948c05726f756e649468028c05726f756e649493948c07736574617474729468028c07736574617474729493948c06736f727465649468028c06736f727465649493948c0373756d9468028c0373756d9493948c04766172739468028c04766172739493948c0d636f616c5f706f736974696f6e948c11666163746f72696f5f656e746974696573948c08506f736974696f6e9493942981947d94288c085f5f646963745f5f947d94288c0178944740338000000000008c01799447c027000000000000758c125f5f707964616e7469635f65787472615f5f944e8c175f5f707964616e7469635f6669656c64735f7365745f5f948f942868bc68bb908c145f5f707964616e7469635f707269766174655f5f944e75628c0e636f616c5f686172766573746564944b328c0e69726f6e5f686172766573746564944b328c0d69726f6e5f706f736974696f6e9468b62981947d942868b97d942868bb47c02700000000000068bc4740338000000000007568bd4e68be8f942868bc68bb9068c04e7562752e", - "timestamp": 1735481767.4378161, - } - game_state = GameState.parse(game_state_raw) - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={}, - ) - self.instance.reset(game_state) - ngame_state = GameState.from_instance(self.instance) - nvars = pickle.loads(ngame_state.namespaces[0]) - - assert "coal_position" in nvars and nvars["coal_position"] - - def test_save_load_items_on_ground(self): - MINER = """ +def test_save_load_simple_variable_namespace(instance: FactorioInstance): + instance.namespace.eval("x=2") + game_state = GameState.from_instance(instance) + + instance.reset() + instance.reset(game_state) + instance.namespace.eval("assert x == 2") + + +def test_load_in_all_variable_values(instance: FactorioInstance): + game_state_raw = { + "entities": "eJyrrgUAAXUA+Q==", + "inventory": {"coal": 50, "iron-ore": 50}, + "namespace": "800495d6090000000000007d94288c046576616c948c086275696c74696e73948c046576616c9493948c08456c6c6970736973948c086275696c74696e73948c08456c6c69707369739493948c0546616c736594898c044e6f6e65944e8c0e4e6f74496d706c656d656e7465649468068c0e4e6f74496d706c656d656e7465649493948c045472756594888c036162739468028c036162739493948c03616c6c9468028c03616c6c9493948c03616e799468028c03616e799493948c0561736369699468028c0561736369699493948c0362696e9468028c0362696e9493948c0a627265616b706f696e749468028c0a627265616b706f696e749493948c0863616c6c61626c659468028c0863616c6c61626c659493948c036368729468028c036368729493948c07636f6d70696c659468028c07636f6d70696c659493948c09636f70797269676874948c0d5f736974656275696c74696e73948c085f5072696e7465729493942981947d94288c0e5f5072696e7465725f5f6e616d6594682a8c0e5f5072696e7465725f5f64617461945833010000436f707972696768742028632920323030312d3230323120507974686f6e20536f66747761726520466f756e646174696f6e2e0a416c6c205269676874732052657365727665642e0a0a436f707972696768742028632920323030302042654f70656e2e636f6d2e0a416c6c205269676874732052657365727665642e0a0a436f707972696768742028632920313939352d3230303120436f72706f726174696f6e20666f72204e6174696f6e616c20526573656172636820496e6974696174697665732e0a416c6c205269676874732052657365727665642e0a0a436f707972696768742028632920313939312d3139393520537469636874696e67204d617468656d6174697363682043656e7472756d2c20416d7374657264616d2e0a416c6c205269676874732052657365727665642e948c0f5f5072696e7465725f5f6c696e6573944e8c135f5072696e7465725f5f66696c656e616d6573945d9475628c076372656469747394682d2981947d94286830683668318c9e202020205468616e6b7320746f204357492c20434e52492c2042654f70656e2e636f6d2c205a6f706520436f72706f726174696f6e20616e6420612063617374206f662074686f7573616e64730a20202020666f7220737570706f7274696e6720507974686f6e20646576656c6f706d656e742e2020536565207777772e707974686f6e2e6f726720666f72206d6f726520696e666f726d6174696f6e2e9468334e68345d9475628c0764656c617474729468028c0764656c617474729493948c036469729468028c036469729493948c066469766d6f649468028c066469766d6f649493948c04657865639468028c04657865639493948c046578697494682b8c07517569747465729493942981947d94288c046e616d659468478c03656f66948c114374726c2d442028692e652e20454f46299475628c06666f726d61749468028c06666f726d61749493948c07676574617474729468028c07676574617474729493948c07676c6f62616c739468028c07676c6f62616c739493948c07686173617474729468028c07686173617474729493948c04686173689468028c04686173689493948c0468656c7094682b8c075f48656c7065729493942981948c036865789468028c036865789493948c0269649468028c0269649493948c05696e7075749468028c05696e7075749493948c0a6973696e7374616e63659468028c0a6973696e7374616e63659493948c0a6973737562636c6173739468028c0a6973737562636c6173739493948c04697465729468028c04697465729493948c036c656e9468028c036c656e9493948c076c6963656e736594682d2981947d94286830687768318c275365652068747470733a2f2f7777772e707974686f6e2e6f72672f7073662f6c6963656e73652f9468334e68345d94288c722f4c6962726172792f446576656c6f7065722f436f6d6d616e644c696e65546f6f6c732f4c6962726172792f4672616d65776f726b732f507974686f6e332e6672616d65776f726b2f56657273696f6e732f332e392f6c69622f707974686f6e332e392f2e2e2f4c4943454e53452e747874948c6e2f4c6962726172792f446576656c6f7065722f436f6d6d616e644c696e65546f6f6c732f4c6962726172792f4672616d65776f726b732f507974686f6e332e6672616d65776f726b2f56657273696f6e732f332e392f6c69622f707974686f6e332e392f2e2e2f4c4943454e5345948c6f2f4c6962726172792f446576656c6f7065722f436f6d6d616e644c696e65546f6f6c732f4c6962726172792f4672616d65776f726b732f507974686f6e332e6672616d65776f726b2f56657273696f6e732f332e392f6c69622f707974686f6e332e392f4c4943454e53452e747874948c6b2f4c6962726172792f446576656c6f7065722f436f6d6d616e644c696e65546f6f6c732f4c6962726172792f4672616d65776f726b732f507974686f6e332e6672616d65776f726b2f56657273696f6e732f332e392f6c69622f707974686f6e332e392f4c4943454e5345948c0d2e2f4c4943454e53452e747874948c092e2f4c4943454e5345946575628c066c6f63616c739468028c066c6f63616c739493948c036d61789468028c036d61789493948c036d696e9468028c036d696e9493948c046e6578749468028c046e6578749493948c036f63749468028c036f63749493948c046f70656e948c02696f948c046f70656e9493948c036f72649468028c036f72649493948c03706f779468028c03706f779493948c057072696e749468028c057072696e749493948c04717569749468492981947d9428684c689e684d684e75628c04726570729468028c04726570729493948c05726f756e649468028c05726f756e649493948c07736574617474729468028c07736574617474729493948c06736f727465649468028c06736f727465649493948c0373756d9468028c0373756d9493948c04766172739468028c04766172739493948c0d636f616c5f706f736974696f6e948c11666163746f72696f5f656e746974696573948c08506f736974696f6e9493942981947d94288c085f5f646963745f5f947d94288c0178944740338000000000008c01799447c027000000000000758c125f5f707964616e7469635f65787472615f5f944e8c175f5f707964616e7469635f6669656c64735f7365745f5f948f942868bc68bb908c145f5f707964616e7469635f707269766174655f5f944e75628c0e636f616c5f686172766573746564944b328c0e69726f6e5f686172766573746564944b328c0d69726f6e5f706f736974696f6e9468b62981947d942868b97d942868bb47c02700000000000068bc4740338000000000007568bd4e68be8f942868bc68bb9068c04e7562752e", + "timestamp": 1735481767.4378161, + } + game_state = GameState.parse(game_state_raw) + + instance.reset(game_state) + ngame_state = GameState.from_instance(instance) + nvars = pickle.loads(ngame_state.namespaces[0]) + + assert "coal_position" in nvars and nvars["coal_position"] + + +def test_save_load_items_on_ground(instance: FactorioInstance): + MINER = """ move_to(nearest(Resource.IronOre)) miner = place_entity(Prototype.BurnerMiningDrill, Direction.UP, nearest(Resource.IronOre)) insert_item(Prototype.Coal, miner) sleep(15) pickup_entity(miner) """ - self.instance.eval(MINER) - game_state = GameState.from_instance(self.instance) - - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={}, - ) - - self.instance.reset(game_state) - response = self.instance.eval( - "pickup_entity(Prototype.IronOre, miner.drop_position)" - ) - assert "Error" not in response - - def test_save_load_items_on_belt(self): - MINER = """ + instance.namespace.eval(MINER) + game_state = GameState.from_instance(instance) + + instance.reset(game_state) + response = instance.namespace.eval( + "pickup_entity(Prototype.IronOre, miner.drop_position)" + ) + assert "Error" not in response + + +def test_save_load_items_on_belt(instance: FactorioInstance): + MINER = """ move_to(nearest(Resource.IronOre)) miner = place_entity(Prototype.BurnerMiningDrill, Direction.UP, nearest(Resource.IronOre)) insert_item(Prototype.Coal, miner) @@ -193,171 +154,115 @@ def test_save_load_items_on_belt(self): sleep(15) pickup_entity(miner) """ - response = self.instance.eval(MINER) - game_state = GameState.from_instance(self.instance) - - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={}, - ) - - self.instance.reset(game_state) - response = self.instance.eval( - "pickup_entity(Prototype.TransportBelt, belt.position)" - ) - assert "Error" not in response - - assert self.instance.namespace.inspect_inventory()[Prototype.IronOre] == 4 - - def test_save_load_simple_variable_namespace_with_exception(self): - self.instance.eval( - "boiler = place_entity(Prototype.Boiler, Direction.UP, Position(x=0, y=0))" - ) - game_state = GameState.from_instance(self.instance) - - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={}, - ) - - self.instance.reset(game_state) - _, _, response = self.instance.eval("print(boiler.position)") - - assert "Error" not in response - pass - - def test_save_load_simple_variable_namespace2(self): - self.instance.eval( - "boiler = place_entity(Prototype.Boiler, Direction.UP, Position(x=0, y=0))" - ) - game_state = GameState.from_instance(self.instance) - - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={}, - ) - self.instance.reset() - self.instance.reset(game_state) - self.instance.eval("assert boiler") - resp2 = self.instance.eval("print(boiler)") - assert "error" not in resp2 - - def test_declare_load_function_definition(self): - self.instance.eval('def myfunc():\n return "hello world"') - - _, _, resp2 = self.instance.eval("print(myfunc())") - assert "hello world" in resp2 - - game_state = GameState.from_instance(self.instance) - - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={}, - ) - self.instance.reset() - self.instance.reset(game_state) - _, _, resp3 = self.instance.eval("print(myfunc())") - assert "hello world" in resp3 - - def test_full_program(self): - splitter = ChunkedMCTS(None, None, None, "", None, initial_state=None) - parts = splitter._split_into_chunks(FULL_PROGRAM) - - for part in parts: - resp = self.instance.eval(part.code) - - game_state = GameState.from_instance(self.instance) - - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={}, - ) - self.instance.reset() - self.instance.reset(game_state) - - print(resp) - - def test_drill_inventory(self): - resp = self.instance.eval(FULL_PROGRAM_2) - - game_state = GameState.from_instance(self.instance) - - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory={"coal": 50}, - ) - self.instance.reset() - self.instance.reset(game_state) + response = instance.namespace.eval(MINER) + game_state = GameState.from_instance(instance) + + instance.reset(game_state) + response = instance.namespace.eval( + "pickup_entity(Prototype.TransportBelt, belt.position)" + ) + assert "Error" not in response + + assert instance.namespace.inspect_inventory()[Prototype.IronOre] == 4 + + +def test_save_load_simple_variable_namespace_with_exception(instance: FactorioInstance): + instance.namespace.eval( + "boiler = place_entity(Prototype.Boiler, Direction.UP, Position(x=0, y=0))" + ) + game_state = GameState.from_instance(instance) + + instance.reset(game_state) + _, _, response = instance.namespace.eval("print(boiler.position)") + + assert "Error" not in response + + +def test_save_load_simple_variable_namespace2(instance: FactorioInstance): + instance.namespace.eval( + "boiler = place_entity(Prototype.Boiler, Direction.UP, Position(x=0, y=0))" + ) + game_state = GameState.from_instance(instance) + + instance.reset() + instance.reset(game_state) + instance.namespace.eval("assert boiler") + resp2 = instance.namespace.eval("print(boiler)") + assert "error" not in resp2 + + +def test_declare_load_function_definition(instance: FactorioInstance): + instance.namespace.eval('def myfunc():\n return "hello world"') + + _, _, resp2 = instance.namespace.eval("print(myfunc())") + assert "hello world" in resp2 + + game_state = GameState.from_instance(instance) + + instance.reset() + instance.reset(game_state) + _, _, resp3 = instance.namespace.eval("print(myfunc())") + assert "hello world" in resp3 + + +def test_full_program(instance: FactorioInstance): + splitter = ChunkedMCTS(None, None, None, "", None, initial_state=None) + parts = splitter._split_into_chunks(FULL_PROGRAM) + + for part in parts: + resp = instance.namespace.eval(part.code) + + game_state = GameState.from_instance(instance) + + instance.reset() + instance.reset(game_state) print(resp) - def test_save_load_research_new_instance(self): - # Save game state including research - game_state = GameState.from_instance(self.instance) - - self.instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - all_technologies_researched=False, - inventory={}, - ) - self.instance.reset() - - # Verify that there are no technologies here - n_game_state = GameState.from_instance(self.instance) - - game_state_techs = ( - game_state.research.technologies.values() - ) # Has everything researched - n_game_state_techs = ( - n_game_state.research.technologies.values() - ) # Has nothing researched - - for i, j in zip(game_state_techs, n_game_state_techs): - assert i.name == j.name - - # Late game techs may be infinite and therefore are always not yet researched - skip = False - for k in i.prerequisites: - if k == "space-science-pack": - skip = True - - if not skip: - assert i.researched, f"Technology {i.name} should be researched" - assert not j.researched, f"Technology {j.name} should not be researched" - - self.instance.reset(game_state) - k_game_state = GameState.from_instance(self.instance) - k_game_state_techs = k_game_state.research.technologies.values() - - for tech in k_game_state_techs: - skip = False - for k in tech.prerequisites: - if k == "space-science-pack": - skip = True - if not skip: - assert tech.researched - - -if __name__ == "__main__": - unittest.main() + +def test_drill_inventory(instance: FactorioInstance): + resp = instance.namespace.eval(FULL_PROGRAM_2) + + game_state = GameState.from_instance(instance) + + instance.reset() + instance.reset(game_state) + + print(resp) + + +def test_save_load_research_new_instance(instance, unresearched_instance): + # Save game state including research from a fully researched instance + game_state = GameState.from_instance(instance) + + # Verify that there are no technologies researched in the fresh instance + n_game_state = GameState.from_instance(unresearched_instance) + + game_state_techs = game_state.research.technologies.values() + n_game_state_techs = n_game_state.research.technologies.values() + + for i, j in zip(game_state_techs, n_game_state_techs): + assert i.name == j.name + + # Late game techs may be infinite and therefore are always not yet researched + skip = False + for k in i.prerequisites: + if k == "space-science-pack": + skip = True + + if not skip: + assert i.researched, f"Technology {i.name} should be researched" + assert not j.researched, f"Technology {j.name} should not be researched" + + # Load saved game state into the fresh instance and verify research persists + unresearched_instance.reset(game_state) + k_game_state = GameState.from_instance(unresearched_instance) + k_game_state_techs = k_game_state.research.technologies.values() + + for tech in k_game_state_techs: + skip = False + for k in tech.prerequisites: + if k == "space-science-pack": + skip = True + if not skip: + assert tech.researched diff --git a/tests/functional/test_auto_fueling_iron_smelting_factory.py b/tests/functional/test_auto_fueling_iron_smelting_factory.py index 5d22c97a5..6868f06fc 100644 --- a/tests/functional/test_auto_fueling_iron_smelting_factory.py +++ b/tests/functional/test_auto_fueling_iron_smelting_factory.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from time import sleep diff --git a/tests/functional/test_auto_refilling_coal.py b/tests/functional/test_auto_refilling_coal.py index 873ca776d..e0a496e72 100644 --- a/tests/functional/test_auto_refilling_coal.py +++ b/tests/functional/test_auto_refilling_coal.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Direction diff --git a/tests/functional/test_chemical_plants.py b/tests/functional/test_chemical_plants.py index 1342a5fe2..0a2ef8829 100644 --- a/tests/functional/test_chemical_plants.py +++ b/tests/functional/test_chemical_plants.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Entity diff --git a/tests/functional/test_defence.py b/tests/functional/test_defence.py index 3e08696df..1afeba6a8 100644 --- a/tests/functional/test_defence.py +++ b/tests/functional/test_defence.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Direction diff --git a/tests/functional/test_electricity_unit.py b/tests/functional/test_electricity_unit.py index c95d76ae0..223982262 100644 --- a/tests/functional/test_electricity_unit.py +++ b/tests/functional/test_electricity_unit.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance from time import sleep import pytest diff --git a/tests/functional/test_factory_unit.py b/tests/functional/test_factory_unit.py index 3c13ecc2d..7f86017da 100644 --- a/tests/functional/test_factory_unit.py +++ b/tests/functional/test_factory_unit.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import time import pytest diff --git a/tests/functional/test_full_oil_chain.py b/tests/functional/test_full_oil_chain.py index ffc4fee7d..51566eb08 100644 --- a/tests/functional/test_full_oil_chain.py +++ b/tests/functional/test_full_oil_chain.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, BuildingBox diff --git a/tests/functional/test_multi_drill_multi_furnace.py b/tests/functional/test_multi_drill_multi_furnace.py index 2a8001644..4c5aa773c 100644 --- a/tests/functional/test_multi_drill_multi_furnace.py +++ b/tests/functional/test_multi_drill_multi_furnace.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import BuildingBox, Position, Direction diff --git a/tests/functional/test_multiple_drills_multiple_furnaces.py b/tests/functional/test_multiple_drills_multiple_furnaces.py index da4becb2d..df106726d 100644 --- a/tests/functional/test_multiple_drills_multiple_furnaces.py +++ b/tests/functional/test_multiple_drills_multiple_furnaces.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Direction, BuildingBox diff --git a/tests/functional/test_objectives.py b/tests/functional/test_objectives.py index ea190e9dc..9b71ea68c 100644 --- a/tests/functional/test_objectives.py +++ b/tests/functional/test_objectives.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance from time import sleep from typing import List, Union diff --git a/tests/functional/test_research.py b/tests/functional/test_research.py index 71c0260ec..643e618dc 100644 --- a/tests/functional/test_research.py +++ b/tests/functional/test_research.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.commons.cluster_ips import get_local_container_ips @@ -8,7 +9,7 @@ @pytest.fixture() -def game(instance: FactorioInstance): +def game(unresearched_instance: FactorioInstance): initial_inventory = { "coal": 50, "copper-plate": 50, @@ -27,17 +28,9 @@ def game(instance: FactorioInstance): "lab": 1, "automation-science-pack": 10, } - ips, udp_ports, tcp_ports = get_local_container_ips() - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=tcp_ports[-1], - fast=True, - all_technologies_researched=False, - inventory=initial_inventory, - ) - instance.reset() - yield instance.namespace + unresearched_instance.reset() + unresearched_instance.set_inventory(initial_inventory) + yield unresearched_instance.namespace def test_craft_automation_packs_and_research(game): diff --git a/tests/functional/test_small_iron_factory.py b/tests/functional/test_small_iron_factory.py index 55b52226f..3434d82a9 100644 --- a/tests/functional/test_small_iron_factory.py +++ b/tests/functional/test_small_iron_factory.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game.entities import Position, Direction from fle.env.game.game_types import Prototype, Resource diff --git a/tests/gym_env/test_interface.py b/tests/gym_env/test_interface.py index 00a7d2001..3e6eb07e7 100644 --- a/tests/gym_env/test_interface.py +++ b/tests/gym_env/test_interface.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from gym import spaces diff --git a/tests/gym_env/test_observation.py b/tests/gym_env/test_observation.py index ce47d82fd..bfa26384f 100644 --- a/tests/gym_env/test_observation.py +++ b/tests/gym_env/test_observation.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance from fle.env.gym_env.environment import FactorioGymEnv from fle.env.gym_env.action import Action diff --git a/tests/gym_env/test_observation_formatter.py b/tests/gym_env/test_observation_formatter.py index 4d3d3363e..96fc59b15 100644 --- a/tests/gym_env/test_observation_formatter.py +++ b/tests/gym_env/test_observation_formatter.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance from fle.env.gym_env.observation_formatter import BasicObservationFormatter from fle.env.gym_env.observation import Observation, GameInfo, AgentMessage from fle.commons.models.achievements import ProductionFlows diff --git a/tests/gym_env/test_registry.py b/tests/gym_env/test_registry.py index ef9709164..ee82970db 100644 --- a/tests/gym_env/test_registry.py +++ b/tests/gym_env/test_registry.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance #!/usr/bin/env python3 """ Test script for the Factorio Gym Registry diff --git a/tests/multiagent/test_actions_multiagent.py b/tests/multiagent/test_actions_multiagent.py index a709f027d..a17bdf73e 100644 --- a/tests/multiagent/test_actions_multiagent.py +++ b/tests/multiagent/test_actions_multiagent.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.commons.models.program import Program diff --git a/tests/multiagent/test_agent_instructions.py b/tests/multiagent/test_agent_instructions.py index 2a1d17ccf..35ba21838 100644 --- a/tests/multiagent/test_agent_instructions.py +++ b/tests/multiagent/test_agent_instructions.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.agents.basic_agent import BasicAgent from fle.env.tasks import UnboundedThroughputTask diff --git a/tests/multiagent/test_messages.py b/tests/multiagent/test_messages.py index c231da570..23a6ff263 100644 --- a/tests/multiagent/test_messages.py +++ b/tests/multiagent/test_messages.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import asyncio import time from unittest.mock import MagicMock, patch diff --git a/tests/status/test_pipes_status.py b/tests/status/test_pipes_status.py index b2524b9f3..a967f0dfa 100644 --- a/tests/status/test_pipes_status.py +++ b/tests/status/test_pipes_status.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import Position, EntityStatus diff --git a/tests/status/test_poles_status.py b/tests/status/test_poles_status.py index 046cd8bc6..503cbe30f 100644 --- a/tests/status/test_poles_status.py +++ b/tests/status/test_poles_status.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import Direction, Position, EntityStatus diff --git a/tests/status/test_pump_status.py b/tests/status/test_pump_status.py index d3de80021..3933a4129 100644 --- a/tests/status/test_pump_status.py +++ b/tests/status/test_pump_status.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import math import pytest diff --git a/tests/test_eval.py b/tests/test_eval.py index 4633c1a5a..aa3743c12 100644 --- a/tests/test_eval.py +++ b/tests/test_eval.py @@ -1,6 +1,6 @@ +import pytest import unittest -from fle.commons.cluster_ips import get_local_container_ips from fle.env.game import FactorioInstance embedded_function = """ @@ -35,136 +35,84 @@ def inspect_inventory_wrapper(): # inventory=inventory) -def test_nested_functions(): - ips, udp_ports, tcp_ports = get_local_container_ips() - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=tcp_ports[-1], - fast=True, - # cache_scripts=False, - inventory={}, - ) +@pytest.fixture() +def game(instance: FactorioInstance): + instance.reset() + yield instance.agent_instances[0] + instance.reset() + + +def test_nested_functions(game: FactorioInstance): - score, goal, result = instance.eval_with_error("print(inspect_inventory())") + score, goal, result = game.eval_with_error("print(inspect_inventory())") assert result[3:] == "(Inventory({}),)" - score, goal, result = instance.eval_with_error(embedded_function) + score, goal, result = game.eval_with_error(embedded_function) assert result[3:] == "(Inventory({}),)" -def test_builtin_functions(): - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory={}, - ) +def test_builtin_functions(game: FactorioInstance): - score, goal, result = instance.eval_with_error("print(len('hello'))") + score, goal, result = game.eval_with_error("print(len('hello'))") assert result[4:-2] == "5" - score, goal, result = instance.eval_with_error("print(len([1,2,3,4,5]))") + score, goal, result = game.eval_with_error("print(len([1,2,3,4,5]))") assert result[4:-2] == "5" - score, goal, result = instance.eval_with_error( - "print(len({'a': 1, 'b': 2, 'c': 3}))" - ) + score, goal, result = game.eval_with_error("print(len({'a': 1, 'b': 2, 'c': 3}))") assert result[4:-2] == "3" - score, goal, result = instance.eval_with_error("print(len((1,2,3,4,5)))") + score, goal, result = game.eval_with_error("print(len((1,2,3,4,5)))") assert result[4:-2] == "5" - score, goal, result = instance.eval_with_error("print(len({1,2,3,4,5}))") + score, goal, result = game.eval_with_error("print(len({1,2,3,4,5}))") assert result[4:-2] == "5" -def test_math(): - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory={}, - ) +def test_math(game: FactorioInstance): - score, goal, result = instance.eval_with_error("print(sqrt(100))", timeout=60) + score, goal, result = game.eval_with_error("print(sqrt(100))", timeout=60) assert "10" in result -def test_loop_print(): - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory={}, - ) +def test_loop_print(game: FactorioInstance): - score, goal, result = instance.eval_with_error( + score, goal, result = game.eval_with_error( "for i in range(3):\n\tprint(i)", timeout=60 ) assert "2: (0,)\n2: (1,)\n2: (2,)" in result -def test_name_error(): - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory={}, - ) +def test_name_error(game: FactorioInstance): - score, goal, result = instance.eval_with_error( + score, goal, result = game.eval_with_error( "an_existing_variable=10\nprint(none_existing_variable)", timeout=60 ) assert "an_existing_variable" in result -def test_sleep(): - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory={}, - ) +def test_sleep(game: FactorioInstance): - score, goal, result = instance.eval_with_error("time.sleep(10)", timeout=60) + score, goal, result = game.eval_with_error("time.sleep(10)", timeout=60) assert "10" in result -def test_prototype_attribute_error(): - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory={}, - ) +def test_prototype_attribute_error(game: FactorioInstance): - score, goal, result = instance.eval_with_error( + score, goal, result = game.eval_with_error( "print(Prototype.AssemblingMachine)", timeout=60 ) assert "AssemblingMachine1" in result -def test_exceptions(): +def test_exceptions(game: FactorioInstance): inventory = { "iron-plate": 50, "coal": 100, @@ -183,15 +131,6 @@ def test_exceptions(): "iron-ore": 10, } - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory=inventory, - ) - test_string = """ # Check initial inventory iron_position = nearest(Resource.Stone) @@ -214,12 +153,12 @@ def test_exceptions(): print(furnaces) """ - score, goal, result = instance.eval_with_error(test_string, timeout=60) + score, goal, result = game.eval_with_error(test_string, timeout=60) pass -def test_chest_inventory(): +def test_chest_inventory(game: FactorioInstance): inventory = { "iron-plate": 50, "coal": 100, @@ -238,14 +177,6 @@ def test_chest_inventory(): "iron-ore": 10, } - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory=inventory, - ) test_string = """ # Check initial inventory iron_position = nearest(Resource.Stone) @@ -259,12 +190,12 @@ def test_chest_inventory(): chests = get_entities() print(chests) """ - score, goal, result = instance.eval_with_error(test_string, timeout=60) + score, goal, result = game.eval_with_error(test_string, timeout=60) pass -def test_try_catch(): +def test_try_catch(game: FactorioInstance): inventory = { "iron-plate": 50, "coal": 100, @@ -283,14 +214,6 @@ def test_try_catch(): "iron-ore": 10, } - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - # cache_scripts=False, - inventory=inventory, - ) test_string = """ try: if inspect_inventory().get(Prototype.AssemblingMachine1, 0) > 0: @@ -302,15 +225,12 @@ def test_try_catch(): except Exception as e: print(f"Failed placing Assembling Machine: {e}") """ - score, goal, result = instance.eval_with_error(test_string, timeout=60) + score, goal, result = game.eval_with_error(test_string, timeout=60) pass -def test_type_annotations_mixed_depth_prints(): - instance = FactorioInstance( - address="localhost", bounding_box=200, tcp_port=27000, fast=True - ) +def test_type_annotations_mixed_depth_prints(game: FactorioInstance): # cache_scripts=False, test_string = """ print("Re-evaluating precise point harvest attempts.") @@ -329,12 +249,12 @@ def test_type_annotations_mixed_depth_prints(): print(f"Failed at {trial_position}: {e}") """ - score, goal, result = instance.eval_with_error(test_string, timeout=60) + score, goal, result = game.eval_with_error(test_string, timeout=60) pass -def test_mixed_hard(): +def test_mixed_hard(game: FactorioInstance): inventory = { "iron-plate": 50, "coal": 100, @@ -353,13 +273,6 @@ def test_mixed_hard(): "iron-ore": 10, } - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory=inventory, - ) # cache_scripts=False, test_string = """ # Move to the center of the valid stone patch @@ -411,12 +324,12 @@ def test_mixed_hard(): produced_iron_plates = extract_item(Prototype.IronPlate, furnace.position, quantity=10) assert produced_iron_plates == 10, f"Expected 10 iron plates, but got {produced_iron_plates}." """ - score, goal, result = instance.eval_with_error(test_string, timeout=60) + score, goal, result = game.eval_with_error(test_string, timeout=60) pass -def test_mixed_hard2(): +def test_mixed_hard2(game: FactorioInstance): inventory = { "iron-plate": 50, "coal": 100, @@ -435,13 +348,6 @@ def test_mixed_hard2(): "iron-ore": 10, } - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=27000, - fast=True, - inventory=inventory, - ) # cache_scripts=False, test_string = """ # Specify the drill drop position @@ -483,7 +389,7 @@ def test_mixed_hard2(): except Exception as e_map_error: print(f"Error verifying map entities: {e_map_error}") """ - score, goal, result = instance.eval_with_error(test_string, timeout=60) + score, goal, result = game.eval_with_error(test_string, timeout=60) pass diff --git a/tests/test_functions.py b/tests/test_functions.py index 6a09d3aa9..f8800158e 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest diff --git a/tests/test_inventory_deduction.py b/tests/test_inventory_deduction.py index 067f0283f..353514a53 100644 --- a/tests/test_inventory_deduction.py +++ b/tests/test_inventory_deduction.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.env.game import FactorioInstance from fle.env.game.game_types import Prototype diff --git a/tests/test_production_stats.py b/tests/test_production_stats.py index fe1d60fd9..e5b45eec0 100644 --- a/tests/test_production_stats.py +++ b/tests/test_production_stats.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import unittest from fle.env.game import FactorioInstance diff --git a/tests/test_rcon_utils.py b/tests/test_rcon_utils.py index bcceee96f..8161c9075 100644 --- a/tests/test_rcon_utils.py +++ b/tests/test_rcon_utils.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest from fle.services.rcon import _lua2python diff --git a/tests/test_variables.py b/tests/test_variables.py index 10beed187..199fb2b58 100644 --- a/tests/test_variables.py +++ b/tests/test_variables.py @@ -1,3 +1,4 @@ +from fle.env.game import FactorioInstance import pytest diff --git a/tests/test_warnings.py b/tests/test_warnings.py index b349ee8f0..909212bc3 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -1,22 +1,21 @@ +import pytest import unittest -from fle.commons.cluster_ips import get_local_container_ips from fle.env.game.game_types import Prototype, Resource -from fle.env.game import FactorioInstance, Direction +from fle.env.game import Direction from fle.env.game.game_state import GameState +from fle.env.game.instance import AgentInstance, FactorioInstance -def test_drop_box_chest(): - ips, udp_ports, tcp_ports = get_local_container_ips() - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=tcp_ports[-1], - fast=True, - # cache_scripts=False, - inventory={"burner-mining-drill": 1, "iron-chest": 1, "coal": 10}, - ) - instance.get_system_prompt() +@pytest.fixture() +def game(instance: FactorioInstance): + instance.reset() + yield instance + instance.reset() + + +def test_drop_box_chest(instance: AgentInstance): + instance.agent_instances[0].get_system_prompt() instance.namespace.move_to(instance.namespace.nearest(Resource.IronOre)) drill = instance.namespace.place_entity( Prototype.BurnerMiningDrill, @@ -41,16 +40,8 @@ def test_drop_box_chest(): assert not drill.warnings -def test_full_chest(): - ips, udp_ports, tcp_ports = get_local_container_ips() - instance = FactorioInstance( - address="localhost", - bounding_box=200, - tcp_port=tcp_ports[-1], - fast=True, - # cache_scripts=False, - inventory={"burner-mining-drill": 1, "wooden-chest": 1, "coal": 2000}, - ) +def test_full_chest(instance: FactorioInstance): + instance.set_inventory({"burner-mining-drill": 1, "wooden-chest": 1, "coal": 2000}) chest = instance.namespace.place_entity(Prototype.WoodenChest, Direction.UP) for i in range(16): From 0060f6f58c74825665d67e5a04729c47b14f9ecc Mon Sep 17 00:00:00 2001 From: hrshtt Date: Thu, 14 Aug 2025 15:30:04 +0530 Subject: [PATCH 53/53] almost there --- fle/env/environment.py | 17 ++- fle/env/game/instance.py | 33 ++++- fle/env/game/tools/agent/score/client.py | 2 +- fle/env/registry.py | 1 + fle/env/session.py | 126 +++++++++--------- fle/env/trajectory_runner.py | 11 +- fle/env/utils/profits.py | 24 ++-- fle/eval/algorithms/beam/beam_search.py | 2 +- fle/eval/algorithms/mcts/instance_group.py | 2 +- fle/eval/algorithms/mcts/mcts.py | 2 +- fle/eval/algorithms/mcts/objective_mcts.py | 2 +- fle/eval/algorithms/mcts/parallel_mcts.py | 2 +- .../algorithms/mcts/parallel_planning_mcts.py | 2 +- .../mcts/supervised_task_executor_abc.py | 2 +- fle/services/docker/config.py | 6 +- fle/services/docker/docker_manager.py | 46 +++++-- 16 files changed, 173 insertions(+), 107 deletions(-) diff --git a/fle/env/environment.py b/fle/env/environment.py index 21fa927e3..6c9234d4e 100644 --- a/fle/env/environment.py +++ b/fle/env/environment.py @@ -291,7 +291,10 @@ def step( if self.game_session.task: game_result = self.game_session.verify_task(reward, game_result) - terminated = task_response.success + # verify_task mutates game_result.result to be a TaskResponse + if isinstance(game_result.result, TaskResponse): + task_response = game_result.result + terminated = bool(task_response.success) # Get observation for the acting agent observation = game_result.partial_observation.add_response( @@ -312,6 +315,18 @@ def step( return observation.to_dict(), reward, terminated, truncated, info + def reset( + self, *, seed: Optional[int] = None, options: Optional[Dict[str, Any]] = None + ) -> Tuple[Dict[str, Any], Dict[str, Any]]: + """Gymnasium-compatible reset that delegates to the game session. + + Returns (observation, info). + """ + # Gym API requires calling super().reset(seed=seed) + super().reset(seed=seed) + obs, info = self.game_session.reset(options=options, seed=seed) + return obs, info + def close(self): """Clean up resources""" self.game_session.cleanup() diff --git a/fle/env/game/instance.py b/fle/env/game/instance.py index 8c1944d63..503e6af7b 100644 --- a/fle/env/game/instance.py +++ b/fle/env/game/instance.py @@ -227,8 +227,16 @@ def first_namespace( ) -> Optional[FactorioNamespace]: # Add this property if used return self.agent_instances[0].namespace if self.agent_instances else None + @property + def namespaces(self) -> List[FactorioNamespace]: + return list([ai.namespace for ai in self.agent_instances.values()]) + def save(self, save_name: str) -> None: self.client.send_command(f"/save {save_name}") + + def set_inventory(self, inventory: Dict[str, Any]): + for instance in self.agent_instances.values(): + instance.set_inventory(self.client, inventory) @property def is_multiagent(self): @@ -264,23 +272,32 @@ def _reset_elapsed_ticks(self): t.add_command("/sc global.elapsed_ticks = 0", raw=True) def _reset(self, inventories: List[Dict[str, Any]]): + # Reset core game state and production stats with self.client.transaction() as t: t.add_command( - "/sc global.alerts = {}; game.reset_game_state(); global.actions.reset_production_stats(); global.actions.regenerate_resources(1)", + "/sc global.alerts = {}; game.reset_game_state(); global.actions.reset_production_stats()", raw=True, ) + # Re-create agent characters; first remove any lingering ones + self._create_agent_game_characters() + + # Clear movement queues (safe now that characters exist) with self.client.transaction() as t: t.add_command("/sc global.actions.clear_walking_queue()", raw=True) - if self.all_technologies_researched: + # Per-agent reset: resources, entities, inventories + for instance in self.agent_instances.values(): + instance._reset(self.client, inventories[instance.agent_idx]) + + # Apply research after clear_entities (which calls player.force.reset) + if self.all_technologies_researched: + with self.client.transaction() as t: t.add_command( "/sc global.agent_characters[1].force.research_all_technologies()", raw=True, ) - for instance in self.agent_instances.values(): - instance._reset(self.client, inventories[instance.agent_idx]) - # self.clear_entities() + self._reset_static_achievement_counters() self._reset_elapsed_ticks() @@ -384,8 +401,12 @@ def initialise(self, fast=True, invalidate_cache=False): def _create_agent_game_characters(self): """Create Factorio characters for all agents in the game.""" - # Create characters in Factorio with self.client.transaction() as t: + # Destroy any lingering character entities from prior runs + t.add_command( + "/sc for _,c in pairs(game.surfaces[1].find_entities_filtered{type='character'}) do if c and c.valid then c.destroy() end end", + raw=True, + ) t.add_command( f'/sc global.agent_characters = {{}};', raw=True, diff --git a/fle/env/game/tools/agent/score/client.py b/fle/env/game/tools/agent/score/client.py index 7045632fc..112aace1f 100644 --- a/fle/env/game/tools/agent/score/client.py +++ b/fle/env/game/tools/agent/score/client.py @@ -21,7 +21,7 @@ def __call__(self, *args, **kwargs): if isinstance(response, str): raise Exception("Could not get player score", response) - return response, goal + return response["player"], goal # if __name__ == "__main__": diff --git a/fle/env/registry.py b/fle/env/registry.py index 90541abb6..be9372cb8 100644 --- a/fle/env/registry.py +++ b/fle/env/registry.py @@ -92,6 +92,7 @@ def discover_tasks(self) -> None: except Exception as e: print(f"Warning: Failed to load task definition {task_file}: {e}") + raise e self._discovered = True diff --git a/fle/env/session.py b/fle/env/session.py index 2604c40d9..5d1a74b7a 100644 --- a/fle/env/session.py +++ b/fle/env/session.py @@ -38,24 +38,14 @@ class AgentSession: class Snapshot: score: float flows: ProductionFlows - tick: int timestamp: float observation: Optional[Observation] = None + @dataclass class AgentResult: pre: "AgentSession.Snapshot" - post: Optional["AgentSession.Snapshot"] result: str - - def __init__( - self, - pre: "AgentSession.Snapshot", - post: Optional["AgentSession.Snapshot"], - result: str, - ): - self.pre = pre - self.post = post - self.result = result + post: Optional["AgentSession.Snapshot"] = None def set_post(self, post: "AgentSession.Snapshot") -> None: self.post = post @@ -80,26 +70,25 @@ def flows_delta(self) -> ProductionFlows: last_message_timestamp: float last_observation: Optional[Observation] _last_production_flow: Optional[ProductionFlows] - version: int + _version: int version_description: str - db_client: DBClient def __init__( self, agent_idx: int, agent_instance: AgentInstance, - db_client: DBClient, ): self.agent_idx = agent_idx self.steps = 0 self.agent_instance = agent_instance self.namespace = agent_instance.namespace - self.db_client = db_client + self._version = 0 + self.version_description = "" self.reset() @property def version(self) -> int: - return self.db_client.get_largest_version() + return self._version def get_partial_observation( self, @@ -164,24 +153,6 @@ def get_partial_observation( self.last_observation = observation return observation - - async def save_program(self, program: Program) -> Program: - program = await self.db_client.create_program(program) - program.update_program_id(program.id) - return program - - async def get_resume_state(self, process_id: int) -> Tuple[GameState, Conversation]: - ( - current_state, - agent_conversation, - parent_id, - depth, - ) = await self.db_client.get_resume_state( - resume_version=self.version, - process_id=process_id, - agent_idx=self.agent_idx, - ) - return current_state, agent_conversation, parent_id, depth @property def current_score(self) -> float: @@ -200,7 +171,9 @@ def _snapshot(self, observation: Optional[Observation] = None) -> Snapshot: observation=observation, ) - def eval_with_snapshot(self, code: str, value_accrual_time: float = 0) -> AgentResult: + def eval_with_snapshot( + self, code: str, value_accrual_time: float = 0 + ) -> AgentResult: initial_snapshot = self._snapshot() @@ -259,32 +232,22 @@ class GameResult: agent_result: AgentSession.AgentResult partial_observation: Observation - def __init__( - self, - post: "GameSession.Snapshot", - result: AgentSession.AgentResult, - partial_observation: Observation, - ): - self.post = post - self.agent_result = result - self.partial_observation = partial_observation - @property def result(self) -> str: return self.agent_result.result - + @result.setter def result(self, value: str) -> None: self.agent_result.result = value - + @property def score_delta(self) -> float: return self.agent_result.score_delta - + @property def error_occurred(self) -> bool: return self.agent_result.error_occurred - + @property def flows_delta(self) -> ProductionFlows: return self.agent_result.flows_delta @@ -332,9 +295,17 @@ def _snapshot(self) -> "GameSession.Snapshot": self.current_game_info, ) - async def get_agent_session_resume_state(self, agent_idx: int, process_id: int) -> Tuple[GameState, Conversation]: + def get_agent_observation(self, agent_idx: int) -> Observation: + agent_session = self.agent_sessions[agent_idx] + return agent_session.get_partial_observation(self.current_game_info) + + async def get_agent_session_resume_state( + self, agent_idx: int, version: int, process_id: int + ) -> Tuple[GameState, Conversation]: agent_session = self.agent_sessions[agent_idx] - current_state, agent_conversation, parent_id, depth = await agent_session.get_resume_state(process_id=process_id) + current_state, agent_conversation, parent_id, depth = ( + await self.get_resume_state(agent_idx=agent_idx, version=version, process_id=process_id) + ) if current_state: agent_session.steps = depth if not current_state and self.task: @@ -345,14 +316,14 @@ def eval_agent_with_snapshot( self, agent_idx: int, code: str, value_accrual_time: int ): agent_session = self.agent_sessions[agent_idx] - if agent_session.last_observation: - self.reset_instance(agent_session.last_observation.state) agent_eval_results = agent_session.eval_with_snapshot( code=code, value_accrual_time=value_accrual_time ) post_snapshot = self._snapshot() - partial_observation = agent_session.get_partial_observation(post_snapshot.game_info) + partial_observation = agent_session.get_partial_observation( + post_snapshot.game_info + ) return self.GameResult( post=post_snapshot, @@ -361,7 +332,10 @@ def eval_agent_with_snapshot( ) def verify_task( - self, reward: float, game_result: GameResult, step_statistics: Dict[str, Any] = {} + self, + reward: float, + game_result: GameResult, + step_statistics: Dict[str, Any] = {}, ) -> TaskResponse: if not self.task: print(f"[WARN] No task to verify, instance: {self.instance_id}") @@ -374,12 +348,15 @@ def verify_task( ) game_result.result = task_response return game_result - + async def _make_agent_sessions(self) -> Dict[int, AgentSession]: - return { - i: AgentSession(i, self.instance.agent_instances[i], await create_db_client()) - for i in range(self.instance.num_agents) - } + sessions: Dict[int, AgentSession] = {} + self.db_client = await create_db_client() + for i in range(self.instance.num_agents): + session = AgentSession(i, self.instance.agent_instances[i]) + # await session.initialise() + sessions[i] = session + return sessions def reinitialize_instance(self) -> None: if isinstance(self.instance, A2AFactorioInstance): @@ -413,7 +390,7 @@ def reset_instance(self, state: Optional[GameState] = None) -> None: state: Optional[GameState] to reset to. If None, resets to initial state. """ self.instance.reset(state) - for session in self.agent_sessions: + for session in self.agent_sessions.values(): session.reset() def reset( @@ -430,12 +407,33 @@ def reset( game_state = options.get("game_state") self.reset_instance(game_state) - for session in self.agent_sessions: + for session in self.agent_sessions.values(): session.reset() # Convert observation to dictionary to match gym standards - observation = self.agent_sessions[0].get_partial_observation().to_dict() + first_session = self.agent_sessions[0] + observation = first_session.get_partial_observation( + self.current_game_info + ).to_dict() return observation, {} # Return observation for first agent + async def save_program(self, program: Program) -> Program: + program = await self.db_client.create_program(program) + program.update_program_id(program.id) + return program + + async def get_resume_state(self, agent_idx: int, version: int, process_id: int) -> Tuple[GameState, Conversation]: + ( + current_state, + agent_conversation, + parent_id, + depth, + ) = await self.db_client.get_resume_state( + resume_version=version, + process_id=process_id, + agent_idx=agent_idx, + ) + return current_state, agent_conversation, parent_id, depth + def cleanup(self) -> None: self.instance.cleanup() diff --git a/fle/env/trajectory_runner.py b/fle/env/trajectory_runner.py index dbc69a0a3..e60d6a427 100644 --- a/fle/env/trajectory_runner.py +++ b/fle/env/trajectory_runner.py @@ -85,7 +85,7 @@ async def _initialize_trajectory_state(self) -> Tuple[GameState, List[int]]: await game_session.initialise() first_idx = list(game_session.agent_sessions.keys())[0] current_state, agent_conversation = ( - await game_session.get_agent_session_resume_state(first_idx, self.process_id) + await game_session.get_agent_session_resume_state(first_idx, self.config.version, self.process_id) ) if agent_conversation: self.agents[first_idx].reset(agent_conversation) @@ -96,9 +96,7 @@ async def _initialize_trajectory_state(self) -> Tuple[GameState, List[int]]: agent = self.agents[agent_idx] agent_session = game_session.agent_sessions[agent_idx] conversation = Conversation() - initial_obs = agent_session.get_partial_observation( - game_session.current_game_info - ) + initial_obs = game_session.get_agent_observation(agent_idx) formatted_obs = agent.observation_formatter.format(initial_obs).raw_str conversation.add_user_message(formatted_obs) agent.reset(conversation) @@ -140,7 +138,7 @@ async def _run_trajectory_step(self, agent: GymAgent, agent_session: AgentSessio version_description=agent_session.version_description, ) - program = await agent_session.save_program(program) + program = await self.gym_env.game_session.save_program(program) # Update agent's conversation with the program and its results await agent.update_conversation(observation, previous_program=program) @@ -196,4 +194,5 @@ async def run_sequential(self): print( f"Error in trajectory runner iteration {agent_session.steps}: {e}" ) - continue + # continue + raise e diff --git a/fle/env/utils/profits.py b/fle/env/utils/profits.py index 1924f87c2..495beeeaa 100644 --- a/fle/env/utils/profits.py +++ b/fle/env/utils/profits.py @@ -1,8 +1,12 @@ -def eval_program_with_profits(instance, program, profit_config): - pre_production_flows = instance.get_production_stats() +from fle.commons.models.achievements import ProductionFlows +from fle.env.game.namespace import FactorioNamespace + + +def eval_program_with_profits(namespace: FactorioNamespace, program: str, profit_config: dict): + pre_production_flows = namespace._get_production_stats() # evaluate the step try: - score, goal, result = instance.eval_with_error(program, timeout=300) + score, goal, result = namespace.eval_with_error(program, timeout=300) error = False except Exception as e: result = e @@ -11,7 +15,7 @@ def eval_program_with_profits(instance, program, profit_config): error = True # split result by newlines output_list = result.splitlines() - post_production_flows = instance.get_production_stats() + post_production_flows = namespace._get_production_stats() profits = get_profits( pre_production_flows, post_production_flows, profit_config=profit_config ) @@ -49,13 +53,13 @@ def get_profits( # merge the crafted and harvested dicts to one dict static_profits, new_production_flows = get_static_profits( new_production_flows, - pre_production_flows["price_list"], + pre_production_flows.get("price_list", {}), max_craft_cap=max_static_unit_profit_cap, ) total_profits["static"] = static_profits dynamic_profits = get_dynamic_profits( new_production_flows, - pre_production_flows["price_list"], + pre_production_flows.get("price_list", {}), dynamic_profit_multiplier=dynamic_profit_multiplier, ) total_profits["dynamic"] = dynamic_profits @@ -148,11 +152,11 @@ def get_dynamic_profits(new_production_flows, price_list, dynamic_profit_multipl return dynamic_profits -def eval_program_with_achievements(instance, program): - pre_production_flows = instance.get_production_stats() +def eval_program_with_achievements(namespace: FactorioNamespace, program: str): + pre_production_flows = namespace._production_stats() # evaluate the step try: - score, goal, result = instance.eval_with_error(program, timeout=300) + score, goal, result = namespace.eval_with_error(program, timeout=300) error = False except Exception as e: result = e @@ -161,7 +165,7 @@ def eval_program_with_achievements(instance, program): error = True # split result by newlines output_list = result.splitlines() - post_production_flows = instance.get_production_stats() + post_production_flows = namespace._production_stats() achievements = get_achievements(pre_production_flows, post_production_flows) return output_list, result, error, achievements diff --git a/fle/eval/algorithms/beam/beam_search.py b/fle/eval/algorithms/beam/beam_search.py index 1a8360f4b..679e8e4ac 100644 --- a/fle/eval/algorithms/beam/beam_search.py +++ b/fle/eval/algorithms/beam/beam_search.py @@ -19,7 +19,7 @@ from fle.commons.models.message import Message from fle.commons.models.program import Program from fle.env.game import FactorioInstance -from fle.eval.evaluator import Evaluator +from fle.eval.algorithms.mcts.evaluator import Evaluator from ..mcts import MCTS, GroupedFactorioLogger, InstanceGroup diff --git a/fle/eval/algorithms/mcts/instance_group.py b/fle/eval/algorithms/mcts/instance_group.py index 299f8055d..aadf9dba6 100644 --- a/fle/eval/algorithms/mcts/instance_group.py +++ b/fle/eval/algorithms/mcts/instance_group.py @@ -1,7 +1,7 @@ from dataclasses import dataclass from typing import List -from fle.eval.evaluator import Evaluator +from fle.eval.algorithms.mcts.evaluator import Evaluator from fle.eval.algorithms.mcts.mcts import MCTS from fle.env.game.instance import FactorioInstance diff --git a/fle/eval/algorithms/mcts/mcts.py b/fle/eval/algorithms/mcts/mcts.py index 845fbf39b..5ba76f86d 100644 --- a/fle/eval/algorithms/mcts/mcts.py +++ b/fle/eval/algorithms/mcts/mcts.py @@ -21,7 +21,7 @@ from fle.commons.models.message import Message from fle.commons.models.program import Program -from fle.eval.mcts.evaluator import Evaluator +from fle.eval.algorithms.mcts.evaluator import Evaluator from fle.eval.algorithms.mcts.samplers import DBSampler diff --git a/fle/eval/algorithms/mcts/objective_mcts.py b/fle/eval/algorithms/mcts/objective_mcts.py index 9a7304210..3edea07dc 100644 --- a/fle/eval/algorithms/mcts/objective_mcts.py +++ b/fle/eval/algorithms/mcts/objective_mcts.py @@ -17,7 +17,7 @@ from fle.commons.models.generation_parameters import GenerationParameters from fle.commons.models.message import Message from fle.commons.models.program import Program -from fle.eval.evaluator import Evaluator +from fle.eval.algorithms.mcts.evaluator import Evaluator from .mcts import MCTS from .samplers.db_sampler import DBSampler diff --git a/fle/eval/algorithms/mcts/parallel_mcts.py b/fle/eval/algorithms/mcts/parallel_mcts.py index 76b0789eb..883ede357 100644 --- a/fle/eval/algorithms/mcts/parallel_mcts.py +++ b/fle/eval/algorithms/mcts/parallel_mcts.py @@ -6,7 +6,7 @@ from fle.eval.algorithms.mcts.grouped_logger import GroupedFactorioLogger from fle.eval.algorithms.mcts.instance_group import InstanceGroup from fle.eval.algorithms.mcts.parallel_mcts_config import ParallelMCTSConfig -from fle.eval.evaluator import Evaluator +from fle.eval.algorithms.mcts.evaluator import Evaluator from rich.console import Console from fle.services.db.db_client import DBClient diff --git a/fle/eval/algorithms/mcts/parallel_planning_mcts.py b/fle/eval/algorithms/mcts/parallel_planning_mcts.py index 357e52a77..56b48538a 100644 --- a/fle/eval/algorithms/mcts/parallel_planning_mcts.py +++ b/fle/eval/algorithms/mcts/parallel_planning_mcts.py @@ -30,7 +30,7 @@ TaskOutput, ) from fle.eval.algorithms.mcts.planning_mcts import get_mining_setup -from fle.eval.evaluator import Evaluator +from fle.eval.algorithms.mcts.evaluator import Evaluator logger = logging.basicConfig(level=logging.INFO) diff --git a/fle/eval/algorithms/mcts/supervised_task_executor_abc.py b/fle/eval/algorithms/mcts/supervised_task_executor_abc.py index de4d94c0e..8946010a9 100644 --- a/fle/eval/algorithms/mcts/supervised_task_executor_abc.py +++ b/fle/eval/algorithms/mcts/supervised_task_executor_abc.py @@ -12,7 +12,7 @@ from fle.commons.models.generation_parameters import GenerationParameters from fle.agents.formatters.conversation_formatter_abc import DefaultFormatter from fle.services.db.db_client import DBClient -from fle.eval.evaluator import Evaluator +from fle.eval.algorithms.mcts.evaluator import Evaluator from fle.eval.algorithms.mcts.grouped_logger import GroupedFactorioLogger from fle.eval.algorithms.mcts.parallel_supervised_config import SupervisedExecutorConfig from fle.eval.algorithms.mcts.planning_models import ( diff --git a/fle/services/docker/config.py b/fle/services/docker/config.py index 40d51b29d..2219fd51d 100644 --- a/fle/services/docker/config.py +++ b/fle/services/docker/config.py @@ -65,9 +65,7 @@ class DockerConfig(BaseModel): scenario_name: str = Scenario.DEFAULT_LAB_SCENARIO.value scenario_dir: Path = Field( - default_factory=lambda: Path(__file__).parent.resolve() - / "factorio" - / "scenarios" + default_factory=lambda: ROOT_DIR / "env" / "game" / "factorio" / "scenarios" ) server_config_dir: Path = Field( default_factory=lambda: ROOT_DIR / "env" / "game" / "factorio" / "config" @@ -85,7 +83,7 @@ class DockerConfig(BaseModel): .strip() ) - model_config = {"extra": "forbid", "frozen": True} + # model_config = {"extra": "forbid", "frozen": True} config = DockerConfig() diff --git a/fle/services/docker/docker_manager.py b/fle/services/docker/docker_manager.py index 29a7d53fb..e4c965ada 100644 --- a/fle/services/docker/docker_manager.py +++ b/fle/services/docker/docker_manager.py @@ -3,6 +3,7 @@ import aiodocker from aiodocker.exceptions import DockerError from typing import List, Dict +import shutil from fle.services.docker.config import DockerConfig, Scenario, Mode @@ -77,12 +78,29 @@ async def restart(self, save_name: str = None): await ctr.restart() def _make_save_latest(self, save_name: str): + """ + Make the given save the one that will be loaded on next restart. + + - In SAVE_BASED mode (with fixed SAVE_NAME), overwrite the runtime save + `_run.zip` with the chosen save. + - Otherwise (legacy/mtime workflows), touch the chosen save so it becomes latest. + """ save_dir = self.config.saves_path / str(self.instance_id) save_dir.mkdir(parents=True, exist_ok=True) - save_path = save_dir / f"{save_name}.zip" - if not save_path.exists(): + src = save_dir / f"{save_name}.zip" + if not src.exists(): raise Exception(f"Save {save_name} not found") - save_path.touch(exist_ok=True) + + # If we're in save-based mode, we always load the fixed runtime save name. + # So replace the runtime save with the selected source save. + if self.config.mode == Mode.SAVE_BASED.value: + runtime = save_dir / f"{self.config.scenario_name}_run.zip" + # Copy regardless of whether runtime exists, to guarantee switch. + shutil.copy2(src, runtime) + return + + # Fallback: touch to bump mtime (for setups that still rely on LOAD_LATEST_SAVE) + src.touch(exist_ok=True) class FactorioHeadlessClusterManager: @@ -154,18 +172,22 @@ def _get_volumes(self, instance_index: int, for_server: bool = True) -> list: def _get_environment(self) -> list: env = { - "LOAD_LATEST_SAVE": ( - "true" if self.config.mode == Mode.SAVE_BASED.value else "false" - ), "PORT": str(self.config.udp_port), "RCON_PORT": str(self.config.rcon_port), "SERVER_SCENARIO": self.config.scenario_name, "DLC_SPACE_AGE": "false", "MODE": self.config.mode, - # "SAVE_NAME": save_name, } - if self.config.mode == Mode.SCENARIO.value: + + # In save-based mode, pin to a fixed runtime save name and avoid mtime-based selection + if self.config.mode == Mode.SAVE_BASED.value: + env["LOAD_LATEST_SAVE"] = "false" + env["SAVE_NAME"] = f"{self.config.scenario_name}_run" + else: + # Scenario mode behavior unchanged + env["LOAD_LATEST_SAVE"] = "false" env["PRESET"] = "default" + # Docker API wants ["KEY=VALUE", ...] return [f"{k}={v}" for k, v in env.items()] @@ -252,6 +274,14 @@ async def start(self): await asyncio.gather(*[item.wait() for item in scenario2map_ctrs]) await asyncio.gather(*[item.delete() for item in scenario2map_ctrs]) + # Ensure a separate runtime save exists (copy-once from baseline) + for i in range(self.num): + save_dir = self.config.saves_path / str(i) + baseline = save_dir / f"{self.config.scenario_name}.zip" + runtime = save_dir / f"{self.config.scenario_name}_run.zip" + if baseline.exists() and not runtime.exists(): + shutil.copy2(baseline, runtime) + server_ctrs = await asyncio.gather( *[self.get_server_ctr(i) for i in range(self.num)] )

    W+~ zfdOJc3#`Q*09O_-G5}zWWd2AOARHH6fUYfL|NB0GX#er2`9G|agWkOWbwG;0?{6OE z?mY3{yWa(Q|DEw*Z&LFIIv*hbed+#tA0qvC7d?GR1ppZg8i_x7MLXO;TOdFNAO}S$ z!@KuDVl5q_&TfAbe7}HZFw0k>E9iNNX%|w*rS(o{@5av7$-SK?vcvhU0U6Il#2(%5 z#^ub2T((+Vqdx*R(1P*DARUPRISH(xAd0QRG+VKHOu1g+rl9`xvxxoZl0%nEwU90d z*>Q$zarI1@;UwO+U4~!@`99izwgoLfjoRB@lzn5rd3JV|hdc4gx;(uY6t+QR zIiC&lPHW!3Xq3-p+s`kx9-Yl+APH?SwQViY&A*{MIu@V(5nxF!;C z*7}gHH)i+>x!mSt1|fTI6D`fB!^~)qDR2d((r6 z^zIv7_z5;w1q6X8q*|`yBbB{g?)od9aU-brN)^zA%v`omL1P77=b;{WuxAm>i>BC? zHA^*F-(#{M{}=)y9=g-h!&H~ig(|urO=Ojj`%UEdz|qZ!LIaRqCX>%JMKgn6r*6qC zx0DnFIn-2y8+uns=mC{Trx7_gwq+_|F$MaNi}C==^tEOP*By8R9+yQ$1nocr%20*$ zdI?K5SKzJdiDZ#eLdGX-V3|e!ze+jfJJ*oQhP=zc#HoH;7 zG0};d2x4hU><0YLGXT`%=%0G*5D@wQJtRP0i>|5P<&=`G*c5jsZ~#u>wHbuLuQL6b=*K0BIKRvG}9`;&(CMdqe31W-xlclr6RIr=#!0T zV04akc6MGRLpI6)+s1{iQK2*0f{dKn5YBdSmvaMZy;SM*?M%Kf;p!~}3_R~jbr+zt zkU>;%J|%^MQYIr0K6KGegYk!) zjn7}S&tJ*yzSYDm;e^h7{(W#pKJ#L^{ouhiXU_bP8~3?HingShgn49*a@lNoWhv@j zUMUwbWw6m=1g@AXH0g2-ht9ROtYJA zM@O~P>HR%hwcG%cI~ZNkEjp+R9+N}=kWEgLHfcqdWs{@2fuUPuXfTjHNv9>QTD(CM z*uX_i&yC&_AKgUPfE(8<60JcJhVQ>=Gg4)p7W{-vV1H9?jhYHte6#TQS^XewAE&44GrU9VxcO46-*^U36ZL0SCOC;>a2*gLb0e21S?&r5Q`#~hX_!Y z%>&Y+bT?WF@xZ?HjsJl287JFae*b3tGl`pR;d}4r-gD167Y-l*{_qQnDv9Y?0vt$Z z-d{vO9D-noe8js#{|^j+Z)*Vj(t>@_q1Uh{u$0!)0sJq-}< zp8B6Wu6PIl*gq)!y*_}f0q|n}g4g~N^Uvx0?_B=-XGHx6{?E&o9F#hM>dVhD+{dB+ zi=Y7Fm`4u4QI6MXN&l3{1V}~jhtKhI@7%`#V448%^^`p9Ee3rTP{F~@K{g(53J1!abNcEpaah0lS&n&cF#NTAmk)q?KQ9+Twe;+)*_#;-plL&I$p=a++Fl zH~>qg7GpUlub4TyXNChNmF?8B05CX>6~ulw>7;`2;)DV>0|1kT&cQMa7Os4IhqDj_ z?CoJI`1;;@ZS!!iU0?7=BdGsV0%7niurS>Z)P?M_TNI5z#NOkw0t287@cNYD+# z4V4jvrE(?>3y)O2(TEq8;@RwSA)Q`<E!!0?wicG#@OP;S}?>7Ap2RmGYqhXEf2| zm9$97$)Jy7lZa#4hX55p=dTP#C>4tH$QmrC1_BpB6chy|&2?=i#n6(Vt9(f;Axy(9 zfQby7%C5>}2d^s2a~a=49J+sq_7)&e@bj!BVQdkaONmGrkQN~1Ln(x@tjN&yr7-x% zLl9?ufDL&m9K1?h6lZ5xEE)&oKW(1D?3D8;5Ssg68X;g2r&46mc_~K5ksT7FBKZHTV{tN1#cDs>{Yn{U0;reQ= zmd>*x4Nx1iZ)Zz}d#dVaGBs8bFP2fD%+44Iru; zz_^=m`!O?jPh7f~Np}Z1W2;IJSp}p*EC`LWm2x!3Kd46$dj^AVQp@ zmcif+-~%lDL-3mw}DRjkcWryfHDqE+m z+*e=SHFB@^n@sO_?(zelj~m@<-cv?O@779O9fpp&Axr=hA&G~Nf6mM|2PM}jNfh>h z8v{6*IGQ5U6_H&k8lb1+@E{vBSAbwbeGze-NiO z*RSs!w8O=44O0S)0n4c{ml2lJFzt$G!>q#~{;g_QmcxNs2-zf-fnCBi2!w?Qw0hz6 zQiiqbb?j1ZFD;eR9e@kz)+3!279@-e6SSn#EU#3<%y0hWKw)Vyc@5+Ap#=sHY6`%C zB1Q9@#Y*t(&(bO#orCiLWheRRf!&#gWM3JYRfJiA=z|18H>1Q^^$-FsY zLXB7w>O2f;W@ECh<0mt)Du!_=Hj_t+ybx4rI^f5e08)JfB}s+XfN(W<5rII7h-6VE zvpzPAbtRGHm7+vhq_Ll!@u)x87<@5==m<&VIK^rasE$HmkLxdmh^ZK0&M4kYH-IOn z3j=V&n=?6JkvzeN`2Q>g%)EXj(LSB`d!XRg3gD>-2Vd7PU7&U&Li?;`G> zc<*U{Nb&Ene?8?t0RTM<5FEe=e!QDj{{R8K1Tf|Q{?T9Wy;$8lSTlEa>^7``NyNYV z37&rpO#nIjKfwUt155z$gcbllol<!GiU&wLR^2)F7IT^<-*<` zmVE83W-W`ldOd49*ugsveE8*;|M2CPpMU;762Qlwe()FXfBf!x7I2+2RPwe}?UTTT zf!9KqU*=vkE&US1%g5cTwrK_lB?mgBA|w)`2^qqoNNW%gz1;7B+T)E z2!Q*TAR+eTCt?Z9a`s2Sm3LdXq zzM3!e`bozH?f}<-lNI|fU+vE)hJU%wopvWbh)x|^7k7VKJ{tep-DXn`I$z0KZe_qp zA#RHau!5~QraQnUpdtR^i4un?B!Z1JAwcRZY#WRikQZcdjAOn5QIBA;vXJJoHWhB9 z%bnHQ;Tp_QaQH_St{<-NHFop>kD*CZ^%DJ_wknag;*Dkhf|MfMPI2f;|B1!7z`m zU!|gD3hsdfDmVlFu{{j9;n#d14pE`&NaZ(o8Lc;e*c;C z$9V-XO#o8}U<&>L{-2w&e=!HVPF4Wpas7~V{jdV?C;*WDk;kQJ{ZA?YvH;-E3I2Ee z&sTSzSq)>S(y-g$0Fl!FFFdP%uLk&pIQn`7U{HiXocG)SK0pFM#3Oz^@DE%7n1JKm zXIKG(KmPQ^0hE7jFmgKOy$_$Y+x4<0aDfZgJI5?&J0~YsfAJr_{P^Q9KYtgqzfV5> z=;QbQVwWZxGsaG7%diF&c=#$?;Pf6hdlAMkf&C-?w@e_2HOj!^4+>fzEb~_i(T-Ax z)OE|D6%6)9+pObqu+4Q8%m^wLN`}hd1i1uu-wH!H1I!MHV!*&Gf#8YN==_cE^&7WN zN+n9ywkIw(0^RucU4DQ2@Op!7ewE|~7Re<8chdjrp4Q(w?scitX72}E+;Y(`QOoy^ z$v)51u8;entq`O1Ts1(WyjJ;^9S_RdaSmv`T!L`=g!(6!%Z+IWC0$a zGk^tYHg7Y_)^c7mv}&%~P1sdchN+>E!W1p)WspGHPNuRk3GeE$c;>=vNQ5Ig2pv}f zusTE zAg8OC6rdSU{e<^Z^x-8W!3ukhDoWRim^Mf%w1U^JLhTpzBdb*B>=^t7BeN+|N%(!l zL7X@Npm!)Hf@GI4ti>({qW&=6&!nhiPy-#>o396_7(0;}dRs)j2}4MF9h~REIt3U0 zcQ1tYH&sAhDKM@6y{|~zpId;w)&Ssn$^q~UfUh7EoEd;!A}s(8{VUM_dMCfv0i0R? z0sq5|^#A7qV9)*^cNhSt(f;pPo15|Kvlj?{zwq(^mVtQlzo%gTuia@kb~^2shaYZ&0;t&vyy)Uz_0u>ndfZC_Gk*MI zZ9V%JfAPuMr(fJh=kIWiM(q0>xcnIv1Ef83blfHGnM#G|dye6IB`7vVh`GT45uB^^ zJ26Jl(BEQ3fa{V%eGA_nx>aKP(^p9KZbzW*3s6#+{w(ZT)RW9-ymL#$fr}t-klu+~DZOUA6aX#_>-VZWv8K z*o*g$e|5W<_{#J}=3R`t12U9v;2J3jQK^{Y9!!Z63h2rfw99C07(>iqi#b~|4S}`8 zBvZhA$f96uB*eIMHrt9vqR8C1J%j9jr@g*jgFXO;K`}O+34<6MpwZ<=B3ClCPAUSa zGt^Ldj#rr6MPDp(?ZPE6{I6VCK%Ayl!Sz=cbejdi06jqs^MkduwZ? z>gjkU+p#sQG1SOiGiV2zDZ%WzJyEg^i^jR0&O-wj4Tu~l!2T<%=~ya|j->-ONOgv( zfj$I-&r$^RM+rn=$cH6L#My2{BpI+@3i2zmo;BhFjW1BjV>lOKiljpqKr584r9u}M zq*BO=B>0(d5Kh5G9#0bV|2(#W%Dn2o##~D#L%<$GJkpUW6~SQQL0h7N-~-KJNXSP~ z$b+hIjZDlj*}ym;C@GPg4|WA9v=U@Ah&bj}ebE5%2!%88lm(>XfgBEKA^I;n|BL@y z-2egk@Hl7r{?ne|YzxRYlt5=HfSL0G0PR1y$Ob_2fCP|iCPwp5&VD?38vxGbf8Hcu zecc-Z!v1$k0YdOk8vrr^c+tp$t@G^g1$=p49{d=LdqMntmoNa%697*C-52Nqo}U!} z5CKeg1>x?6|2YK^y8u1Y5EKCS@2}MhVE??_0}halTXwo$2K)}{`>4qtzTCU?hadl< z>BR4U^8ASvhc(BzZ^N_$XEk^_0)i8){XzG1NSnQ_fd!7O;f}500OC1NY7x;x_G=b+}5DgTVA~J;lcgFh)HyB7jxsD)ju7LfAo9&)uW9!p%NPPR+b0j zNhrj}@<%`4nJ*dnVlKg`6n>YON?2k7I|=YlZMte-U@TKmP;}Uw#hI2`#VjMIQ>gsy zk%I&fPs5le&ZbahV@hApC=~=>Li*cWC;lIMn+w+tHrr2N6S9yIbV>?CnLHOs-_C&2 zBVjLalr{Sh00CmVjHCa`!li`^t2#?Z1FKmuauQKB0Cyl%2GJ|A)pi|H?4{d}iIun> zx3d{mCu>CkpgIojmSdTDE>U#4UDt`46boe$r5IEMid#+w7uZ)TTP$Tp!+AA9Hzya83n1yJ1==VnqE z9HfZ3Pg3=Qg~mUqxV?Svog{9bRGUFlbQyvhi8)yvAO8%K|?FsaN z0JxU_A6$OiX1=`-=$!uRI}-rDb^s+$c#tZ9F#pbB00h6cmdrB&CFB1qBoPo75QP49 z5B^&}^j@a$AFlKH-{1Z9Gx+}=w4d(PU%q%|zkK-=6aeD;>uvp*(tSwlkJY{J?!q(+ zJAE+?eD3LlyxG6EF$8b%3IgE!Q`2Cq{XF>KetDp!ROMA_cx((V zA8yumzI?yaXm>t(@&Mev1Y-^>RrKq`!MlVQIqojL8bKyRS3saAExlvlOn>!iJosu* zC4o)Gc~nHm2Ftx;o;NTf1hR07ZTh7BhZGQAz>2ACk+Z*onIZ@30NBBZc8+7Y3ZuXs za0p1Tl;jl1>h&8i@lM};@Zk2HvhkOrkuhlf+!aCm{D!c)^KYCO>ozNDQaE3kxQ+*_zAMb;5o7Uus<4}>KS z(W!@ZGpm#!rcrEN&}jfRP8cMdtiCtb)a5QRU)yj0pYODdE=(a4oB#thJ5ApSeq+c%fs zJCrHgU<74bg_dU7c|++MC5vn3iIxg8i&9VPF-l$pgC3I;^xsTMf-!KsR7&NYB;}MS zZq%cFk{Bv6nB>dwH7_y14A~-7`LOynkUTJwz@!n%9ZW{qtQ~1f%+hIOK~#zQ~s(DRnUge7=~JWWWhH-6b)JkWJ0-5D5?Mq75`nH z+JBzefL$QizxRS0Hh_Em|GE4R2;ejNS@y-w(-$u{cj~LpUZT|ZTz_%UzY7cyod8Jy zAAPV(R`{a_0R8t90D*+&bH)Jp_?!alJ$uff82`g&qh0P4%A1EfY28*5Ix~RE-*G_h zKOOgrfm%HO$@^==d~bfS(;9Y`xbXl{vt?AVxZiZT`Oeb)yT`|WIUXQP5z|`$^Nw+5 zzj`$sPx=WkdNC>(!urx-V)+4+5;WHUKX`nCkGiDk29rRsYy)+7pJ0Fvh`)$Q{6sM+ zO3CGbc`XmB;2Mg|vdNWimlvDmJ9lp0-@Wso)7d|DdiHvI?Lo~P=O>flXhWf!Rr}^w z_XOjux1$(=btlcr&;8DK&4YdGyBML+#vk>Lex{fdbNw7YgcsqEQB01mScayXno?y< zk>yOMNGsgDkdFI-^<$;CKnXh8^v~HGN(4}HjC`J--8@9ZZwk7;x&tjRE&!;F8U@mn z5KKj&=&z+go{mOfixXRGP)vknFJeG;HF9mCL8ViENc_~;YAmj>K{d$<6qLh|@rE&C zgpjZN(e34OdFeKWv#qjjXA2s{6t;A2?LyKfX+^TzkligdcFk!Pv`4Dy}AFjGXoIZ zaO!*X|0^U3;6b(ko_T7g>Um>86w}x&7nt6zB zb5)4)hNE7;RIL&^peV4le5BFCfNjw4J6lzE&{z6n)3hpp2waR4hFj1Fn2DhSH$eOu zi{ZeC1KQ3oiQEwD#=!qgI&U0VdwV~Z#>?NPnI)At+jrCuaWP%BRG37 zeAn&T&A+^5jDGe9CKaj+NPztx-{C*Bf$2@QT*V)w1zN%>X&B!_k*R|VP6NR)Cz zBMvVy7tY@zh)r5CA#$DYa4Z(1>2y2^_hJ-BF!fM?0NS>|wdVEk!sV;A^)_Jq`r78D z!%G)l?me%iS&jn3UWO6JRWOQJ9iZ485eFAcGZD?x(g*D~;TslJbn2B99KG>(1T9#UC0GI`1;~s_ z{v<&A*UO`>- ze8KpusuyrU>C9{^=)EAmi(VJ-udWTpYw%n_{6CoiaQB}F{geZY7vlXhb^jnEz}H)V zUyA@p^q*M)Pd9+$j`_cr0KDcO6aYd6JU9P569KPJ>wjQ?XYe0+K>i;;00m$#{Ve<8 z%lJd^e}4l2AEx}cJn|3G{y&6m77_r|KxhX%AutgFAnHl%pYefl!#-d_9ze;k`|N}E zQmfOre)y2982$0!c$}?NU8BP_C$s>^OQAQgw+2WIU~2%}z=W$Pi-18l zLE7+&Wf`XF4u^0HK^0yB4zJ*3W?%%)0H{M3k-xDLTUqQbFXr9m54^nd9Zy!53z3*H z-;Ay6t-`1aeXr>2kGccixv}9t{>vkG`_^3wRmIVyJ2*XEG2|clX-VXQvkUWS(@bH= zz@Q^hO6WTj!*noi=xRJN4rk39vTd6#*EeOA5^Mom&1&U1OLI7h!BP@pww>kD9HjYy z)y)fGE*@BIV>#fHI@|%%;bIiyy>NtjOg4bQXKVIS{kCn;Ie$vm1lVFlf&W7ifX&H` za=I4aGE$J|0yzfKCmy;ynY|BZ#AH&teP;>K|AQqIBI$Umpk-NMemTxHH7cJsmX-Y7 zR!JS0Ny|~pL=$4mNggeX&u2zw_M*WRLwSvcAXkjA zIhG3XY)Iu5QzUi695O&O81_r6fxXS^K->ZW&dNcVTTyakwl4*7Q)q;{o4oID^-9KXn{);sI*YW?YlsR$*zFzn`V}|8fKsxslSL+|@|dMVNn?-%fbo8Dp^%{TK+ABd zj?*V2e>+>lYM<3Q3xhDkpM$R2a5x5#VK@%wm2vb#l(D8DrR~Z`sbkWFNS7{ zp!BJJk|*%rRzpzu%^FA+zK|EPg!Rn%bk7T%D2HBq@gu537zKViE?+-;oq(qv_R0(I zt>nk!f64^Je7>ixL)ampw2?UA< ziV*ZaC;$NJg_rw|<&SpsbQSn|)lgQ^FK4i7V2HjS5CANK(l~*U0LUgWxCCM%{-St4Mz8Q_~)C0A0y`1(18ZmNelFj`v z_2txPRGLF|@(xA$sXKO&vqDlBwrarmLNM$?S_#*n1JL7~PNn1$!k;hEq7|p=g)|qg z)ziOx^8wOMyaN~j)0uK1tFiIzX57iz^LcB9@?l*{b(C-%6HW|nr9>r#WXNtoMHB$YXevTHS%eU==#K^^fO-%W$o`pd zl0+}g@WfmYD?j9vNVtH0RV>aBiVxW~1`A;o;2}1bUG#c)K($5e8_D?tG4II5$$BQf z50SW6gRd9!&HU@ez5nFsPX6~9LEf<6|Fr{v?>rftIRQfi>;VANEZ|uIc^BCLaJC5e zl1Kmb>VJ>?iyIQa^adB;DgA%y2kxEzr1<|v{6Bm7ta0!HJUx*1o~_1rcR%|vjQP9_ z00|H||FOg8$wM6f_i=yz@X2%R_CQu3G9ZLMn1E;lkO;i^hdF>p7y@AM?C@a8jXQ>; z!+^8U7t$3=8=6@#_uLa(qcD}atrgtyWZu}m@%Z6`rfH@7TP3J_UFZQ}lKab_eD-K^ zXQ_MigTMHw%lp~6L%MQOB}TuDQRy?r0FM920g*ef1uw?3s~D#I0bX77*Szk%N)4lxiUKBGfO5{|ydS?T8X@3f-FzuFA3-}7r~wYv=3 z?CK)&$Ai1wV^#j1qtj4gI657@I{v|V)GB~9qj3vFpDn&QBwaetk=>s%G$ZBnRGdj4x!+0nb}s#e-V=5btrv9nHag% zGVuN>+bJqi0x2QKKno};=~$d$1-+$b9=-8qp&Va=9G5*EkvmL}RZC(^RKtYALpR85PwDVsC39?FAo#G2Rml%k+YC?!+KbsQA? zGA{`tsQiL}@Pqp}&je;!r4#X8>G-I*@SGp|9}En9h{~^If5Jzfh&f-Wwd+kf)d=?>gwZIA$R2RqPS=gy-)_=_)o_lwWJ{QPhK;qxc=?>BPH0E9k_?Ge8N zdKk{55*YMzF@1S3NYx%d)doU}rLUrf$tsLuc|tIu+3wN>MPovk?B7v~2_g zL9byXn#n?(m}V3PCf-?=$?CM;AglilFGvfp{XU%A+`9%7wHUHQB#g2ib3BakV4n!` z`?ZDIQaZwgAg|5T^HIG%w=f&PQV=LDrPKl_{uybmw(7ZirdbMGxk@4u0K@7TI)bSK zn|}1p<43nU<#ah)D4+z$68;Z1m}0I1a)VszaVVt>uG(`#Je^|<@Qhd{9*~d&1k^W- z8o}XMM{V>u z>W!{1tbgEiuJ?cN{%7z1 zIGq^ZK0ZO$50vkOX6zAULH(huyH$ZrXLEfE>_2;Tbraq>mcR)dssR)M#QeLa8)l{N zO#%%A3_w;!AtxxzdF+lhy$O#U(>-h9QU3=?#pGtna@ zSi>@Uu4O|LRI*Uagp?xK{sQWK+t6&}R}@?_WjQ=D0fYgtKf*kbz-l7I0cnF16Dre( zNDtOS5r4Sh>sOZ^6{$i3R2cMo19dedhma^xIiT6MqqR^_BpyF0Kb|h)_ozhno59@h zpQ`bLq@5sJff071B=a1mW!9O(s!5feN?@a*D^}k2}xW8CULxm3taC+z2%LcS& z@8SS|`W$n=4~PNKCm%e->5n4d`Exw*`Yz7@7o`3NNJxf#Ui|-}*Zkx7#~*|BFY*04 zIDG!FQ-|D@qOt$9>yw@Pf4E2M-?K4Uc{G`HyUXCycR}JA zo7&==kN*UxzjuAS`^H~<`B(RH?q`1i9G|T9?H@-Y@guN(`g9>@7Gs$Q=>&TEp>V0O zgXMRYp`fpa@DD_sgwsRouMD6CnQH1LU4S+-hvIE`@bav~T{vj{-lPH<+LHZ@jevI6}09f7Cm3@~^*9j0_!vrztH zG4h2NDu7F53CM+YSR@7D01%6JVj+NkF$nvk(Q-Js6!c%du)2!=KvhBWUx?>Jev%== zumz8`8!=S&^R@)6JO_VZItd>TnNMLWh@uJpU<3pr><`9&Q)S-#&bMzD3LV6MA*Ud< zDQc9(k2ScFrF;NF@G&--D;53ZR1ZesGCn&?)(eIuP;pa~3Wv^XAt8X)B9}`fOG%md z0bC422!zI8<^d>*Nyvj|0nz7*Q8foT5O}^tQ2ZsY#VE=I3J4KSFGRtxF$)s8e<+1; zRim94*&71l06vV9lzd91iWz*zi$9VMyz>?`C87mZlb&bzxeIS7_^nOg9&r%=Ci3R& zC;wghkZGi!hx>E}cn<%0-#P;X&o%&0HvwVnfAPxM8qf<@5c-4yr?lyuM+^`|LFwO z!J6DdDUvzV>o3+QQ7r%9_T=q%ZcauUqtmWS)&OXCHn#8Hy#EDC09>Dc{#T!W{KcQ# z?SAlnCs9(i%#*>1IG1_g5*+}mIf@T3xv2nd(;H>eEF==91L17~Za`Z)Ge!g3QUGPSeEiIxxSIl0nT0zd~j?J+= z+`!7;tpUt~+~dS(g-aR+vKDN^fB*O9+0pMT=ZA^WJQjZhz+iXsJw@U27BR*$)2tY3 zFu?eA*48;r)39R9q-Yf`UVsLvKp@_=D7^;LlujRh}+LsNj%*nDU;@pqX z3B!y3KtP4**J>*4++F-mr~-A!fZ}A^(R_TlwY+F+1sv<$u9i$0ny45_1#SGYLBxIe zuIT{&vlN<^nWWRCMTRyV#+Y-l#f+F+Xs^#EkO}Pwvgo}gRuV8 zQ;`hi3*5$8Di0YSn~-Qmf~uLCL))K|L#Y($(|JBBm3T1}jEG?{0?7(6Sp>YA6vI9c ze`kOI;s&iyiVVqN9^?m|A6`fye2GQ@JO2>UZ1CYFQqX%oA;cXBWj;mG2zT6OLSPt9 ze@I$_l!VWuyiFlkbL7Qku}Gr+pHBox0{He&@SiaUrr-XzBHw>z{XPAS9{f-4csgeR zk>2F>`tK{U#A^`X@3@;?T)DA5fB%cm{^WQ6_Vdrb1PJiO+oR^AFWkAQ}L zr)&DcVq@{F%h>#bic0##%MpPQB67FSDMUZVpY0#?GkSqeGW~6g7UXI7L{iHbUdz(Cpql& z*|wbJd8a!X93NNTdy76fJ{?Ua>3Fx8a6?cVC(JCZwbFK{Mr1lRr_$kg)-WjD;pliA zps0?*9Qt35p#_>+Vw12%dbqy6die7CPVF-GfCy==JD`y@;O?D@fY%H2uhqX(N)_5@`BX_|UlwsshltP;wqe~2AgGd1p zJ6pyz=uWYFoxB4kjPi??bv*C>7Q!mQ+G9)4J z&NqW2ql8EnAu%Y2&S-!B|Mae)*J%Qw{2ze~@S)d30K9u`0q!;b*b5qW}T_ z_W%F_{!J%<)A>IX0BHZG{l5qQd!0Yje{nl06vm zq0E1{h6Vr$05d=w>kpr=J$e4TeIIkc&Q5LhLjV9|L_l%`+5lqrPiz4}`~CEjCr=(U z;(1F+r-}1_e8Hw;FRilSUADra!10_kX^qfJTi~~;DD*<;I^U0wcVZ*hs&-;Og{kw4v>)g!>tNu0st7eko6*Z6XM{j zuEXXl{n5P1<(q+|;v^XG1fV|XA!YQP(fCz!CGYfs7~bm{e10Quxu>J8SKCGf!X#t4 zXPzFb{bRpqEOQpc##=UgxAUl<1&Y;zkye&&X(qxXO;G|efMM9Sk~IaiNU%i%-<{IVjt2%-lj20x_2_XoIC5fSfKF(a6hNmBvZ^F`9GU&FDU zWa}^%Ctro8*j@;};@|~^RpK8;Yy)0b@Luf?vH|&+R6HB;D6i? zfPC!*{n=Z*e!Tz);Xe&~5Bob)0l@)?IDr!v(5V*yO8@Bu&~pJK{2xUAlm|$Vzo`ny zyUx#lGy$IbC!SustiOBkf_>U}@o+C=7IvPsA>W177m07E0e*}i`Z)e~4xW7SVz*OY z_;8P`3@6+}QrX?yJ+uq}g0~aA#$5v~lN)k3ajX&;Q|X z|LSvqfOk&sIlbooC|TO-xlC^ihfYX-VGSfNj=SL&1bZV0x4!!7)qCB=#jXqeOUqF5dA4ex zG3OKr3|}E{0G@yl;q(Y8W;_|~cPZZ4&R52#uf{)PzWS+NmzPbZ|CfUh4D#)sfsls< zQktR*P}@Q5pN#{id$5qTfh>TS52hb}vyf2$0qS-q-NDBe2qjRjL!DzOicQo3zy$+n z-eb5$GCER5#i>F%gY5zMQMnGM%vCfA^*C4sWSJK5enn*Z99?9M6l=m~ zoA~_3xROY-;M9RjAkZpHMFVV!FZ}N9N59<3#Fo?cVr#iIZ*vs8j8!e&*0POaMeCXs z&M`ovQaHydX%6zcsgX0E2{9ZLeLA*MMUscU-Abp z!s&dWCCU{nLQMLvMAo7u;5n)!CvypJo2aU)G&Ycis4yLo$ZRem%S;Z8AgFnGKm!r9 z-$W%4E&>D)QJ^PF!5Jl`663HCus}$HAQ`#>-4CK0Wq>pzMjC!%CQdj!82P8BTL6$< z>f~ER{$XFal`Xj3$T9ub^Y%}KjQ!CDinZ~gUzQe zm)g&i_yHmX(w(R8J|yw)0RZR+9zrB|@NliM)3}g1ggW(XA(*!j_yeGRXC5I~s@rwS z3OxI52jUmgRq_T`iz5{X6?dq|kE>2$>B%2{vGU`;{Lam0?Gm_Zn+=Fumv5|W-@SA5 zjhjck{n2Q0w7uBf$dB%=JpO~n_y6jz{$%A(KK|mJ@@Ri0zm08uxk_c=S_4nQ+dp9{ z{n+xcnW=yg;JE~EW#hFp;zV&wg(Gu6EEsF7?LNf6g7}hj$_7v>E8lYm$VLf8AVdMI zA00qkyj49y_Te1-K&WSINcKS==2$^7j6Qb%pPV4`Y;Pq_n*bJFYjiqslALjhJt6M# zd%woNH+qHbCEJQSp>h_l%^S8A=ag2##zCE-QZ&dm2z5Wqcm>qTx{_9OFae>$trZHo zj#a2jnQVDMD6?n`C?Rbz={VGiI7=bw0fquvADh-|WHNX;kXfolmSG>BhIw$K7Atqa zYh8_~7gjq$TnI-vRMP=UiL?G(D}b+}wMc~LW%&4;B|b+9Oh8ftf^-cl0pYzx7araJ z@tc?i7QlCow_4~0B9H|O+VZ^1DfR#y?3`!}7{js>jN{}nSP>iqJg9;hhBYua)a*En z*KH2U3R)4eCPa-<&d^dJ+Mxlym%{3>Uqq*louHTOUOxokWg_HT5@c*~PR__V37ram zg1DTy7Dip-1A7;kr5uf_P#q^LY6_Wvm(^lUj$n1@T(Fc-5z0^?my$fNPq2KYE5!ws zml4P$SrBfiDUp7ASKO8P%?lbm_||{Re=OLA?gKnYQ=XJh!ScG zfQA(>0fhYjGRWuu_N)KDv{&>T2f*!|fplK|<9<&6!F}eO2YhA$`t=&%YaR>mDtSfm z59+U%1&H{^3;*-@pQ-?+2HI~SHPUhX{k=r4ZxyMO$R$M0;+`{=+WthhON^26IJKlt|Jn{RiIM*F>2 z`}_M_N89=N8+V&;Z@+!#R_~~LcV)vkoxpGR^r$;P2B5PBiuf_sYf>-+cQq-UiPtgD zH!2n%%S7}}inT1XriO!?arkWa3CT~WY}$o53UI^f6O%F23S7lPPlENmFdrC=jqSU6 zF;_Ly5Zl8`%-u>173hb6GhhY)U~=m?u`wPV&C3R;%-vCw50p+uTlia#woR-HdQ}>p zyt+53R8Banqv?99mjFZ7T(A*aVErx9tj&Q8fS+UpxB<87s{vH;ZjV2lL8qwk~aMj%n^AJ@uUysqP(Q*5!GV-*8l}b6q_!z9z4Fi)Oq}zcfbhFqu{3u{KU*#MCarH{&&zRP%MBA%wB8*-HsIi z-w_;Z&149;RT;>9NdR_0iN8%G{J{(P96U$2hNY0d#U=Slzo=q7AooMbA;NVwBCw$p z!ZeU1z<)SJ@e(EhGaw74l9&gm$&v}CKZrqo8Rx$&z;ysq%}^2PA^;O0EE`Z!0;oU( zzz-%g-%xmMCROvM`$Qk0%0Y z{Zj$ZCC>ur>mp#?vjBg+0Kl97!2#f}_i9g{?liE3r``HQ^3V*B91+7EB6tZZ*LkD5oLBX@Lk zuli~-x_ftHzPWN|{x9!MM#rabX(-X(nlz2mBdF~Iilc3f2V2KE%<0T=_v%H^TChSA zWP_w{wH8`flvqW8rqBe`UVe$~W++pjV?zAfXAG^e*HZvd>{rIeP#7KeC(ht#S?V^6le^s`DKzXKyT>ax?l=`>fmg^V z#}m*DDX~z`2z3V;Y2YZ?PFl!z9@MjX28>%SD};cg2`tOOypV+fFedg=3`CzW>hiP# zAdLfRU?bFwJjYoyWyP~XP|B3Cw&%k7>S}GL5mwb$8KcE;D(f17K%o^>!7Hd;?v#V! zYwL|h8Z3K}&gF7K2Fup{;V?~bf1U=2VCPiq8EHVB7Y;@+gpjcQqtjU|7t--Nzx1HK zlr1l7f{hd1X&E+$A)jV$VYlLV;8>WcS_20x3YX^cIdB_M@f$9fz&&JCQ-Rqh&!to8WnvG7i8zUK3`uh4dL;jC3#ug%7u~%v0wDlGNc9Sm4$E! zNx+xFtS^VqmM|4S9P*XfP-#|$#t@OtOFj|D|11W5GUJ;oNwZvlPnC$9Z%Up~dEyo1 z115q%g)LL65e;Uc2%Z-5GA0S?tU9OiJ{docM#;e&B6g#*h&%*)C z`SjY855WEV4YJc8wZJZV0-V(4P5cs^|GT?n9}v#}Kl${7Czu8z0X#wYca}O>%mo0s zZ#!hCXExu1^fnJ{a%VYfZz=j6cj@_GeEPeez42Xd{PLrlZ{KvHMXak^c)5G?#y*bz zNq1v=(mXxx9&HT1OBqe}C-d9QN%wSre=-`~%^x4H+*+AO9RLEJIWjA5?-ki%h!XmQc9;;V$1W_ydy^4XefP|Hr#~5;9%I};bo1KA z(ct(m-2`?N46I3S>y;4<$8}xLD$pN7+*3%~g~r33RlorGI5Jfs5G`|{>SXk+P3vh0 zb}vVD%@HU&zz8K!ZnHX?Y6pKR4I2*~O1;cNs)qI;unq)v6B^$Pnte@A2joJOiPz9` z`@)P!gYy?jU0Pk$YZ)CYJ&E{-3a8swd=i`kB2W*4dDNgF(!)RBp+l@c$r5t_R@Muh zOu61!dgsS)WA$nl7iK1!1H}TOolz=V4kXpM8{8maLRdj)TopwbAl@0RCmWamf^|to zo@86Q2K}XN!IHhe2&^Pg0gO0kUoj8s9*~e`=l$|lLY7JiU$!Xnm4xW8CX%xuBKU!X zKa=rOHgb~`Vp9w_2*_F~{6ng|U#x~u2!wFUff#@hVHhn1AValCMd`{lA__=KDWDB0 z$bq7>2=mk|B*1d0fx{Bp!3fcZ(1VJzQV2AfNGM1bePl8j@?!U*iX5TBP4qPzCnU*J zL=Lv1-x%t7kALkbC*#C_9^c3>|5C_%zwWj3U+}E(_h3Q1o+$uv$9od-X8^#|1eh=Y zyzM{N$s%xY0I!g^ClkQw6956y_v7w8T_7qTul>j4YykLofBxqOn}7E1i@hIfKierk zdzJ=Bwgy9ho!tVCc+Uld@Od9RxL>Qk)L|=q_+jnv!@V7H_#^y3`RE-80`Z2#|L((X zOVg^AxaIbYbSHmY)ypUW(DSdy8hO(&lBS)bE&vD)P5y}|f2x5X+e<5S1+scb#1SD>O*z`I>Ew0h^kYC4VRh%1mp znPA!5@ZW3@Tb^2EA;Y3qf&PcFbg*X%G+Gx>@3FBX3{Gz#9*==D1GKNUy0ZgEKSBfs z*cVTyvFOvs*7dS1@N$kREJUOW>zkY9Kr~IK?KEO4N}<4)OJXh;V&FH70wj=0=8^<^ z3=46e7{B6=DOQo!(`+HzS$gosFD#{@3bf;zp=)tN(?A8L(aRlM)2O#b9I#2md)G)f z3_#W@<0^_%%Hi(|>qXA3C`MkhGjW0fLGZ-MT!4{j$TK7ueS~5mJ$kJxAD4KjeEPmD zUmWI2LA6(eJ`nDS0Pw=uPKe>b9g5ImlxzorgbBqz_<%g0BFzBYeTqEf!T5kgh138c z0T20^l1yrGiV6iws!zt~j`#_LRN@k#!f(h&TnO`aG|2NRGaF#DBB>_vVB_|M`!DfEuOXf6)-c@3DZ+E^iwM9%n=V za*_EzdBw}Q2*6tc>@k3^l4O7Z0099I{g=-sfQ0+Ientd5-vEaDQ~?YC5IX=58f)(! zK3m(_d|58<9b|V7_OfLh`p^ME2!!e%%Acj{*X!(I>=I!6!_9*=bpM1T^yw3f1z(T^ z@B|5Asq2E9s~eg-uhrspGu>K_gCMRst-U23(?4zJ!P3JoHYSrFx^?SM{`PPG>diOa zXy)_6TqKj&{)I<~@$KgJWO8)sZclD?;k2EfZ1?VM-?-aDW^nf3GADPN(uajmUx@<{baOmJFIpcLRQwKz0$eFE^oYPHYx?hn6^Z6(hFcv%mVqH4+9b;`Ob!nQRRJA!M4s339F-W|O|aA4fTUAc}plloOFT~PB6vd} z0H%;3{0ROz6$*be@~9?~ctOa^(O@Xx&k`E&bi+;&flcQ8m`d`VFSs;|pGiJa9T23= z=lO$rF^y>c+6L&`lRtdMi`nxPqW}xYRzegJ|L%m)e;)Ja1OVL6XopkWAJ61_B;?6; z_KdK{yXg#oOaP{3fVTz=Nr1$^xBrjq0Xnn)z!VT~rdvQg{Qn#QoC1Jk0Ql_XpFKS+ zJO>Tvpq?<>ZL?h8foXREtRJv|J|XKo?>}7HT|Z2lL_&B72GGtfz;q%CeDY|Gn0?{) z@Ikw?=z>c#tYzb4R04G&y;{Dl#p6JVwTDYfdc4<*XIpjcX0N$%WB=aczx}Jf`rRMj zUb*%C%}~Y;=SOdD^yW84qy3|!?Yl?Y+jp&_=4fTUJ2|?s(VOgV&l}C=Xf!@rX}Ui} z3V`16S4XF%Y#^K0s&EQ;4+-ORg0aE=sJk!DSXi`~j;^_gOQ!)ZuP0z9cxdKRB!GHe zi#uq`<64>$m3piWCoI*nkCP^u6-*=T32s{yEI-j-=uq0A zXwGJ^uoDhIjrd1wvX3Cm=`_rJK@-eUI#&QoHxePecp7d#ae=`v%G7+(Z~)vX{0xG@ z5KAHU>)DJH%J7LQ95q@Eiq@JC;=&7DplD}kaIBU>V3=Q!& z0Wt7w4CjUQxksoZecSv4sps1hfB#vY2%hnU|J@{TUjBPY;9UMgFn=#2ymkQaUpyNF zp1Xh$3Lsvm^#5yT^k2dNLil?|-~|3BV&F3h@H+0^7yt%9-*N!>vlpv>_VTa)?B%Y# z(>Bx3jPh;=W&k_oYYfHCXBRfjhAKz)N+-pvb-u&XTFMj-Ja&Kh= z8W#?(mcO>$#p#c0bmQi|mF8VUee=flt^EG2$^PhcWuv#f-Q72DbP?dA@&57gSNnq! z)y{78dc)Jxqw&$b-l&B7QiHF0pMyvHke^S!Te}zm}7)G zl)@}4rUh*2#BxEH1esaE%u$gvPDX2dyoEhu2y6xr_eo+f@T5Q(8swYZyBodA2y;MS z0i!J+4a+}(1sqC|qkG%o=DoKZEUp52hEblTpt8<-wLJ^1oxnjFlcsG7lQo-NsG;G? zJldR1!T0}VD#|3{F^Z~Z8KafAdCo@n4;;c2&;)ei7N(69n1G@6NT$A;2|zX!2uHyQ zh;bMg#MD|$%`rnJfxoF^237R+6wjM>fTA;01fbfbONXo3IADQbAd|4c_`(9 z3Yad`4kmH~@`X|%b05dWKQu?IR}~>)p><#kb1vcJg4Z^3eCb%^VJTJ)#oeu3Qk6@g zF~b9l<0Oi&(qaiJVmSeLu2{sGFAZ}j7AjH-!#mjeh&jZE4X<~K-|g6?^XWiYrt@k@c+8}$MsZ!sQekb_Y{%eSbg4Vm z)2C|>i4+)fv&E&gwadb0oY4>4yMJ`pe)bHEKokH=ZOjGmKmoARUIKaE&aMOLO94wabsm;g2=xcpWGWAZ8&r|1D;##?$2zD5dZu8 zM~~j@-ny|20ARAw1JCB@*65an$-w@`@p}`)o!rQObqW*Cn;V1Zg>3IfP$C>3|LVQU zPwaR1yG>(k#A#s*Q2n?nL@5CXRpfw9$@(K=JC}^`?Cb_`sfW&l8j|Kp((SlOYQQ`vy)B)qAvn`o-Z?9dL^>=^k zna@;^JR!G3{6Jc3n}T3r=9Uaa;Mt@T+T$%6Rxj@g-#$!L0MqYqM~3XN2w}Gf-D7MU7*g+ z3x!Ut{owJP#oI^$cD_Xg6kz)xK!IK5_KA&>HE<2$phR}5Cftqh35huvt@RkL?-{Iu zQDsznJ&QoEfCS0JfK^aZ840WEgsS0WCAbVWU`gGApNE8APS;ffIJzrA(a2{9JzudFgH^K4n7MxbY{06F^4!rz+%Tzh??2$&xJ zm%S;VH~sfGfKwN6QvY9{w*Th~06YfJ!QtT{TmW{Q-QAs+jfd?<`QV^lZrtD9!TG=T z96EqqY>jxb2kVKpmZ`Vf2Ol2pK0A2$War_Nc6qP;qz%y!2trGXT~NcaIAaEbfYHr6 z`f35Y{;dI7zgbw@dy;q3wdYTEns?uL1Ck%a_Pyq4e|z#C1qsY*I8$!!PcQ)7KSg8z z=9?QU-_>odbVqN^uNae~d+4|^9vB_H*EA<4=7E#R+vS`0-#y&S9{=UN@vB!yU+ueC zFZfgY`R*~3?~RKmeV*2=a+wZ;lCr+`0L~>CWD?&pSQDp#Wihsi@_sy4Si#m=NM@iJ z64EdtS1N*KQi1}d;sCy-uq^Sc2k>RJ3fh3VrH!=Fij{zO$>4Z@S!lxyplV41*zq^etWU4^bX@CL}``lj0~RTEo%Vt821^_c|N z*UAvHLWfOYJOJt^h|t6h8EuG*8VNX@#Rpq*j_3g+sRi8_%6u*)C&bdY>JKJoG8u(V zZizU6t5y)!MKVH7E|zHcf``NbM+b@De<0*54MT|G9Bl*8%ZXwnDS`IWPpQ}{ketn_ zZ~}|M_6S^nu&kmEi1JWDK^7PZ2B94;6_c3m0WRbvobhrBuEM1DCv!Ud)@$lTA2P?OA>dgTfDCZH z2N*XG0mSH!@c*Yuzzd!Qz?A-j1aNKv>^T5W9RR%n;O@)VpFKNx+Su9KiN6&3*`30{ ziydcY@xk8C4g&khyU&-_mRjwd*oRKNBiQB6!On+30UstYQJpnA|+sCxD0(5 zm>>V3&?I0#$bPqOG_hn8hyJY(DfV$}=eOQ7wti-!4XQnSeF7JuTz<>!X$($M{qXPr@&5@CDn8#W;6&4_3XXH! z`owzQ9F$l(3)>*<5n!r70*17Os<&c*CP>5rVOS9NldC@CVQIMdt`1F6n$6_#eUmF@f}*N4?)W`Ok@nvvX50 z0N=&yx%bD+nGGNj01yz+TLn9_!OhU|%}`A@Zl!?!lj} z|JlwOivDLWHi7MBm)fWR+Pe=R2P&@>fC1qAZ}03iE~RzN);rqT!`&U&>@5`zb{-VK z3WVB!2b972vgw%SmYH=7-IK00}I{3^+e?g0x}0r0jl*!t=(yGMDj_gn8BC#uIM0}{6|9!NmBGcGsU&poO# zY|3^Wl(AJrwuNf7WLCLE)qyWawxQwULtIc6Ln5r8dNs-H9}*$-zg`!NcBVh5Xx+(o zU=i@iq^otut;N0d&K}`SKUh0}fa=kYx39N>4XPO7>jI0lr1321d@(@>8py?gpmVgY zXE4;a6wzQ95e0}Rt5cp_2rPw>dxqc*5jS*!hc-VSP!|@`;Nvg(qSEEd3s-B6g@s16 zb~UL(JQ)x`8KT)V)P1@j$SfH2m=6jpChiuaZPP(9!SJbCEOmK3a}@@nN;b;Y1jsj^XI{FDj^PO_Ik1%f9C(AvWC+bu@q+#VTEREfCCHAjX#=(}!$ zDS-kx%86=aFw8-0Z6+%Cc_~-jq6elqwk%LA$#}&<2L?B!e7kosok9*cC#LLm@Y zO*a%^c75QUm5`qsROMiD&Y;8MNkU8%{T0yqiiFkAix3bdl76TfR2uO|hWaxQ0^tac zu$59c#Dmn63k6k#I50V}2nTXrMV;VhLmnAOn#H<+*-()u0lVN2kqw~u(;+qG9quw_ z2f=_ue1VB6Sd`=o{~6vV!eS7qpa==jYv9j|%h!qk@~714d++%_X%>!{KPUg6?E*eq z4|e5>{~QGbO@TZk`p;3ob045Cle+~b6Ip8;5 ztp3sAv-S>Xy$5^33v>eZYJ8`?hW$K^g95bt&mSIi78~`Y#-X{lNY%^Pwfj#xtB37| z-dHTR>mdBK>!=i#G}!wC0jlD~troQ9Zr*NLS=%yQA*+omI2rU#R&U&1zPHf(OZ@=4x6DQ7pBC=^#_wS96EPgHD9KW~U-@kRE*PHZC z$H)8o&a1Ki=+#ek7bl?LHg610PVXJvGY8$uKG_*Q09ZFB;vN(PXzuf!HSiTt1i;bw zA-Jg(oa=TPG8?Xfl0RXR3YVrF6STEH5&)2EONS@-p6Vj3J%vihA`G!7!ra(3A^xL(h7a@A^LWF=h2ajm`r0<>xL zn%!m~m2@;4It$z@xl*N)8=_I@V_1R(sPJHQAQs{rb9r9q*)juA6vMd#0kAhm zQH)qYq=O=a#865WBZ%rm4h2mp44_XerHEy(h+;yT%J~5dEQCi% zd=McHWD%bUNor~umv|vg11hl?0l{!;5HxiY$KxF6|0mF&FX_YYB>p1*Rw3Y@@_xQe z0!FBx3jpwfK=*r1z}Xg%Y4Lwv|9dh(Tvui>1MnQc&;Xn{0iy>XBY-LRhlD^xz-JJ^ znE>#Q&J{p^_U^NT!*zXU6Bs~wFH_rlSw|~SjZ@=dhqud-c^cS-~G~thJZ0OwR4=mP^Lu{cP{{!^N?N5Wsi1h2F_Y1y!SEJGwi@ zD(_x2T1{g;=8z|DGej0sEug{z@07+dVAW10J~2J#tUKIvRcT-b!z~ zcXYIM^xnjU*5+t5{{EZFQ7JT9`JTIvCdd1uCVqT;)O1DT2S!6gJy`(mR;zerymMPC zFJV;i6s)CppRYNf^k_Pa_$*69{41P^j$!QPlIcdfj8!2V%>elVff(5f$R9400l1J> zkPXbj+oNq_{&RZo1_-j{>mMF?^AM1t4-P3rlF-TKcnC`_9{xv(U z6M}Kp$|Z2+DolxnjFy7rjZL8HaH1)ed(vJF6&VXsI{fXE80{5wtFz-9x%2!4M!nYBuxFvThgJhB$*pwhz#fTd_jB0$jI1>e<$STq3K zj}uz7qS|UOY+-=LV1}0BiUEy-T`M7 z|df3PMlOb8TGz2_2z|!Utrkto{jC?O|XclNkvK^)3);NK> zIuoG*sKO`M2<5P3v6$>{B|=4%Bc&lEJ_!(i5b;G>BAsCV_ zUX%!B7YHG801*dc@*k9V1UT#k;11yV1YwKV+zi=B(yc3d_$UmDLr^<6@(@O zZ;=rG9rCX?WOyz8#S@;-FOm&jkN>8%|JfSAY4J})0OWxL;Pn7!;Gf6*ze;9+XW~Ea zjvJ1DZv(&|c>{pI`@6sXjTh~`>wi>#`7FB!s(fv8)o8615c}ma+JHuOce%Y&E-dYo zcUp8qZ)PcMjahowC@-%!47gurS)rUY>S=8I$#;&&rd`FU3m$+`URqk}_8`Wktu3ME z!e;jb=0C0#EpLI~f9uAiIl1u*KMtJl26OS!g;-{F_4bX){t7@rfd23R9rc>a+uNi4 zTk|UuZU0vH^ryfDK7~H$=wA2qUK7zba!>zq%O7t20p^H6;_fczp$!;y{Y>|0SVheb z#10HTV=zddcZCor-bNnVYd=I4u%?|HJIZQpHSKV)xCZKOoS?_gGs!@G&{;ESj%7Hp zGOpkxBa1@~Vm*xLACPiFLACzI(W~Q1b$-6Ptx=IUG|^<`5o}WT_I5E5DH8((4E>Y< zY4pjeT>)A^6TS#E6hLXW6NI((*I9oL#|J@qQIbE2}2{0RsbN-auKJ#K&S`>wg-t1p+*j{ zj49D~f%c6I8eT`PlkZM!0Eh|XQh2MmSgC~nv(rg*N(R^v1EvD(mqGbI=yvVpctMwK z$6$Foy6{G2r~=7N%32^EnGJkk&Fz<(0LU#|$0 zJpE6IxDAFp^C7ZJ{NJ1verx~lIR}6s0q65SPy6R51;E(~urm(8RQ^xYf0xhzcm%-d zBEYMj2gpRn+UTxLut)(TR56B)aK}^@e#9Yfd=|ZQK?~TS)vZ>E8PI}nv z(=NMRr<(1@`dkfz@>i$}v~E?ndV4(Czx7>rFt+=Jzx~~Jwr>RBXZJF$ukXMKaMIns zv9S#T;i$L0Jm0-{H2JBc?a_Sq2x;N18{5zT91Y$>gRnpP>Zm*KZhv~Y(AoZrk3MZ4 z?{7EV{(HUC700LiAbO4QR%MKN{lG=fnn+a8ie^!yH0tT{4#pZr-|1yz={WhHHmX@b z{6PCTb6E{Ev@xqUHf7Y_Q2b-gZ6Tlr0}}fu_!KlltQB|iNJaCUIe&LUpyAf@^56sT zA3u7yw)e6PO2n!iM^!-J1BPQj<;ha%II3wP>*4C);0je^A_s@4m#fgi=3GNo6G{b3 zgc%eBIVF!3|HfiFoX)a_?F)D2TZ*A1SnLMA8oPR-c37k0I!Js%R`5mA1Otc(2;Evt zh-Xv3lo6U?1hDk*t!HWU_gqR%rg+?D%oZT>KZ;bsFo_FB9&HI zB_pvwB=|5#R`_J?K)J<&#Aj$u&vY^IcSW_D824a$lSE zy%i}`Mas0QM$e+GJlZI29(P06ZX& zT)s0AFkWZp&TPQ1%#j`dO+O+3ko)X(3)lsCfqEXG#0Jb`0igPa0szmr4o`S{cy+W?ARK30Of{}%{JOb{`Rvn-LN`?#r(rZ z4?D|?ne+owvq1ALgXC`;<&M_sZV85qGJ4B|CZ|^&4@MC4P1uY&r&iuiXNbgo;+ zg9OddPPx%&ucB8$M}wNhfEvee1{LV+$ZGK_)3@Nwy#RWPvDJ#Uvkt^QIQK~DkC!b( z{(vMDm>CJ0W4%2Y<$KHX&3mq7xOEt)@1nH05AVbF!<`1{gnG87xVW2~!rJK!xDaU& z4lu39+HP|wmbNOx9QZn@`E$!04NWtH@K1nPBT@--IpPcV{^Sxs1FaF(+?$QmfKj;6 zmCNB+0L?#^0?dVly3I(TnBaMX#uhHE*V#2?w0vC4YgtZnY>)yq6MrA< z=E_WpehZ36>;WuQCDm21%qhVnM8r-+vvE>K7!mV3A`>W;ieLn(kP2a)Z(^vXbQW$& zp@_O(kA*?rry`^w=Ya@HrJ`B{C9ovTN$3Yi&koPv85#CR7zawTIhll+3WPxRg+L6z z#2!@&A_{+Z!v}IP15MMl*CICZw;e6FdNT;L3L1$!{GK zfjyXj8{6FR#F&$ZMQCGfXXnXMr@f@v3RFLagWw+n)a!AHF-2u4 zBaemSCypHE6vsv353XCEc#dINnTP&WC|U>4J-)KNthwMT4*a46hmp01PYzHvVY4Ru zQ-Fl_G}|25d0<~Q4}wvqE_f@xYT2}&HON+SSpR~9c~~7a zMX(mT%YYFW#}M+Ct>LXNY5H50ph!m^t|nw(e<*Q=$w&AKf%+3gI?-pq+@(SL36{wI zGBK2+O2rU4{*g9|Y=DU_Ecj*@1Qz;$C`GKpRG)|@0n@`oaHg10!M_$~(N+X8=#N|p zgj7Istm=ydJZnHC29>PYj6y@K`cOYyLz>_LE7C=>HE7O9mIe{X4JyN#nYo}h&4>LU zx`Gly`89I~G~(jd0sPt(K>8-_2ke7sQV7caZ+8s;3<`X$1DcY5@cYT<&hh_q51^T; z^#^&2N`T=1SEe4|b60@!lKP))0-6p0apWTpTtAC{WP@*603iY3zI%W*pdTV7?Ch;R z-4mYW@5Fa6trt4G<+bw59S4MfdVMbokY516ywr~C*VmpGw%}P`Hcp^nGx{CFZDq@A z2c6SCX8)L$xr5#q6@Z?efQkwq5aIeF`hUOzr~wA%g~p9r+c$2k+_}54v43xWqe(@> zn`wobt=(%v8nBIRoH$#Ju``-%cUzOY^S4$27EDGz`04LHzWali2Nu5P)BDX|`db(S ze)jP{eEI%o+mrE|_kZcr)zj0r%;TRq%`vA#`1-#>&0aONKFScZB_`0p_5dig=tL4K8!O4rK7BK1Z(w7P>;Kecd5 zZv}z^es7kd(%~8uKlpF9T4pg6q(lwM06LP=Gi;QnmwAb9(nTQx#v@OK&DLBEJ|iM; zWk}~>4j7i8Sk$11l>BTUM8_Wl!r=6|=<~)72t@lw zJ%tDW4S)hwzNhpJOi2d4glQ&Ra{A{1{B>N2j-9hQYs1o5yC%kF*8&q9A?#U)R)p3;QU6^ zcddxjAc`VjdnPJgQ;VgUVG#NqRmHzTz8C>vSoC9yAdEsqKa7Qah;;(|LmVi|5(a+0 zWJF3zVE7nCZTD9tAQ5;BEK0`p23CWd4YvP`P|Y5aRqf`7;OFR7B?=SeE~ z=f1$xRC0dDHAVcqhyS$~Ac=jy2mg7ui{}o29p6%^!>bqOF&_hu*5UA)IA|*f3JtS zkdAG?_14CBZET|rK*zt*j9q@Zu+wU)k;2_Bx`J-E*Bik%2Tb4X2^c8NTW>c5a#K@cw6yy8HWKe)K~e_g)`6!y!e8 zD^52j_N-ym#7MtuRt*LkxuNNr)$GF$-rbD@lRN1nKjgL?{EZ!5N0p1*m7q<;zW|{N zmf}`{{TW7i?a60<7)M&bJR`o`Z9M#OND8n<=g!?|(8A?7wjP5DUTMw8HCKa;E(Eo< zQ>CJC_RlVmF$j!&%FB6%#k8@Kgn2O>$rF7*fJ4><#2^Yo25kTsuT*~k+>bN#ER3%} z?rAM*R*u%Roi;2$vd{rFUuO3KwZY} z-cUFz#K0s-%X4&o-eFUg6#YRFgSJ>&=3)PLQi5-nJgKO;TooO&l;DHOVIm3LvRF*y zA~G$DNw9npHZ?aVP_qkJDl{7csV@}PBj~Ydyk$@<$o^7fMny=6Qj98L;zx-gRP^u* zy_oXLMZyq5H*wJmGAX6b!7e;1tE9*dCW(;nqA!IGKLt(>A~{9&0w)Q{Cmi+0fu(5F zcbSX>2)pp>c>OlA7cBmZP+yQt1&aUN`*)Vme_m68{8JI&x$uWPdoSlUAOr&>_vvGf z+&usgBS7+uJD`6&2na}ue*ys@^v^{A>x2b#Rso<1_&WYyKoZk__TlyQLShv>zp^9j zZE)tAZPV@YPMa)<04%t-QwSU`jtjlQQu^@uE_i)uy*xA#F_j11crTxyz&y5q)BhOD zGRh8Ij!UaY;4@P@m(v;?F}{b>{+aHqjkHv z(Y*ckohFQ@5dE;|8%^e$&AZz-wtJKAy)LGM?~LASf*<_pn{VGH@sAAf5C4EZ;Fs_I z!e<|K-ux5ySAHhsi;a}gNq=NnOvx|+NQ-^P5)%%J(}Xs#IF}6Zwk#`75`Y6z+vsHA z5qt>$U(0h2`0ag$MYYRVO0JM9wBm3#`s9=M|KZEeKd?#1V5b)!-#+-|kDfnz=c8YK zT!^QJPJ*b2F--@V&{)j3T%v;`tNs+5M{uSUfrY47$m#{t;3zhMf*Hj~ztSI)H#G6f zOc)hh3GBiY?7W*rl$iz~Ivvs-mDd6(hf)@v6zoNg^2K5R04OG2rmzxh7CS-%ek}hg z+@`F=z|zE0l>_}RoX#-dHG>KiU}@kAPSVzjBFBax4+Nr5JG@kW81`30no8tdHBq99 z*}THDoJ3a`N@tSz6|6#KBdI97M>0{B3J4sXD&1WS(hRnyLhE(P3`QeU7G2P!9d`h} zp{p?3Xk;ZFmVsfg4gYY{)-VDX-R;pd#aP`uGzFGmE(?~G3@&ND1>FI}ms2ZSTeO-} z72fbyayax*^#>~<9?HK2W_&yhM3QWV3aerU)_s&TQ)hXs5an2(B(NCP(J5bvR-p|h z^MX{0d`Fb%Xo^ZnD*Aiqc}f?9vlmqv1%R6JbbV?RSpiBzob~b?Ve+Cb2uW!3MR#5A?jqmDF=@;OXn@;15mvgOLB9B>=C$fBkE| z_5wUB{Bg(oDF*;IZw5F80X#bpT;2o#;qS=+ryL-Z{RICf4&dkF-^)`9;5v)|&P+i6 z+MEAPO~7`a{`o<97b?Kja%HEH*>N)*p!&5-ZMNRV@t?QNEG(+@%|ljOY`fJ~A@l6X z!?KOzEN!OKme~cs3pVbnUMKI^J*aUp=_BMj)bQ}xM>nvwP@5k$$7a=ahS{#U(0S`E zNPzAl1>E|<8@GOqO){AXv9tL8Ut8HO*v*ZNZ3Vbp@7O)+HkWU0Z~Va9&E~yr5CcY& zlVkXV^p9`-!ROv?ppTISKL0G@yX|3evCZ? z?=Q7=#*ABF>)ms!Fftj7*0XDb2Eg9GY?DQ-TM}l4GWdA*>fG00IL2 z^9le@`-^L8|3fH19tg1REdj%I#sEa{69(Y!v%Q07FFyRU59lQPsv1%>L1!D1EyU|p* z`R$dv3R;NMo}_>O$|g{a1fW>iLAH@GNB|1Wlo%xedLhui z97F81(TZd0rVP4hJy<)!3XEAyrO}YZ*f@~AE7%2sWx>Gy*^C~>ri?(NOp}8g00zgi znLr2)M?kPrDZwx5dMd?6XcjVDQDnV+LkyRU26!RP=ei17V1bfNp{R!g22Eoa7ZMek zUI>6CNLK+&a4HT>m14P^B8E^ta4A}Q%qT*L!vT-APNy(=8}t-s+~Y{nMlVDqG|e*d z+f4>wBg3Mq0%y>k*TTg6+)*%rQ0A9WS@X1S;lV&XKDalJ1`p*ICSHr~WGc_BKi6a2v0sw4BLLkF` z56zFLo-qHSrvm^KIBn9;TL2ZH!q*0XC8Uz#YiWRYss4Y_DEze!&|Cj==0uDKwC;$=o(`Sf)yx<1{;DLYBGJq5TxGs2oKc3G-03Uh?z}Ft&fA`n#{syZ1 zofq(y1^piufHynqFRl9hk9G>Nc(x4~w_de3ZJhtT-jYzxFW+8z{_ZEwA27A+4X1_O zI<0uChxjLpy9E&9s|Y6-1AxJ(n?($oK#Mc$WP{=Mf^UWtuo<7BT zk}tma-A`XUeNt`|?1W-gsjy%*O)S;b9L)sSU7*QUVh*i;I!-knEZz%H;AtvF%V8HRT$YGMI00uE~Diss7T)!^^`ppB5dNkDgdjg3X6HqXW0-l9$~W_j5CP`4+U_$$FhbBmc2iUA{me>QsNhF9QF#`mE#3C^WlOmuBc(}mVf}haW5+E`!{8#*c{r}qd zk37C@2Z%tQCIa7C{GYe~vu7gUua|+N`ajbE5e3kc0pLl1u9E0SCE(frdqyCycYvN* z0N`q;$yUF^KRR4Le7SS5*?kO2@b$xT$4oc!#zB0qp+g#20BJy$zyB~(b~|No;uoJl zM)TxuM^EcEYVGo<+0l&h`1r(d(6bXS{VvA+%E{P?YbRp^D10UZ$!m*WkEIQ>I||f( z{nm|JQ1~MH!T;@sucn3YrM7iBu&{pT*S0tAE<-!e?HRNXuGOM3R`=FlEZ~aim#@|q z9k#OE`V@-+arTq&|BXXxA%jhrt9C4mPiC+!|1#9d8j5)gqXy)7AN8vsCM9H+$l0sV zr4|bA4s6ZhoQo`BfKO7+n@ANy0|fwVKrkPV)u7))x>$OSHAi25{2QPB*t_pP*Bwqk zx6vHUKl$X{cfUXe`0c-c3IpVJA)7XAI%^9`c7(`>c*f3V$sz)HX~O(i;P9i?9c|tO zJso~rK+i43sFq4fl_or6lMxwm`X^|^*@6+C2t~2U5NlCN6|A0COiVOij0pv>$K&$l znogOtj84Q3Dn^zDsILboN)a*Y(}i#>3jk0MGchWX66$LyKFdZZI2}Q3K!u4Zu!tKh zHfV(=qCXBvu4I*iY&e(!tyT~Wo;T8rj&~eNfJ%uGDN{@_RuQCoKET5ixK7p03tB!B$pgwWt@-8ra+CIj zgoW#KZbB--j+c%|QT#O{p>#k{SrqvymGp&yJ5r$t070ch&V4W^WRxN3VxlQ#2)Q$6 z17e8clQfuslnUy8Qi7l`$y-d6N8w)*Q-EM2CzFuF& zgn#V=B!Gw6%{8r@U$3oqZWrQ%@o~k#o}9^7$569(o3CDt2i;!I*g^wt)FBdpUj-X$ zuNrQC6u#R0sg+yk|8e-Q+_+)RF2KJgT)v}YzP)$zyEg7N7cETx6M=;bwX`0yGZA>) z;bhk}r|*V?myxAj93KA0m+$}B$6tQ@i|@WXBr%W5xRwcAT@bJdD|2;jJ%jm`*+nsb zOvL-gM&%fyAciwd%hDCoL@%+l_T>JOppD@1#~^4P@sH(@oQP63cN$~*|t9TSKnJx&HAah)fO43mp zX@yPlf)D&)4tiyo1va2Gw_O9I3?m`-T+<9#GJ>7>{-~*oUQ~K}0de*d z-5@?%#6V%XDipQBdDu&yHNd|z3@rYOnc-aU=Rc?YkfvgKo8|%Bz2+acuXO+yam}6Y z0zbC`^<;nue_SN`ai2B-(;0x*27LJ11pG__@aIqWu+k^}?B$2oU;fdv#_Haq2kErA zx7RK-UNk^wSzWcw4pvh)v_b<~wvHC>Jly@m2WeEjX_)@jFF)z^-B;r+;W$4+*c&={ zaj#BKUj60Bfp4~A3UwlOj~v#nuQujUjO&5V(cGR7R7ax-jBW%*=d?JMW5Dnx-L00i=9oQv`d42*UBCYF{db@J*v={(F*7tO z1Qt%YOo%MRs-+jg{#CTb#_Wwpkl}H{WT^FD>1EP;hz!E6I|O zE%&w(Rq$3ZjVqTo_Zp@P$E&TukQe~8f8+fxK6vqm1(OqqzwKgoe(~X_PyYht|7UO% zBU_Rlf)m})To>|wVk2ZA?m12Y?^oe$HcjNTr1=0NIyc@TfbcW4XjwfI zy?+kOeq1z53WbGCR$&)rIUP2C(O7_uNMTMcQXGo?DBQql9s7~!l0eG<9M}w(4Th)~ zMv^G@vjG)HJR9X$fldXYv{;0^NX76&j0$8JN5O!Xvz+aXk&A^5yg6xtIKeyJW!kX- zqH4<+{@?A&eycYStB&G07#vsf_!~MJaz&oP8gGj0V!cM=Hka{TeutGJI#i1(i4xJ@ zOEMJ^G5rt13sk0L@C9+G&rqo_Eu=&^gF)pdCvqhKb};xYq3=i7t2|zF0RMs^p7W`Y z2QqAu2K_!nB_py-wh1C0p%}&o!2p2bUvX0sIk^Mm%m+dMGeKYi@>~h5Awc%woa7_g z!6XLn5*i0W!1MHf1P+)hBHlqSLbeD34?tBQA!h$cy!-!o0`R}~3;7q@f$936GY|le zGjEWyEugR0fqGAb{x^4y|B>^bECTUVKm-Xm+XZ|H&u8Mla|!?~z&`wT0q}I^X?q9s zwGaOYh5t_4Zr1yW2_uK$6FmJon$>kb29zfO+%J2u{FiQ-qKY4I^^UW`Q>C?M! zHwK>`?yb_;oDqD7dEpnBM<$a3v3KK}CaHl}*zJZC`7HgF2} zKg2Er;wT7{)LrQQ+UCVM-3d#DbujP#%Ujj1;R1-`_h~kW-++qDVxRtj**@q*boI~0#Sg9&_FZM z5|oG*5e;c>#Fu0k;E*|&*f&_+ zZAF<(%&65HX$n9W6O;dVQ%mN%h75Y8RY64rzhutop$sr-({Z6^vV19^_K~GKw5WxB z*Xvvodk&E~C7vZ-J(yRN5>fc`M@4J^^QC~?Md4P!VucY}5`U5A6FEsr$w?UkClm!R z3cwtI>;Oa@=PEPFO3}}hcpUf=hyc$N0&K zez7El2)Vy#MT0?7%S$C<7moIU)ce>!;BoPQ@rB5+e_GK?5@w9JN$?3dlL+EEkKk`# z@}?DALorS-MP8?ftmg=L;rK*uS{#WvTk?_|JL zE9hn5zsq#t`Z(UD(@XSe&CJ z4s`b3@vE)s)Av8_40`)FALoDfcbmVzc>OlJfJGX1z8I^4Xp_OEf$Num8@3GP&B8j{ z%40AA7#(E5El?4axZ~nHceEBPt~#LVE$4ZLtO~=TLOX3kh;J%XUfbDy*6x7I2w?zO zGMr>*1Ny<||L|N$gPUMf+^$LZ4Vmk|@CiJ{uq*lb+D@ZD>Rc=mt6C0+lMZ}&#C94u zA2I^#XbKOIztkV+hHv+BTSGPH#KlUkA}9(>vlLGWP@)YJ=eNQkX(;d=BS;}sL9|)+ z^CY@p_K&gd`sMY_dMhtR)*eg2g}s`RHxgY#kcyQ!&UjiCDVk+y7y`gifJ*R`NHZM1 z1-~&_rUG<19tp!WfXzq(VFv^P7UqsJ{>CHda7{*r1egYy5SHgW9Mp4k!iff43VQiO z4yUdNnqsKPMgzK|$h3)GDB<*m)}X1GAON|9GwmpDw>#g<(=dS(4K4?26o4TsL43iq zu8q2Zvzkr8Pv?B$n{Vd@+vISPrgc^njbRC!-AzGE1(PK%C4tF{${y~1a0CqT3I%c` zPG&(2A^fY7luH3A;dylYXxL?*`1@dQ7~no4?F)u-sgRE(h6H5AR3hjLh^Q%;Fq}UU z7y**5fu-?BB7pnst+KvMDf&rN_y*qocK-au?hD*^Dplc+R$r{$ z+550<@1_gwtlpZh#W4fG8H@IH&)BTHU29J(+{v%HY5l>2&f1eF`h#VDy%ryzO#Twn zz}jJ_33s1YuVBDgPGuncipSPtm+Qdi0_(MMv#*SW`o>YOyT5W|vqC^$#g>bOI}=EM z@2U&<_1#|gsJU_%3gD5Ej&@i^1QQR-ID5zN^I7`j;Kf?&WYX;3{POdUMj`r^5EC#6 zkJT>MXab3|%h(A7l^T3|)mTOd2UhjbaTN*x&%D=#K+$!!P^CkYQZKafoat%^e?`EE z&bCoMEZ7$3X!YF}2WuV8CGH`^%9xL8FxXlDi`~q68O$cI1u-ZDIVteq(WfW?J~-HE z#M6+*xF9)}NcGP+x}e}@F*YcYEzQylF(q(hI{!#i-ki^ti$OCU!AOmKktpMZM68$=Jud`MRUR5p#ZfOF6M$@8|ZXS|> zQ63PEsbPj`YRIh`=wYq}?xl&zKs66lFk_A!o}Tfw7oj4yC{1CazEF%-1(OQPVX*!n z@h>I)nD$pVbOJE{1}Y$65D+Z}D47rY6BYFHrD72u#f71s7)mCI{)dO~4~xV2WHgx| z@mWGT;4u+(R44@D1_JjGobn}z2Lq@Gk~2OojOfNX z0Q3w9JuDtoJ*X~J^iKG2YI?SZLO~+)3nSdcnb{#$51se^01&;mN$=VJ@9zWl`u=l2 z0PiuqdB}h#0(iX*_{;}*?#u>|9RITz0L;xn|L56&kpket+0CQ>5CH&g=N@3w3E+7H z@N#G8>5KT&gL2)vbhx?HjBh@BXg0tHfGObm%l!Rv9Mx^PQLo#3@m}02w3f@Z*;=i) z9yFF7*w*7ZrH@`6gVwPbudR0Pj^Q)uA7GrkeH8|Za*k*%;di8MGwOl!V6RiPz zx5)^3Is$NTJ@$B|3+=w4v_?SAs%CFI!XQDZt=pHgEc}CeO#z!|Ph7X#N)(mCCr`d; z&VTW@fBWgBz(y9;uAYei-Ov@`GFPp36GTd?F$04Ed(1Rb7t$^>91oB{APOX&Kvl=U zd?DY;YlXP2airR&6^B?ABr-Y@>KSVzq3*nvx7@b9sZ&etDVPJ}!2^38o-4axO zI;l1oD^VIkK1VZbNo4JcT#Vr3eAQ4n%p^D}VI)cfcSmMpDn%&@RuI}QSOtJ18P`zV zV+zpgPV&oKG2x8z?)I|f%xlY5)>QHysmerB*Ay`aC;)EC7?4bRxExliqDUa10}=lo1>ox~fI9~VM*Ug4e$ZB4)IL0XK5BI+}e51T$%v+s~Tj&hu+j`x6nAI1{wYZtnJL!RQtn~)baCWm+UvA~Qr*0qiR|DX^ zWx-BcX?v7UT#2ohyT|UR%G`LbU%C*>Q^MqEwjNvf)99GBM~g$Ija4A1~d0=jKNCYBPSN4)`Bf=eiDxK)i{y;FST5hTk1v z_V=nkG(*>*lC$BM3#r1^fN%n;sCGw~GA_q##~HKagf(4=g)D|QvuTKSl^tvgFE~|b zf9SY2?kD|W9X@{LdMvxxbx`_u$KB@Ll^+bmZ@v+ZRt>{YK=VQIF<+%=h0A0pXoTVS z!3ws`*-n0361jMRst%Jm#T;l$f8H)~9^^szWrC>(1V7Fu zjI7DAx%|XPLMXL2cV)H16qR&!nPIv%9nb|TjOrdvrAc(TY6QuOq60`93@QKy3Zn{g z0h2@*P+}0i@an^vNfn~WB~B=+=r&AJc~mfn5TSpI2uL2)A!3~y=8{@o1tI_qK`vK8 z=@J3lNSh)71K}A=Wzu=4#Ia@kP6-}c0FUhFhiSu`uPx7PyK;5905WR@$RFMw@4Mbgloxie_%+00L zVYTRI9e9diB4}sHNvi?*I4N4)`_qZ;AnYV+iO80M2wkqzU-O zD)2c^12pGZ0AUCS9RO+lad{hn!2l-L)Cpw$k30qt`hPF}i3)Hh4in#Z4^RfApDi4| zyt%aZAq+nb)(_ieSJ>Q3J7vhYVFUnx%UB*50XJtx%L!OaTTLjM?oo z3|Us8(Am8m^0PjCuT0DkDpD-7`6Qk0+-@A+|0oV>qif{nH#Y8s7v~w(F!Q!fc6PI{ z5oFU@nzDr~@jak9fD#30{;FFj%(De$n2<}RnQvv4?(u#%>Eji&7-YLf0=Rpr(%-_| zlus%N7fb{$LdA@p2?nF4i8*t0mkXp9>JJy%dOC8Y0mCm-VXy`?z)}S84{Ifv5y1ik zWG>LWKqrPE6jks`MM~sgAHoKx1p149f^cYxLLBn`LP`ZDVO2_eCC4L4$d*5aYKlb* zBr<3^irGk*@P3&RG7PlD$S-2R%t5=H4&z+5@&+A+#H#DUuE9Y%)t1@bw(H2w1k-(J z|17KNn0QC`y33B~_Gkzsz?7zX`GITpe9;!dN{9yAk3`OG!RK3uQxvK!f3V!D_=-@^ zQ<&vpNGeM)K7ryM{~m^~LfbF-a-aqHaNH9XV3IKZ@B%7ult72ckO zgJ)=DcdmaZ96Z@P-1}s&v2%&tD`y)IGR9!Zgs(+8o6lMr{C*nq^P0)k3+q%nlVLOQ z^>xrb>v^DDX$ux000)|*F_%BxZ+4IA>)qpc&s}Ufc3SI#7pUtCWgaHB7uT~JN29I^ z!&-#mAbe%D`3fYRY!~|`x1blIYXGR~F*}R!?`ebHFdC^jwbqab{0GM_Hru0b9V?i# z0RiJI#E4~ZX2OEo7h|uknidQ{5u7p{4Tq6{9&4nt<)z#ApMCNto$)Kz>CTVl=eM^V zS~dvWA2;$i>k;zltPQ!30Z1K;AVU8IET{-ZQe+)6;A24U)oj~6ZvN66eigib{5?+q z03?VP7SxCpJ|bW~FfLI***FCQ_YzowO35rNT-mD~u5a!fUL~B43z@n?S5$B#s0_<; zT#A8ImO)7mi2?d{h>lrr3;CqUKq<}ml=7tzDT1Ki4FJWCVlG!k?rf9H- ztxyC8qEHjqnoa5Y0ca(@T?rj9UMcAJbD|BLa3XgnQJ~ZmZ}vbw$ii%D*h@( zjY;_z^qz|#MyMtvl|gLhQocY*3RH8D`X*_)s6nBsM^>6Co15SyTeWX_Y>USH4KpfV_&I6Kg=pTO~{qh7|nPps)C?U=RJP z3xZxR0rS2(^Z%X-f=C89?*fPk2(Gy^A3%=)G<#+4^&$|&`c(u!?o$Ua;sl1rnFj#w zQwpFL{wM(2FW!B(Z9Quo)?ZT3D(wTIwp35saglAr4_gJj;Ox|~9 z$Q4X>`Msl2?jlgXyLqhZi8Wu1PZR-U5h(4)C+vDGqhtG?4bi@kRp7yc{es=G`^o3K zPj2gACg_T`9LNt97}G_8A5+?d%0I>`ez;K~W{JPI3uAxHFt!X>*AWEpICrg9=J1>P z11I0>Zo$}|;SHGnJ1pxMkf~zcpBNaHkZx>d31wh_6e5C!8_AS6!`O=5x&8Rj(|14l zs5@VupB!N~@pe3sK!%Fj4(W3hJD=wijf)e~t7X|lFAV>G!v!X20<$J37$a0nOhF-} z`sF)83Qi?t4FM^_tHfY{qr!+?_c&1sVPF8*-Zpc@8c2_^we=bnu&!UacnfdR0M^r)GWs}(Aj%xR0XpJEuAg%mg^qMAo# zj}+kaV8EEq&M)h1o_w8lx`x&>F`HcGG;>~~4ICJ72hdDY_GLd3?06v<3E~t*ng!>Zu{1PDc5Y&Ebii!wC7@SjS(0V08`b+R6$T0&*hDqIz zuZloBBv^>>0OwH(V0q9SY6DC(fe$2AO9a2zn-?T73?!gE_)wuB6AV_7H+;cC$ydT0 zFbQ)3B&Ui$G81L}(GVO)L^y|pisa-E;tEBA(^Ub$CxR)00>Eb+z#-WtKmY>|4p7h9 z`fr@;=NSS0f%LDlgUQp^RG_H{@O%I`i`Q@Be^U$)54eBKdJO=AA9vLK9u3%o|2!Ko zU;yhL{}+n@&(;9`**8o8cL4wIws)SrJY0YI;mc?B_KWnhr8uN(RHH*3?$onu>H3bg zWR_dj=3>5YE_YxClrP6qY$w)$KWJ?oOxLifLk>84Wt!;!jfQc!emV2tt-xx_#_npp zU}Uucs9~}4VuY^8v8)#|bOg~6+JbI(G{&4>do}Jm*`W#VZa8@9;q@BMQ42`gqHFRB z4CqF8ZZ}=)MtYmBCw>$)ykYo$~W-^08W&x6klqPhn8J&j{rftMkcSzsPcX9Iy+ ze5p~_%A0HVKYHgWHlME*%6EFB?fm>QW0*9A5;kTCjE%s@L<vEZdZTIeE5fb;`5P~_Vk;#@ZoYocC3y6UXi;uWRaN`F2E~hBYVqpq^y`W`% z0cia?Ss1)~Wi=~scEGYKYJi5skJ3$Al)=nJGe8FZXyYN#p+wC7fCFHrKmoGDuOe8w zn4l9JzyOf5pq%5uOa^~A90mEG0$j;t;2a#Sr9sIFr80E6PQi;K%ykw*9A_xJ77lM_ z1e*&&Tp&b?IkNP~fT|LF-ud}fi)J_G;r3%J-_6Fmj?v{NdF*X>C})|m@<;}X+iY?m z?CmV2RK5^R-!6osSA+2u_QW!3kTYbn+EbxajvEq-wxty9CzGLzK{^T~9yyFI@%U6u z42w8y72X%(1QY;8kwNemi($mDT*Sjiti!8Z?{`bOFR^P{diCMBfiRVIOY5R@f&8f<7A7q-ZH{zLP%&Q!sBhCrQL4 z5JrNb>CUex5`ecj=%OkC2=E4b=RJPV=iUA1_5Z)11@=aNxOTXd{J;SdSIR_N8m*!unZWpI2E-GPc7 z&o7S$PIJ_?_=VNlPEEKoxMF758mzYS!;zA=(hJ!U6{njcm`AVbnfd(2=+gdQVql1Y zfu)UL)Cq3Q_|O| zk?PXCGY}|v=~c*qPD8Oq>lqr1{;alKC_j1l!ADPCV3Y832qE&Ba;po%3zg9!@MTp2 z9Ri9byAhyV$Fhj701UtareheZ0jdYk1)Fb9DYpyr`qEP4(HnxFslYv(*bMgv42IW{ z7)Oi%wnqTLITH&%SsPP&zE;EZtlUT~G|HC1b?%2zbD-I06@kZTjT@&}6p(*%TvOo4 z5I%6c6Hd`I)P*#Z$sGC#fhh?r=)1@XVgl`efdENTuq9;8H0?k#9FStQxeMXIh0H=a z9YvE&>zyUrABLoEp$-_$v?-%Fdm$8Qcc|JzB!XQeA*6I9DggM<`xJoE-hpBi%Kt8& z4737Gk|6RBK7i8zPdEp|Rn9ehvs473N!{FN7Sfu|rS#i^uCS&Xs?ZQ7q*9MT0j!{P z&qa9N7o!ywgL}V{3`RuAk?33zgg>7UOwkxc(tHS=KXC|_341U}DgsJOT%3XLXA;05 zs)Gv3f7Av+UIsUqqeJ16nj(Sahmt5HCDFYrPLNNe5TD>@qkhNE02L0QMKK)Y!fFW6 zVFd2qXdUJ}qOZpV3Q1>{eJTnRk;nt4vF`bN;jJIMz%w-<-jIDlXc4?Gr{DSZ^-q@t zcu{|DCGhn!V2=TS``I#3azE<;Jo^7Esr>-}dO{$a{sj6X*BSe7ef?|*i0Fq4@M{ww zOaL+P+IhO$c==3!cKAonUcT%s?L2!>--D+W>}K*>-6=!R60aXN%JEirtaXgE&g|7e z%ztxrA^wB&*{o7E@>ZR#yRC*>aF6@_EJMW`^YLL{@N_Vyi;*C!*s)CqSpR#(Ks%gS zP3Pw)?$z60y*mBs6?W@uSRs-~jDfEyjGY0s1Y>MgCQ*HKJI%gzy2}Yda<&~EMO4@zhvq4Y}|4PfKUyGYb^`{mZ4r@ z1dEQF2{Z^8J33hWZaQoj5p7y1-)+H$eJ?C`wKNhy&soAM(WRY(cb-3gxOCU# zB4tdrvK(g@>{hlGO*ix!;wfDY@D_3Y(vb}cd9y-X$PtqX*P6$L{3T@3DuEtVP0&MuQ@|F&&%+jR zJO~I`9+JWuC1le{Dx(>}NE{k(A~E)-Rttj2aS0(fcO?@EVhc$?55XYt0X0e_0d;be zou9wEtQ+}dr{~C%rmHPG90vb3*Xz>5?WW>b8rcJ2I&v@1_s91x(yyDJcd{l!&4rCFbA_oC@$EMkE9P4F7-) z00O|8FBLgJ%!Pfa9ENZ?34K8Ycw$LS7D-c%CPq%lMH$0E#C6Uu&cH7`SS76k4|0x7 zVQ`>cRK*C2bZi5R`jLE44 z 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≪0levAG|vN{_-sZ@Xa^v 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%0A1@aUIIDS!kPv&~BR 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

  • -rH_Ux@$r$~e>fdePR$Z7o)tHW-@&XHt;76Oryl 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 literal 0 HcmV?d00001 diff --git a/fle/env/factorio/scenarios/default_lab_scenario/script.dat b/fle/env/factorio/scenarios/default_lab_scenario/script.dat new file mode 100644 index 0000000000000000000000000000000000000000..6ed02ae17325594e0b2647a9296391e92b33cc71 GIT binary patch literal 1441 zcmb_c!EVz)5Vh0YHcCiSL6CYtqDWkQpf@hmH$G#vu_x}T>s@PiorLlO`~v&|7fxLG z9R7emfalmr8=6E#!cro8X6MbDH!~YYabzMqF@9Xrob>Ncy!q_xe8@(bBN-?cDp2Y9 zF&ijnO;TwXkDAyhx=0yYSetjBzpmJErF^jZ&TuwUfN^C~%0k{MvkrZovS-uU8FERf zj50-%JEiXhj(}q*ncL29VPx(p5_0<9OO3GDTvcX)sRyg0)3iLgie%-CX=$F}>=?5N%gwNZRRbm}jNEFK+SPzKFINdc zSp!E)9|T77Aq0S5>^k`uU{k*`!C3*4$h0Co-nLw#j$vV?MjD4JkH>d3M~yvM-LTeC F{0E) tuple[List[str], List[int], List[int]]: + """Get IP addresses of running Factorio containers in the local Docker setup.""" + # Get container IDs for factorio containers + containers = await self.docker.containers.list(filters={"name": ["/factorio_"]}) + container_ids = [ctr.id for ctr in containers] + print(container_ids) + + if not container_ids or container_ids[0] == "": + print("No running Factorio containers found") + return [] + + ips = [] + udp_ports = [] + tcp_ports = [] + for container in containers: + # Get container details in JSON format + container_info = await container.show() + # print(container_info) + + # Get host ports for UDP game port + ports = container_info["NetworkSettings"]["Ports"] + + # Find the UDP port mapping + for port, bindings in ports.items(): + if "/udp" in port and bindings: + udp_port = bindings[0]["HostPort"] + udp_ports.append(int(udp_port)) + + if "/tcp" in port and bindings: + tcp_port = bindings[0]["HostPort"] + tcp_ports.append(int(tcp_port)) + + # Append the IP address with the UDP port to the list + ips.append("127.0.0.1") + + # order by port number + udp_ports.sort(key=lambda x: int(x)) + tcp_ports.sort(key=lambda x: int(x)) + + return ips, udp_ports, tcp_ports + def parse_args(): p = argparse.ArgumentParser(description="Manage a local Factorio cluster") p.add_argument( "command", - choices=["start", "stop", "restart", "hot-reload-scenario"], + choices=["start", "stop", "restart", "hot-reload-scenario", "get-ips"], nargs="?", default="start", ) @@ -342,7 +385,9 @@ async def main(): if config.mode == Mode.SAVE_BASED.value: raise ValueError("Hot-reload is not supported in save-based mode") await mgr.hot_reload_scenario() - + elif args.command == "get-ips": + ips, udp_ports, tcp_ports = await mgr.get_local_container_ips() + print(ips, udp_ports, tcp_ports) await mgr.docker.close() From 778a4007f6e24d800f5bce062379327aca839c5d Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 30 Jul 2025 11:24:23 +0530 Subject: [PATCH 29/53] cleanup --- fle/cluster/docker-compose-linux.yml | 41 - fle/cluster/docker-compose.yml | 19 - fle/cluster/docker/Dockerfile | 20 - fle/cluster/docker/README.md | 22 - fle/cluster/docker/build.sh | 18 - ...build_alternative_installation_location.sh | 32 - fle/cluster/docker/config/config.ini | 753 ------------------ .../docker/config/docker-entrypoint.sh | 98 --- .../docker/config/docker-update-mods.sh | 28 - fle/cluster/docker/config/factorio.pem | 27 - .../docker/config/map-gen-settings.json | 78 -- fle/cluster/docker/config/map-settings.json | 120 --- fle/cluster/docker/config/rconpw | 1 - fle/cluster/docker/config/scenario.sh | 46 -- fle/cluster/docker/config/scenario2map.sh | 27 - .../docker/config/server-settings.json | 51 -- fle/cluster/docker/config/update-mods.sh | 96 --- fle/cluster/docker/mods/mod-list.json | 9 - fle/cluster/docker/probe.sh | 6 - fle/cluster/docker/run.sh | 23 - fle/cluster/docker/run_local.sh | 6 - fle/cluster/factorio_server/__init__.py | 0 fle/cluster/local/README.md | 140 ---- fle/cluster/local/__init__.py | 0 fle/cluster/local/assets/connect_button.png | Bin 2715 -> 0 bytes .../assets/connect_to_address_button.png | Bin 2933 -> 0 bytes fle/cluster/local/assets/ip_field.png | Bin 4468 -> 0 bytes .../local/assets/multiplayer_button.png | Bin 2701 -> 0 bytes fle/cluster/local/assets/quit_button.png | Bin 2709 -> 0 bytes .../local/create_docker_compose_config.py | 109 --- .../local/docker-compose-1-example.yml | 43 - fle/cluster/local/factorio_server_login.py | 195 ----- fle/cluster/local/run-envs.sh | 373 --------- .../default_lab_scenario/blueprint.zip | Bin 215969 -> 0 bytes .../default_lab_scenario/control.lua | 446 ----------- .../default_lab_scenario/control2.lua | 6 - .../default_lab_scenario/description.json | 5 - .../default_lab_scenario/freeplay.lua | 299 ------- .../scenarios/default_lab_scenario/info.json | 1 - .../locale/en/freeplay.cfg | 4 - .../locale/en/level-01-locale.cfg | 71 -- .../default_lab_scenario/paperclips.lua | 81 -- .../default_lab_scenario/planet1.png | Bin 505684 -> 0 bytes .../scenarios/default_lab_scenario/script.dat | Bin 1441 -> 0 bytes fle/cluster/scenarios/open_world/README.md | 1 - 45 files changed, 3295 deletions(-) delete mode 100644 fle/cluster/docker-compose-linux.yml delete mode 100644 fle/cluster/docker-compose.yml delete mode 100644 fle/cluster/docker/Dockerfile delete mode 100644 fle/cluster/docker/README.md delete mode 100644 fle/cluster/docker/build.sh delete mode 100644 fle/cluster/docker/build_alternative_installation_location.sh delete mode 100644 fle/cluster/docker/config/config.ini delete mode 100644 fle/cluster/docker/config/docker-entrypoint.sh delete mode 100644 fle/cluster/docker/config/docker-update-mods.sh delete mode 100644 fle/cluster/docker/config/factorio.pem delete mode 100644 fle/cluster/docker/config/map-gen-settings.json delete mode 100644 fle/cluster/docker/config/map-settings.json delete mode 100644 fle/cluster/docker/config/rconpw delete mode 100755 fle/cluster/docker/config/scenario.sh delete mode 100644 fle/cluster/docker/config/scenario2map.sh delete mode 100644 fle/cluster/docker/config/server-settings.json delete mode 100644 fle/cluster/docker/config/update-mods.sh delete mode 100644 fle/cluster/docker/mods/mod-list.json delete mode 100755 fle/cluster/docker/probe.sh delete mode 100755 fle/cluster/docker/run.sh delete mode 100644 fle/cluster/docker/run_local.sh delete mode 100644 fle/cluster/factorio_server/__init__.py delete mode 100644 fle/cluster/local/README.md delete mode 100644 fle/cluster/local/__init__.py delete mode 100644 fle/cluster/local/assets/connect_button.png delete mode 100644 fle/cluster/local/assets/connect_to_address_button.png delete mode 100644 fle/cluster/local/assets/ip_field.png delete mode 100644 fle/cluster/local/assets/multiplayer_button.png delete mode 100644 fle/cluster/local/assets/quit_button.png delete mode 100644 fle/cluster/local/create_docker_compose_config.py delete mode 100644 fle/cluster/local/docker-compose-1-example.yml delete mode 100644 fle/cluster/local/factorio_server_login.py delete mode 100755 fle/cluster/local/run-envs.sh delete mode 100644 fle/cluster/scenarios/default_lab_scenario/blueprint.zip delete mode 100644 fle/cluster/scenarios/default_lab_scenario/control.lua delete mode 100644 fle/cluster/scenarios/default_lab_scenario/control2.lua delete mode 100644 fle/cluster/scenarios/default_lab_scenario/description.json delete mode 100644 fle/cluster/scenarios/default_lab_scenario/freeplay.lua delete mode 100644 fle/cluster/scenarios/default_lab_scenario/info.json delete mode 100644 fle/cluster/scenarios/default_lab_scenario/locale/en/freeplay.cfg delete mode 100644 fle/cluster/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg delete mode 100644 fle/cluster/scenarios/default_lab_scenario/paperclips.lua delete mode 100644 fle/cluster/scenarios/default_lab_scenario/planet1.png delete mode 100644 fle/cluster/scenarios/default_lab_scenario/script.dat delete mode 100644 fle/cluster/scenarios/open_world/README.md diff --git a/fle/cluster/docker-compose-linux.yml b/fle/cluster/docker-compose-linux.yml deleted file mode 100644 index 607070994..000000000 --- a/fle/cluster/docker-compose-linux.yml +++ /dev/null @@ -1,41 +0,0 @@ -services: - factorio: - image: 'factorio:latest' - platform: linux/amd64 - environment: - - SAVES=/opt/factorio/saves - - CONFIG=/opt/factorio/config - - MODS=/opt/factorio/mods - - SCENARIOS=/opt/factorio/scenarios - - PORT=34197 - - RCON_PORT=27015 - volumes: - - type: bind - source: ./scenarios/default_lab_scenario - target: /opt/factorio/scenarios/default_lab_scenario - ports: - - '34198:34197/udp' - - '27015:27015/tcp' - restart: unless-stopped - user: factorio - #entrypoint: [ "bash", "/scenario.sh", "default_lab_scenario" ] - entrypoint: [] - command: > - /opt/factorio/bin/x64/factorio - --start-server-load-scenario default_lab_scenario - --port 34197 - --server-settings /opt/factorio/config/server-settings.json - --map-gen-settings /opt/factorio/config/map-gen-settings.json - --map-settings /opt/factorio/config/map-settings.json - --server-banlist /opt/factorio/config/server-banlist.json - --rcon-port 27015 - --rcon-password "factorio" - --server-whitelist /opt/factorio/config/server-whitelist.json - --use-server-whitelist - --server-adminlist /opt/factorio/config/server-adminlist.json - --mod-directory /opt/factorio/mods - deploy: - resources: - limits: - cpus: '1' - memory: 512m diff --git a/fle/cluster/docker-compose.yml b/fle/cluster/docker-compose.yml deleted file mode 100644 index 102731e12..000000000 --- a/fle/cluster/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -volumes: - scenarios: -services: - factorio_1: - image: 'factoriotools/factorio:1.1.110' - volumes: - - type: bind - source: ./scenarios/default_lab_scenario - target: /opt/factorio/scenarios/default_lab_scenario - ports: - - '34198:34197/udp' - - '27016:27015/tcp' - restart: always - entrypoint: ["sh", "/scenario.sh", "default_lab_scenario"] - deploy: - resources: - limits: - cpus: '1' - memory: 512m \ No newline at end of file diff --git a/fle/cluster/docker/Dockerfile b/fle/cluster/docker/Dockerfile deleted file mode 100644 index b06b0e558..000000000 --- a/fle/cluster/docker/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM --platform=linux/amd64 factoriotools/factorio:1.1.110 -#--platform=linux/amd64/v2 - -COPY config /opt/factorio/config -COPY config /factorio/config -COPY mods /opt/factorio/mods -COPY config/scenario.sh /opt/factorio/scenario.sh - - -# Open ports: -# - '34197:34197/udp' -# - '27015:27015/tcp' - -# Expose ports -EXPOSE 34197/udp -EXPOSE 27015/tcp - - -RUN chmod +x /opt/factorio/scenario.sh # Fix the chmod path -ENTRYPOINT ["/opt/factorio/scenario.sh"] \ No newline at end of file diff --git a/fle/cluster/docker/README.md b/fle/cluster/docker/README.md deleted file mode 100644 index e46437a95..000000000 --- a/fle/cluster/docker/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Build - -To build the Headless Factorio docker image and upload the mods to your local Factorio installation: -``` -sh factorio/run.sh -``` - -If you don't care about mods, you can simply run: -``` -docker build -t factorio . -``` - -and then -``` -docker run -d \ - -p 34197:34197/udp \ - -p 27015:27015/tcp \ - factorio -``` - -Note: You should provide your own location for installation files. - diff --git a/fle/cluster/docker/build.sh b/fle/cluster/docker/build.sh deleted file mode 100644 index 42b420723..000000000 --- a/fle/cluster/docker/build.sh +++ /dev/null @@ -1,18 +0,0 @@ -docker build -t factorio . - -cd mods -tar -czf headless-player_0.1.0.tgz headless-player_0.1.0 -tar -czf stdlib_1.4.6.tgz stdlib_1.4.6 - -mv stdlib_1.4.6.tgz ~/Library/Application\ Support/Factorio/mods/stdlib_1.4.6.tgz -mv headless-player_0.1.0.tgz ~/Library/Application\ Support/Factorio/mods/headless-player_0.1.0.tgz - -rm -rf ~/Library/Application\ Support/Factorio/mods/stdlib_1.4.6 -rm -rf ~/Library/Application\ Support/Factorio/mods/headless-player_0.1.0 - -cp -rf stdlib_1.4.6 ~/Library/Application\ Support/Factorio/mods/stdlib_1.4.6 -cp -rf headless-player_0.1.0 ~/Library/Application\ Support/Factorio/mods/headless-player_0.1.0 - -cd .. - -cp config/mod-list.json ~/Library/Application\ Support/Factorio/mods/mod-list.json \ No newline at end of file diff --git a/fle/cluster/docker/build_alternative_installation_location.sh b/fle/cluster/docker/build_alternative_installation_location.sh deleted file mode 100644 index 8e1f4c557..000000000 --- a/fle/cluster/docker/build_alternative_installation_location.sh +++ /dev/null @@ -1,32 +0,0 @@ -# This has has different mod location -docker build -t factorio . --platform linux/amd64 - -cd mods -tar -czf headless-player_0.1.0.tgz headless-player_0.1.0 -tar -czf stdlib_1.4.6.tgz stdlib_1.4.6 - -mv stdlib_1.4.6.tgz ~/Applications/Factorio.app/Contents/Resources/mods/stdlib_1.4.6.tgz -mv headless-player_0.1.0.tgz ~/Applications/Factorio.app/Contents/Resources/mods/headless-player_0.1.0.tgz - -# unzip -# cd ~/Applications/Factorio.app/Contents/Resources/mods - - -# remove old files -rm -rf ~/Applications/Factorio.app/Contents/Resources/mods/stdlib_1.4.6 -rm -rf ~/Applications/Factorio.app/Contents/Resources/mods/headless-player_0.1.0 - -# untar files -#tar -xzf ~/Applications/Factorio.app/Contents/Resources/mods/stdlib_1.4.6.tgz -#tar -xzf ~/Applications/Factorio.app/Contents/Resources/mods/headless-player_0.1.0.tgz - -# move files to mods directory -cp -rf stdlib_1.4.6 ~/Applications/Factorio.app/Contents/Resources/mods/stdlib_1.4.6 -cp -rf headless-player_0.1.0 ~/Applications/Factorio.app/Contents/Resources/mods/headless-player_0.1.0 - -#rm -rf ~/Applications/Factorio.app/Contents/Resources/mods/stdlib_1.4.6.tgz -#rm -rf ~/Applications/Factorio.app/Contents/Resources/mods/headless-player_0.1.0.tgz - -cd .. - -cp config/mod-list.json ~/Applications/Factorio.app/Contents/Resources/mods/mod-list.json \ No newline at end of file diff --git a/fle/cluster/docker/config/config.ini b/fle/cluster/docker/config/config.ini deleted file mode 100644 index ff820314e..000000000 --- a/fle/cluster/docker/config/config.ini +++ /dev/null @@ -1,753 +0,0 @@ -; version=8 -; This is INI file : https://en.wikipedia.org/wiki/INI_file#Format -; Semicolons (;) at the beginning of the line indicate a comment. Comment lines are ignored. -[path] -read-data=__PATH__executable__/../../data -write-data=__PATH__executable__/../.. - -[general] -locale= - -[other] -; Options: true, false -; verbose-logging=false - -; Options: true, false -; log-saving-statistics=false - -; autosave-interval=5 - -; autosave-slots=3 - -; In ticks -; minimum-latency-in-multiplayer=0 - -; In seconds -; multiplayer-initial-connection-timeout=10 - -; port=34197 - -; max-map-preview-chunk-side=64 - -; max-map-preview-threads=7 - -; In bytes -; max-multiplayer-script-reload-size=1048576 - -; Options: true, false -; enable-steam-networking=true - -; proxy= - -; proxy-username= - -; proxy-password= - -; Options: true, false -; check-updates=true - -; Options: true, false -; enable-experimental-updates=false - -; Options: true, false -; enable-new-mods=true - -; Options: true, false -; use-mod-settings-per-save=true - -; Options: true, false -; disable-minimal-mode=false - -; Options: true, false -; disable-blueprint-storage=false - -; Disables tracking which mod created/changed what prototype. Mainly for faster startup during development. -; -; Options: true, false -; disable-prototype-history=false - -; Print a warning for all prototype values that were not accessed. -; -; Options: true, false -; check-unused-prototype-data=false - -; Cache data stage prototype data for faster startup. Experimental. -; -; Options: true, false -; cache-prototype-data=false - -; Options: true, false -; enable-razer-chroma-support=true - -; Options: true, false -; enable-logitech-led-support=true - -; Options: true, false -; enable-crash-log-uploading=true - -; Options: true, false -; enable-heap-validation=true - -; Options: true, false -; enable-threaded-message-pump=true - -; Options: true, false -; enable-taskbar-animation=true - -; Does nothing on Windows -; -; Options: true, false -; non-blocking-saving=false - -; Related to MacOS -; -; Options: true, false -; discard-mouse-events-when-accessibility-zoomed=false - -; Options: true, false -; enable-blueprint-storage-cloud-sync=false - -; Options: true, false -; force-enable-factorio-version-check=false - -; Options: true, false -; bring-window-to-top-on-click=true - -; Options: fast, maximum -; multiplayer-compression-level=fast - -; Options: none, fast, maximum -; autosave-compression-level=fast - -; Socket to host RCON on when lauching MP server from the menu. -; local-rcon-socket=0.0.0.0:0 - -; Password for RCON when launching MP server from the menu. -; local-rcon-password= - - -[interface] -; Options: true, false -; automatic-ui-scale=true - -; custom-ui-scale=1.000000 - -; tooltip-delay=0.040000 - -; entity-tooltip-delay=0.000000 - -; tooltip-offset=20 - -; output-console-delay=1200 - -; train-stop-label-angle=0.085526 - -; active-quick-bars=2 - -; shortcut-bar-rows=2 - -; Options: true, false -; autosort-inventory=true - -; Options: true, false -; research-finished-stops-game=false - -; Options: true, false -; use-item-groups=true - -; Options: true, false -; use-item-subgroups=true - -; Options: true, false -; use-version-filter-in-browse-games-gui=true - -; Options: true, false -; use-version-filter-in-install-mods-gui=true - -; Options: true, false -; play-sound-for-chat-messages=true - -; Options: true, false -; fuzzy-search-enabled=false - -; Options: true, false -; pick-ghost-cursor=false - -; Options: true, false -; show-minimap=true - -; Options: true, false -; show-tips-and-tricks=true - -; Options: true, false -; show-tutorial-notifications=true - -; Options: true, false -; show-turret-radius-when-blueprinting=false - -; Options: true, false -; show-item-labels-in-cursor=true - -; Options: true, false -; show-rail-block-visualization=true - -; Options: true, false -; show-missing-logistic-network-icon=true - -; Options: true, false -; show-interaction-indications=true - -; Options: true, false -; show-grid-when-paused=true - -; Options: true, false -; show-inserter-arrows-when-selected=true - -; Options: true, false -; show-inserter-arrows-when-detailed-info-is-on=false - -; Options: true, false -; show-pump-arrows-when-detailed-info-is-on=true - -; Options: true, false -; show-mining-drill-arrows-when-detailed-info-is-on=true - -; Options: true, false -; show-combinator-settings-when-detailed-info-is-on=false - -; Options: true, false -; entity-tooltip-on-the-side=true - -; Options: true, false -; show-mod-owners-in-tooltips=true - -; Options: true, false -; show-descriptions-in-tooltips=true - -; Options: true, false -; show-total-raw-in-recipe-tooltips=true - -; debug-font-size=18 - -; train-visualization-length=5 - - -[sound] -; master-volume=0.800000 - -; music-volume=0.500000 - -; game-effects-volume=0.700000 - -; gui-effects-volume=0.600000 - -; walking-sound-volume=0.250000 - -; environment-sounds-volume=0.550000 - -; alerts-volume=0.500000 - -; wind-volume=0.350000 - -; audible-distance=40.000000 - -; environment-audible-distance=30.000000 - -; maximum-environment-sounds=50 - -; active-gui-volume-modifier=0.800000 - -; active-gui-environment-volume-modifier=0.400000 - -; The maximum volume allowed for any sound. -; maximum-volume=2.000000 - -; ambient-music-pause-mean-seconds=45.000000 - -; ambient-music-pause-variance-seconds=30.000000 - -; Options: main-tracks-only, interleave-main-tracks-with-interludes, randomize-all -; ambient-music-mode=interleave-main-tracks-with-interludes - -; zoom-audible-distance-coefficient=0.500000 - -; zoom-volume-coefficient=0.750000 - - -[map-view] -; Options: true, false -; show-logistic-network=false - -; Options: true, false -; show-electric-network=false - -; Options: true, false -; show-turret-range=false - -; Options: true, false -; show-pollution=true - -; Options: true, false -; show-networkless-logistic-members=false - -; Options: true, false -; show-train-station-names=true - -; Options: true, false -; show-player-names=true - -; Options: true, false -; show-non-standard-map-info=false - - -[debug] -; force=enemy - -; Options: true, false -; capture-perf-statistics=false - -; Options: always, debug, never -; show-fps=debug - -; Options: always, debug, never -; show-detailed-info=debug - -; Options: always, debug, never -; show-time-usage=debug - -; Options: always, debug, never -; show-gpu-time-usage=debug - -; Options: always, debug, never -; show-sprite-counts=never - -; Options: always, debug, never -; show-lua-object-statistics=never - -; Options: always, debug, never -; show-heat-buffer-info=never - -; Options: always, debug, never -; show-multiplayer-waiting-icon=debug - -; Options: always, debug, never -; show-multiplayer-statistics=debug - -; Options: always, debug, never -; show-multiplayer-selection-rectangles=never - -; Options: always, debug, never -; show-debug-info-in-tooltips=debug - -; Options: always, debug, never -; hide-mod-guis=never - -; Options: always, debug, never -; show-tile-grid=never - -; Options: always, debug, never -; show-collision-rectangles=never - -; Options: always, debug, never -; show-selection-rectangles=never - -; Options: always, debug, never -; show-render-rectangles=never - -; Options: always, debug, never -; show-entity-positions=never - -; Options: always, debug, never -; show-entity-velocities=never - -; Options: always, debug, never -; show-selected-entity-advanced-tiles=never - -; Options: always, debug, never -; show-selected-input-transport-belts=never - -; Options: always, debug, never -; show-paths=never - -; Options: always, debug, never -; show-path-requests=never - -; Options: always, debug, never -; show-next-waypoint-bb=never - -; Options: always, debug, never -; show-target=never - -; Options: always, debug, never -; show-unit-group-info=never - -; Options: always, debug, never -; show-unit-behavior-info=never - -; Options: always, debug, never -; show-pathfinder-fringe=never - -; Options: always, debug, never -; show-path-cache=never - -; Options: always, debug, never -; show-path-cache-paths=never - -; Options: always, debug, never -; show-rail-paths=never - -; Options: always, debug, never -; show-rolling-stock-count=never - -; Options: always, debug, never -; show-rail-connections=never - -; Options: always, debug, never -; show-rail-joints=never - -; Options: always, debug, never -; show-rail-signal-states=never - -; Options: always, debug, never -; show-rail-segment-collision-boxes=never - -; Options: always, debug, never -; show-train-stop-point=never - -; Options: always, debug, never -; show-train-braking-distance=never - -; Options: always, debug, never -; show-train-signals=never - -; Options: always, debug, never -; show-train-repathing=never - -; Options: always, debug, never -; show-network-connected-entities=never - -; Options: always, debug, never -; show-circuit-network-numbers=never - -; Options: always, debug, never -; show-energy-sources-networks=never - -; Options: always, debug, never -; show-active-state=never - -; Options: always, debug, never -; show-wakeup-lists=never - -; Options: always, debug, never -; show-transport-lines=never - -; Options: always, debug, never -; show-transport-line-gaps=never - -; Options: always, debug, never -; show-pollution-values=never - -; Options: always, debug, never -; show-active-entities-on-chunk-counts=never - -; Options: always, debug, never -; show-active-chunks=never - -; Options: always, debug, never -; show-polluted-chunks=never - -; Options: always, debug, never -; hide-chart-tags=never - -; Options: always, debug, never -; show-enemy-expansion-candidate-chunks=never - -; Options: always, debug, never -; show-enemy-expansion-candidate-chunk-values=never - -; Options: always, debug, never -; show-bad-attack-chunks=never - -; Options: always, debug, never -; show-tile-variations=never - -; Options: always, debug, never -; show-raw-tile-transitions=never - -; Options: always, debug, never -; show-fluid-box-fluid-info=never - -; Options: always, debug, never -; show-environment-sound-info=never - -; Options: always, debug, never -; show-environment-sound-area=never - -; Options: always, debug, never -; show-selected-entity-audible-range=never - -; Options: always, debug, never -; show-recently-played-sound-info=never - -; Options: always, debug, never -; show-logistic-robot-targets=never - -; Options: always, debug, never -; show-logistic-robots-on-map=never - -; Options: always, debug, never -; show-recipe-icons-on-map=never - -; Options: always, debug, never -; show-player-robots=never - -; Options: always, debug, never -; show-fire-info=never - -; Options: always, debug, never -; show-sticker-info=never - -; Options: always, debug, never -; show-decorative-names=never - -; Options: always, debug, never -; show-decorative-collision-rectangles=never - -; Options: always, debug, never -; allow-increased-zoom=never - -; Options: always, debug, never -; show-chunk-components=never - - -[multiplayer-lobby] -; name= - -; description= - -; Options: true, false -; visibility-public=true - -; Options: true, false -; visibility-steam=true - -; Options: true, false -; visibility-lan=true - -; max-players=0 - -; Options: true, false -; ignore-player-limit-when-returning=false - -; max-upload-in-kilobytes-per-second=0 - -; max-upload-slots=5 - -; password= - -; tag-list= - -; afk-auto-kick=0 - -; Options: true, false, admins-only -; allowed-commands=admins-only - -; Options: true, false -; only-admins-can-pause=true - -; Options: true, false -; autosave-only-on-server=true - -; Options: true, false -; non-blocking-saving=true - -; Options: true, false -; verify-user-identity=true - -; Options: true, false -; enable-whitelist=false - - -[graphics] -; lights-render-quality=0.250000 - -; Default preferred display index should force finding primary monitor -; preferred-display-index=255 - -; screenshots-threads-count=8 - -; cache-sprite-atlas-count=1 - -; Options: true, false -; cache-sprite-atlas=false - -; Options: true, false -; compress-sprite-atlas-cache=false - -; Options: true, false -; texture-streaming=true - -; streamed-atlas-physical-vram-size=0 - -; sprite-vertex-buffer-size=1048576 - -; max-texture-size=0 - -; max-threads=8 - -; 'low' and 'very-low' options are deprecated and will be migrated to 'normal' -; -; Options: high, normal, low, very-low -; graphics-quality=normal - -; brightness=0 - -; contrast=0 - -; saturation=100 - -; Options: true, false -; full-screen=true - -; Options: true, false -; minimize-on-focus-loss=false - -; Options: true, false -; show-smoke=true - -; Options: true, false -; show-clouds=true - -; Options: true, false -; show-decoratives=true - -; Options: true, false -; show-particles=true - -; Options: true, false -; show-item-shadows=true - -; Options: true, false -; show-inserter-shadows=true - -; Options: true, false -; show-animated-water=true - -; Options: true, false -; show-tree-distortion=true - -; Options: true, false -; force-opengl=false - -; Options: true, false -; v-sync=true - -; Options: true, false -; high-quality-animations=true - -; Options: true, false -; high-quality-shadows=false - -; Options: true, false -; high-quality-terrain=true - -; Minimum number of turrets required to turn on the turret range overdraw optimization -; turret-overdraw-minimum-count=4 - -; Scale at which the turret range overdraw optimization will start being applied -; turret-overdraw-scale-threshold=0.200000 - -; Options: true, false -; skip-vram-detection=false - -; Options: true, false -; halt-rendering-when-minimized=true - -; Options: true, false -; runtime-sprite-reload=false - -; Options: true, false -; full-color-depth=true - -; Options: true, false -; render-in-native-resolution=true - -; Options: true, false -; use-flip-presentation-model=false - -; Options: true, false -; debug-api=false - -; Options: true, false -; discard-buffers-on-begin-frame=true - -; Options: all, high, medium, low -; video-memory-usage=high - -; Options: none, high-quality, low-quality -; texture-compression-level=high-quality - -; Options: true, false -; compress-virtual-atlas=true - -; Options: copy, copy-sequential, flip, flip-discard -; dxgi-presentation-model=copy - -; Options: none, flush, wait-for-vblank, flush-and-wait-for-vblank -; dxgi-action-before-present=none - -; relevant only for flip presentation models -; -; Options: true, false -; dxgi-allow-tearing=false - -; Options: false, true, auto -; dxgi-flip-do-not-wait=false - -; Options: true, false -; dxgi-present-restart=false - -; dxgi-swap-chain-buffer-count=0 - -; dxgi-max-frame-latency=0 - -; dxgi-adapter-index=-1 - -; max-sprite-loading-threads=32 - -; Options: true, false -; gpu-accelerated-compression=true - -; Options: true, false -; gpu-accelerated-mipmap-compression=true - -; Options: true, false -; wait-until-mipmap-generation-finished=true - -; Options: true, false -; check-for-unused-pixels=false - -; ogl-depth-buffer-bit-depth=0 - -; Options: false, true, auto -; ogl-accelerated-renderer=auto - -; Options: true, false -; ogl-double-buffered=true - -; Set to true if mipmapped sprites render very blurry on your GPU. Limited support. -; -; Options: true, false -; legacy-gpu-no-mipmaps=false - -; Options: true, false -; force-linear-magnification=false - -; Options: true, false -; custom-mipmap-workaround=false - -; Options: true, false -; buffer-rename-workaround=false - -; Comma separated list of OpenGL extensions that should not be used (for example: ARB_copy_image,KHR_debug) -; disabled-opengl-extensions= \ No newline at end of file diff --git a/fle/cluster/docker/config/docker-entrypoint.sh b/fle/cluster/docker/config/docker-entrypoint.sh deleted file mode 100644 index 70e95f934..000000000 --- a/fle/cluster/docker/config/docker-entrypoint.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/bin/bash -set -eoux pipefail - -FACTORIO_VOL=/factorio -LOAD_LATEST_SAVE="${LOAD_LATEST_SAVE:-true}" -GENERATE_NEW_SAVE="${GENERATE_NEW_SAVE:-false}" -SAVE_NAME="${SAVE_NAME:-""}" - -mkdir -p "$FACTORIO_VOL" -mkdir -p "$SAVES" -mkdir -p "$CONFIG" -mkdir -p "$MODS" -mkdir -p "$SCENARIOS" -mkdir -p "$SCRIPTOUTPUT" - -if [[ ! -f $CONFIG/rconpw ]]; then - # Generate a new RCON password if none exists - echo "default" > "$CONFIG/rconpw" -fi - -if [[ ! -f $CONFIG/server-settings.json ]]; then - # Copy default settings if server-settings.json doesn't exist - cp /opt/factorio/data/server-settings.example.json "$CONFIG/server-settings.json" -fi - -if [[ ! -f $CONFIG/map-gen-settings.json ]]; then - cp /opt/factorio/data/map-gen-settings.example.json "$CONFIG/map-gen-settings.json" -fi - -if [[ ! -f $CONFIG/map-settings.json ]]; then - cp /opt/factorio/data/map-settings.example.json "$CONFIG/map-settings.json" -fi - -NRTMPSAVES=$( find -L "$SAVES" -iname \*.tmp.zip -mindepth 1 | wc -l ) -if [[ $NRTMPSAVES -gt 0 ]]; then - # Delete incomplete saves (such as after a forced exit) - rm -f "$SAVES"/*.tmp.zip -fi - -if [[ ${UPDATE_MODS_ON_START:-} == "true" ]]; then - ./docker-update-mods.sh -fi - -if [[ $(id -u) = 0 ]]; then - # Update the User and Group ID based on the PUID/PGID variables - usermod -o -u "$PUID" factorio - groupmod -o -g "$PGID" factorio - # Take ownership of factorio data if running as root - chown -R factorio:factorio "$FACTORIO_VOL" - # Drop to the factorio user - SU_EXEC="su-exec factorio" -else - SU_EXEC="" -fi - -sed -i '/write-data=/c\write-data=\/factorio/' /opt/factorio/config/config.ini - -NRSAVES=$(find -L "$SAVES" -iname \*.zip -mindepth 1 | wc -l) -if [[ $GENERATE_NEW_SAVE != true && $NRSAVES == 0 ]]; then - GENERATE_NEW_SAVE=true - SAVE_NAME=_autosave1 -fi - -if [[ $GENERATE_NEW_SAVE == true ]]; then - if [[ -z "$SAVE_NAME" ]]; then - echo "If \$GENERATE_NEW_SAVE is true, you must specify \$SAVE_NAME" - exit 1 - fi - if [[ -f "$SAVES/$SAVE_NAME.zip" ]]; then - echo "Map $SAVES/$SAVE_NAME.zip already exists, skipping map generation" - else - $SU_EXEC /opt/factorio/bin/x64/factorio \ - --create "$SAVES/$SAVE_NAME.zip" \ - --map-gen-settings "$CONFIG/map-gen-settings.json" \ - --map-settings "$CONFIG/map-settings.json" - fi -fi - -FLAGS=(\ - --port "$PORT" \ - --server-settings "$CONFIG/server-settings.json" \ - --server-banlist "$CONFIG/server-banlist.json" \ - --rcon-port "$RCON_PORT" \ - --server-whitelist "$CONFIG/server-whitelist.json" \ - --use-server-whitelist \ - --server-adminlist "$CONFIG/server-adminlist.json" \ - --rcon-password "$(cat "$CONFIG/rconpw")" \ - --server-id /factorio/config/server-id.json \ -) - -if [[ $LOAD_LATEST_SAVE == true ]]; then - FLAGS+=( --start-server-load-latest ) -else - FLAGS+=( --start-server "$SAVE_NAME" ) -fi - -# shellcheck disable=SC2086 -exec $SU_EXEC /opt/factorio/bin/x64/factorio "${FLAGS[@]}" "$@" \ No newline at end of file diff --git a/fle/cluster/docker/config/docker-update-mods.sh b/fle/cluster/docker/config/docker-update-mods.sh deleted file mode 100644 index bd3a223b1..000000000 --- a/fle/cluster/docker/config/docker-update-mods.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -set -eou pipefail - -if [[ -f /run/secrets/username ]]; then - USERNAME=$(cat /run/secrets/username) -fi - -if [[ -f /run/secrets/token ]]; then - TOKEN=$(cat /run/secrets/token) -fi - -if [[ -z ${USERNAME:-} ]]; then - USERNAME="$(jq -j ".username" "$CONFIG/server-settings.json")" -fi - -if [[ -z ${TOKEN:-} ]]; then - TOKEN="$(jq -j ".token" "$CONFIG/server-settings.json")" -fi - -if [[ -z ${USERNAME:-} ]]; then - echo "You need to provide your Factorio username to update mods." -fi - -if [[ -z ${TOKEN:-} ]]; then - echo "You need to provide your Factorio token to update mods." -fi - -./update-mods.sh "$VERSION" "$MODS" "$USERNAME" "$TOKEN" diff --git a/fle/cluster/docker/config/factorio.pem b/fle/cluster/docker/config/factorio.pem deleted file mode 100644 index a7e6661a3..000000000 --- a/fle/cluster/docker/config/factorio.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAplLz90nTGrSvwi3BhyXfKTk7xbOjwoPgpVf2NRd/QRVN7LLZ -d2kyTeFreUpig+rJD0tcS04sZO3Ydl8EbKnRazKT5/9rmd3xw3AQ6iTcEDWNxVJ3 -x+nuhxKisYNOnpBYKl/PJBVYecbyS7vaLXMifsa8PfNYim7aCQu4hNWOTE8436lF -iCoASg+Ve0tZULjmnk7htUaRYTP1g0rRNblLkrlQFCxTOGF4VKWSDSA+yBV8KKQ2 -g2jRGH+YzeyoSGKTMiMzPQ5VLdtCKgK5YkrrQjtxLS5Yegqx92k7ve3i5BhKrbYc -q4yN9IhABr5WEBcw/z1mBt+bWdhbHD1D7U7UQwIDAQABAoIBAG/q07UrnPyUZswP -w/Cv4Ck6FOlkhqYF3W0E8mwpQnzF3CiQ9FowlCPpskrsbbv8iYxcFl+p25+SmByW -tUB1SLCMWRA8Q+hGez8aMpeLc/UIRDxp0KXZ2KJVPNqb05RI5rqku5q5Lk7LOxEQ -96+rkKOkENjS3VpRWy/JDkF7/gOEGN+6eTqk5LRWW8bnumIQ8WYhT0jskBcAYq6d -NX5BEmLMTOEcjPMfl6KNkuy/ZHHRDfjsSqbIWltxfQ2YrpCW3hbBK07VExNyWvXy -OJJJyFypSFTp+pdh7NM7Z4JtRN5UdjJnDqA0MDcsa4uGC/gHzu+bOJ0/yycYUK25 -mRVAfTkCgYEA3zGtTn7TTa0ddGsBBnOWiPx+2Py66R2PjNuXkLadgXxJ0JE5DYuu -lERRA9Pz/BMJyO3sOnqymbCX2CmiXKvsuyfcVPwkFk+khp6YRNuuc8r4BFpRn/fC -LhmHEDRmZzQHako+TXTenVyKgfEnFLlb12LaF5MRP5rex56Y5OgyNm0CgYEAvsVg -eZ855X/ZpfbrRNt0ZSwpdaQYULVHzq+VjWTuE3/8bk2Bf0xRrIZ+B8Zedk7GJPai -fByp+P2Ldbbul0eC8S8vLVevsJrO2/VpFQjRZ9SB3XEenJoC9/NoU/EcfL2l4pVr -alMsDD3MlKFbdybdNiUe7Tm5Nruytt9tfxmVR28CgYEA1Apn8aj/qk4LK6z03mI1 -45KuGAuSryGMXW/JwIwdewTPQ7yUDVBk3StCJHba1uJna2LOCIjE4MoqmAyF2bDk -SLIH2x1oU2H6loJZeyGRzSRJGCmOXoGNEyrha/28Q9PkL5C1O/UKAUoHtIecb03h -+PBK7hakTl8dw1vjOaV2/skCgYBF0tI70aaMNF5CG30DB9uzSBWopDXuHFu675Mq -XF2zL24CJUYXhKSEI/mu+NEMNkrZrpfMG2Wu54YKwLjVD+yCQEXhL63zUsYRxeH/ -O+oBOcuKdGPC56/cdu+hgadulK9JnmzhTq3ZkSj24ZQrqULgFXgbVbxPpyWDP4XB -Nmjn6wKBgCGdNay79epXzt0LE5X3hzA9Ka1wLHL0oKNNS7damUDZkajJs7+/VxAI -/cMlnp9MD9Gf4qmbDLhb8HSpvKImKOQhqhkfLN2HVarSpD2hAIsoFr1n01z48cYp -TAPainVHnJ99qpD6fOEPrEFhKFJKwMjFK3JdsARvFn4tZENB29// ------END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/fle/cluster/docker/config/map-gen-settings.json b/fle/cluster/docker/config/map-gen-settings.json deleted file mode 100644 index 51c5ec3ff..000000000 --- a/fle/cluster/docker/config/map-gen-settings.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "_terrain_segmentation_comment": "The inverse of 'water scale' in the map generator GUI.", - "terrain_segmentation": 1, - - "_water_comment": - [ - "The equivalent to 'water coverage' in the map generator GUI. Higher coverage means more water in larger oceans.", - "Water level = 10 * log2(this value)" - ], - "water": 1, - - "_comment_width+height": "Width and height of map, in tiles; 0 means infinite", - "width": 0, - "height": 0, - - "_starting_area_comment": "Multiplier for 'biter free zone radius'", - "starting_area": 1, - - "peaceful_mode": false, - "autoplace_controls": - { - "coal": {"frequency": 1, "size": 1, "richness": 1}, - "stone": {"frequency": 1, "size": 1, "richness": 1}, - "copper-ore": {"frequency": 1, "size": 1,"richness": 1}, - "iron-ore": {"frequency": 1, "size": 1, "richness": 1}, - "uranium-ore": {"frequency": 1, "size": 1, "richness": 1}, - "crude-oil": {"frequency": 1, "size": 1, "richness": 1}, - "trees": {"frequency": 1, "size": 1, "richness": 1}, - "enemy-base": {"frequency": 1, "size": 1, "richness": 1} - }, - - "cliff_settings": - { - "_name_comment": "Name of the cliff prototype", - "name": "cliff", - - "_cliff_elevation_0_comment": "Elevation of first row of cliffs", - "cliff_elevation_0": 10, - - "_cliff_elevation_interval_comment": - [ - "Elevation difference between successive rows of cliffs.", - "This is inversely proportional to 'frequency' in the map generation GUI. Specifically, when set from the GUI the value is 40 / frequency." - ], - "cliff_elevation_interval": 40, - - "_richness_comment": "Called 'cliff continuity' in the map generator GUI. 0 will result in no cliffs, 10 will make all cliff rows completely solid", - "richness": 1 - }, - - "_property_expression_names_comment": - [ - "Overrides for property value generators (map type)", - "Leave 'elevation' blank to get 'normal' terrain.", - "Use 'elevation': '0_16-elevation' to reproduce terrain from 0.16.", - "Use 'elevation': '0_17-island' to get an island.", - "Moisture and terrain type are also controlled via this.", - "'control-setting:moisture:frequency:multiplier' is the inverse of the 'moisture scale' in the map generator GUI.", - "'control-setting:moisture:bias' is the 'moisture bias' in the map generator GUI.", - "'control-setting:aux:frequency:multiplier' is the inverse of the 'terrain type scale' in the map generator GUI.", - "'control-setting:aux:bias' is the 'terrain type bias' in the map generator GUI." - ], - "property_expression_names": - { - "control-setting:moisture:frequency:multiplier": "1", - "control-setting:moisture:bias": "0", - "control-setting:aux:frequency:multiplier": "1", - "control-setting:aux:bias": "0" - }, - - "starting_points": - [ - { "x": 0, "y": 0} - ], - - "_seed_comment": "Use null for a random seed, number for a specific seed.", - "seed": null -} \ No newline at end of file diff --git a/fle/cluster/docker/config/map-settings.json b/fle/cluster/docker/config/map-settings.json deleted file mode 100644 index c2eab18a2..000000000 --- a/fle/cluster/docker/config/map-settings.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "difficulty_settings": - { - "recipe_difficulty": 0, - "technology_difficulty": 0, - "technology_price_multiplier": 1, - "research_queue_setting": "after-victory" - }, - "pollution": - { - "enabled": true, - "_comment_min_to_diffuse_1": "these are values for 60 ticks (1 simulated second)", - "_comment_min_to_diffuse_2": "amount that is diffused to neighboring chunk", - "diffusion_ratio": 0.02, - "min_to_diffuse": 15, - "ageing": 1, - "expected_max_per_chunk": 150, - "min_to_show_per_chunk": 50, - "min_pollution_to_damage_trees": 60, - "pollution_with_max_forest_damage": 150, - "pollution_per_tree_damage": 50, - "pollution_restored_per_tree_damage": 10, - "max_pollution_to_restore_trees": 20, - "enemy_attack_pollution_consumption_modifier": 1 - }, - "enemy_evolution": - { - "enabled": true, - "time_factor": 0.000004, - "destroy_factor": 0.002, - "pollution_factor": 0.0000009 - }, - "enemy_expansion": - { - "enabled": true, - "min_base_spacing": 3, - "max_expansion_distance": 7, - "friendly_base_influence_radius": 2, - "enemy_building_influence_radius": 2, - "building_coefficient": 0.1, - "other_base_coefficient": 2.0, - "neighbouring_chunk_coefficient": 0.5, - "neighbouring_base_chunk_coefficient": 0.4, - "max_colliding_tiles_coefficient": 0.9, - "settler_group_min_size": 5, - "settler_group_max_size": 20, - "min_expansion_cooldown": 14400, - "max_expansion_cooldown": 216000 - }, - "unit_group": - { - "min_group_gathering_time": 3600, - "max_group_gathering_time": 36000, - "max_wait_time_for_late_members": 7200, - "max_group_radius": 30.0, - "min_group_radius": 5.0, - "max_member_speedup_when_behind": 1.4, - "max_member_slowdown_when_ahead": 0.6, - "max_group_slowdown_factor": 0.3, - "max_group_member_fallback_factor": 3, - "member_disown_distance": 10, - "tick_tolerance_when_member_arrives": 60, - "max_gathering_unit_groups": 30, - "max_unit_group_size": 200 - }, - "steering": - { - "default": - { - "radius": 1.2, - "separation_force": 0.005, - "separation_factor": 1.2, - "force_unit_fuzzy_goto_behavior": false - }, - "moving": - { - "radius": 3, - "separation_force": 0.01, - "separation_factor": 3, - "force_unit_fuzzy_goto_behavior": false - } - }, - "path_finder": - { - "fwd2bwd_ratio": 5, - "goal_pressure_ratio": 2, - "max_steps_worked_per_tick": 100, - "max_work_done_per_tick": 8000, - "use_path_cache": true, - "short_cache_size": 5, - "long_cache_size": 25, - "short_cache_min_cacheable_distance": 10, - "short_cache_min_algo_steps_to_cache": 50, - "long_cache_min_cacheable_distance": 30, - "cache_max_connect_to_cache_steps_multiplier": 100, - "cache_accept_path_start_distance_ratio": 0.2, - "cache_accept_path_end_distance_ratio": 0.15, - "negative_cache_accept_path_start_distance_ratio": 0.3, - "negative_cache_accept_path_end_distance_ratio": 0.3, - "cache_path_start_distance_rating_multiplier": 10, - "cache_path_end_distance_rating_multiplier": 20, - "stale_enemy_with_same_destination_collision_penalty": 30, - "ignore_moving_enemy_collision_distance": 5, - "enemy_with_different_destination_collision_penalty": 30, - "general_entity_collision_penalty": 10, - "general_entity_subsequent_collision_penalty": 3, - "extended_collision_penalty": 3, - "max_clients_to_accept_any_new_request": 10, - "max_clients_to_accept_short_new_request": 100, - "direct_distance_to_consider_short_request": 100, - "short_request_max_steps": 1000, - "short_request_ratio": 0.5, - "min_steps_to_check_path_find_termination": 2000, - "start_to_goal_cost_multiplier_to_terminate_path_find": 500.0, - "overload_levels": [0, 100, 500], - "overload_multipliers": [2, 3, 4], - "negative_path_cache_delay_interval": 20 - }, - "max_failed_behavior_count": 3 -} \ No newline at end of file diff --git a/fle/cluster/docker/config/rconpw b/fle/cluster/docker/config/rconpw deleted file mode 100644 index 7f75f64fc..000000000 --- a/fle/cluster/docker/config/rconpw +++ /dev/null @@ -1 +0,0 @@ -factorio \ No newline at end of file diff --git a/fle/cluster/docker/config/scenario.sh b/fle/cluster/docker/config/scenario.sh deleted file mode 100755 index 5e443696f..000000000 --- a/fle/cluster/docker/config/scenario.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -set -eoux pipefail - -if [[ -z ${1:-} ]]; then - echo "No argument supplied" -fi - -#SERVER_SCENARIO="$1" -mkdir -p "$SAVES" -mkdir -p "$CONFIG" -mkdir -p "$MODS" -mkdir -p "$SCENARIOS" - -#chown -R factorio /factorio - -if [[ ! -f $CONFIG/rconpw ]]; then - pwgen 15 1 >"$CONFIG/rconpw" -fi - -if [[ ! -f $CONFIG/server-settings.json ]]; then - cp /opt/factorio/data/server-settings.example.json "$CONFIG/server-settings.json" -fi - -if [[ ! -f $CONFIG/map-gen-settings.json ]]; then - cp /opt/factorio/data/map-gen-settings.example.json "$CONFIG/map-gen-settings.json" -fi - -if [[ ! -f $CONFIG/map-settings.json ]]; then - cp /opt/factorio/data/map-settings.example.json "$CONFIG/map-settings.json" -fi - -# --start-server-load-scenario "$SERVER_SCENARIO" \ -exec /opt/factorio/bin/x64/factorio \ - --port "$PORT" \ - --map-gen-settings "$CONFIG/map-gen-settings.json" \ - --map-settings "$CONFIG/map-settings.json" \ - --server-settings "$CONFIG/server-settings.json" \ - --server-banlist "$CONFIG/server-banlist.json" \ - --server-whitelist "$CONFIG/server-whitelist.json" \ - --use-server-whitelist \ - --server-adminlist "$CONFIG/server-adminlist.json" \ - --rcon-port "$RCON_PORT" \ - --rcon-password "$(cat "$CONFIG/rconpw")" \ - --start-server-load-scenario "$SERVER_SCENARIO" \ - #--preset "default" \ - --server-id /factorio/config/server-id.json \ No newline at end of file diff --git a/fle/cluster/docker/config/scenario2map.sh b/fle/cluster/docker/config/scenario2map.sh deleted file mode 100644 index 94c4dd23d..000000000 --- a/fle/cluster/docker/config/scenario2map.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -set -eoux pipefail - -if [[ -z ${1:-} ]]; then - echo "No argument supplied" -fi - -SERVER_SCENARIO="$1" -mkdir -p "$SAVES" -mkdir -p "$CONFIG" -mkdir -p "$MODS" -mkdir -p "$SCENARIOS" - -if [[ ! -f $CONFIG/server-settings.json ]]; then - cp /opt/factorio/data/server-settings.example.json "$CONFIG/server-settings.json" -fi - -if [[ ! -f $CONFIG/map-gen-settings.json ]]; then - cp /opt/factorio/data/map-gen-settings.example.json "$CONFIG/map-gen-settings.json" -fi - -if [[ ! -f $CONFIG/map-settings.json ]]; then - cp /opt/factorio/data/map-settings.example.json "$CONFIG/map-settings.json" -fi - -exec /opt/factorio/bin/x64/factorio \ - --scenario2map "$SERVER_SCENARIO" diff --git a/fle/cluster/docker/config/server-settings.json b/fle/cluster/docker/config/server-settings.json deleted file mode 100644 index 810b30947..000000000 --- a/fle/cluster/docker/config/server-settings.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "Name of the game as it will appear in the game listing", - "description": "Description of the game that will appear in the listing", - "tags": [ - "game", - "tags" - ], - "_comment_max_players": "Maximum number of players allowed, admins can join even a full server. 0 means unlimited.", - "max_players": 0, - "_comment_visibility": [ - "public: Game will be published on the official Factorio matching server", - "lan: Game will be broadcast on LAN" - ], - "visibility": { - "public": false, - "lan": true - }, - "_comment_token": "Authentication token. May be used instead of 'password' above.", - "token": "3799e696-74c0-4d6f-9f55-80fc737a17ab", - "game_password": "", - "_comment_require_user_verification": "When set to true, the server will only allow clients that have a valid Factorio.com account", - "require_user_verification": false, - "_comment_max_upload_in_kilobytes_per_second": "optional, default value is 0. 0 means unlimited.", - "max_upload_in_kilobytes_per_second": 0, - "_comment_max_upload_slots": "optional, default value is 5. 0 means unlimited.", - "max_upload_slots": 5, - "_comment_minimum_latency_in_ticks": "optional one tick is 16ms in default speed, default value is 0. 0 means no minimum.", - "minimum_latency_in_ticks": 0, - "_comment_ignore_player_limit_for_returning_players": "Players that played on this map already can join even when the max player limit was reached.", - "ignore_player_limit_for_returning_players": false, - "_comment_allow_commands": "possible values are, true, false and admins-only", - "allow_commands": "true", - "_comment_autosave_interval": "Autosave interval in minutes", - "autosave_interval": 10, - "_comment_autosave_slots": "server autosave slots, it is cycled through when the server autosaves.", - "autosave_slots": 0, - "_comment_afk_autokick_interval": "How many minutes until someone is kicked when doing nothing, 0 for never.", - "afk_autokick_interval": 0, - "_comment_auto_pause": "Whether should the server be paused when no players are present.", - "auto_pause": false, - "only_admins_can_pause_the_game": true, - "_comment_autosave_only_on_server": "Whether autosaves should be saved only on server or also on all connected clients. Default is true.", - "autosave_only_on_server": true, - "_comment_non_blocking_saving": "Highly experimental feature, enable only at your own risk of losing your saves. On UNIX systems, server will fork itself to create an autosave. Autosaving on connected Windows clients will be disabled regardless of autosave_only_on_server option.", - "non_blocking_saving": false, - "_comment_segment_sizes": "Long network messages are split into segments that are sent over multiple ticks. Their size depends on the number of peers currently connected. Increasing the segment size will increase upload bandwidth requirement for the server and download bandwidth requirement for clients. This setting only affects server outbound messages. Changing these settings can have a negative impact on connection stability for some clients.", - "minimum_segment_size": 25, - "minimum_segment_size_peer_count": 20, - "maximum_segment_size": 100, - "maximum_segment_size_peer_count": 10 -} \ No newline at end of file diff --git a/fle/cluster/docker/config/update-mods.sh b/fle/cluster/docker/config/update-mods.sh deleted file mode 100644 index 20bfffeb5..000000000 --- a/fle/cluster/docker/config/update-mods.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/bash -set -eou pipefail - -FACTORIO_VERSION=$1 -MOD_DIR=$2 -USERNAME=$3 -TOKEN=$4 - -MOD_BASE_URL="https://mods.factorio.com" - -print_step() -{ - echo "$1" -} - -print_success() -{ - echo "$1" -} - -print_failure() -{ - echo "$1" -} - -update_mod() -{ - MOD_NAME="$1" - MOD_NAME_ENCODED="${1// /%20}" - - print_step "Checking for update of mod $MOD_NAME..." - - MOD_INFO_URL="$MOD_BASE_URL/api/mods/$MOD_NAME_ENCODED" - MOD_INFO_JSON=$(curl --silent "$MOD_INFO_URL") - - if ! echo "$MOD_INFO_JSON" | jq -e .name >/dev/null; then - print_success " Custom mod not on $MOD_BASE_URL, skipped." - return 0 - fi - - MOD_INFO=$(echo "$MOD_INFO_JSON" | jq -j --arg version "$FACTORIO_VERSION" ".releases|reverse|map(select(.info_json.factorio_version as \$mod_version | \$version | startswith(\$mod_version)))[0]|.file_name, \";\", .download_url, \";\", .sha1") - - MOD_FILENAME=$(echo "$MOD_INFO" | cut -f1 -d";") - MOD_URL=$(echo "$MOD_INFO" | cut -f2 -d";") - MOD_SHA1=$(echo "$MOD_INFO" | cut -f3 -d";") - - if [[ $MOD_FILENAME == null ]]; then - print_failure " Not compatible with version" - return 0 - fi - - if [[ -f $MOD_DIR/$MOD_FILENAME ]]; then - print_success " Already up-to-date." - return 0 - fi - - print_step "Downloading..." - FULL_URL="$MOD_BASE_URL$MOD_URL?username=$USERNAME&token=$TOKEN" - HTTP_STATUS=$(curl --silent -L -w "%{http_code}" -o "$MOD_DIR/$MOD_FILENAME" "$FULL_URL") - - if [[ $HTTP_STATUS != 200 ]]; then - print_failure " Download failed: Code $HTTP_STATUS." - rm -f "$MOD_DIR/$MOD_FILENAME" - return 1 - fi - - if [[ ! -f $MOD_DIR/$MOD_FILENAME ]]; then - print_failure " Downloaded file missing!" - return 1 - fi - - if ! [[ $(sha1sum "$MOD_DIR/$MOD_FILENAME") =~ $MOD_SHA1 ]]; then - print_failure " SHA1 mismatch!" - rm -f "$MOD_DIR/$MOD_FILENAME" - return 1 - fi - - print_success " Download complete." - - for file in "$MOD_DIR/${MOD_NAME}_"*".zip"; do # wildcard does usually not work in quotes: https://unix.stackexchange.com/a/67761 - if [[ $file != $MOD_DIR/$MOD_FILENAME ]]; then - print_success " Deleting old version: $file" - rm -f "$file" - fi - done - - return 0 -} - -if [[ -f $MOD_DIR/mod-list.json ]]; then - jq -r ".mods|map(select(.enabled))|.[].name" "$MOD_DIR/mod-list.json" | while read -r mod; do - if [[ $mod != base ]]; then - update_mod "$mod" - fi - done -fi diff --git a/fle/cluster/docker/mods/mod-list.json b/fle/cluster/docker/mods/mod-list.json deleted file mode 100644 index ec0b0287d..000000000 --- a/fle/cluster/docker/mods/mod-list.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "mods": - [ - { - "name": "base", - "enabled": true - } - ] -} diff --git a/fle/cluster/docker/probe.sh b/fle/cluster/docker/probe.sh deleted file mode 100755 index 9da5f7792..000000000 --- a/fle/cluster/docker/probe.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -if nc -zu 127.0.0.1 34197; then - exit 0 -else - exit 1 -fi \ No newline at end of file diff --git a/fle/cluster/docker/run.sh b/fle/cluster/docker/run.sh deleted file mode 100755 index 23fb1d5f5..000000000 --- a/fle/cluster/docker/run.sh +++ /dev/null @@ -1,23 +0,0 @@ -docker build -t factorio . - -cd mods -tar -czf headless-player_0.1.0.tgz headless-player_0.1.0 -tar -czf stdlib_1.4.6.tgz stdlib_1.4.6 - -mv stdlib_1.4.6.tgz ~/Library/Application\ Support/Factorio/mods/stdlib_1.4.6.tgz -mv headless-player_0.1.0.tgz ~/Library/Application\ Support/Factorio/mods/headless-player_0.1.0.tgz - -rm -rf ~/Library/Application\ Support/Factorio/mods/stdlib_1.4.6 -rm -rf ~/Library/Application\ Support/Factorio/mods/headless-player_0.1.0 - -cp -rf stdlib_1.4.6 ~/Library/Application\ Support/Factorio/mods/stdlib_1.4.6 -cp -rf headless-player_0.1.0 ~/Library/Application\ Support/Factorio/mods/headless-player_0.1.0 - -cd .. - -cp config/mod-list.json ~/Library/Application\ Support/Factorio/mods/mod-list.json - -docker run -d \ - -p 34197:34197/udp \ - -p 27015:27015/tcp \ - factorio \ No newline at end of file diff --git a/fle/cluster/docker/run_local.sh b/fle/cluster/docker/run_local.sh deleted file mode 100644 index d20e07052..000000000 --- a/fle/cluster/docker/run_local.sh +++ /dev/null @@ -1,6 +0,0 @@ -docker run -d \ - -p 34197:34197/udp \ - -p 27015:27015/tcp \ - --name=factorio \ - --restart=always \ - -it factorio \ No newline at end of file diff --git a/fle/cluster/factorio_server/__init__.py b/fle/cluster/factorio_server/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/fle/cluster/local/README.md b/fle/cluster/local/README.md deleted file mode 100644 index b2fc6495d..000000000 --- a/fle/cluster/local/README.md +++ /dev/null @@ -1,140 +0,0 @@ -# Local Factorio Cluster - -This directory contains scripts and configuration files for running and managing multiple Factorio game servers locally using Docker containers. - -## Overview - -The system allows you to: -- Create and manage multiple Factorio server instances using Docker -- Automatically connect to and initialize each server instance -- Configure server settings, ports, and resources for each instance -- Share scenarios across instances -- Choose between different scenarios (open_world or default_lab_scenario) - -## Files - -- `run-envs.sh` - Main script for running and managing Factorio instances with options for scenario selection -- `create_docker_compose_config.py` - Generates Docker Compose configuration for multiple Factorio instances -- `factorio_server_login.py` - Automates the process of connecting to and initializing each server instance - -## Setup and Usage - -### Prerequisites - -- Docker installed and running -- Python 3.x with required packages: - - PyYAML - - psutil - - pyautogui - - dotenv - - opencv-python-headless -- Factorio game client installed locally - -### Managing Server Instances with run-envs.sh - -The `run-envs.sh` script provides a convenient way to start, stop, and manage Factorio server instances. - -#### Basic Usage - -```bash -# Start a single instance with default settings (default_lab_scenario) -./run-envs.sh - -# Start 5 instances with default scenario -./run-envs.sh -n 5 - -# Start 3 instances with open_world scenario -./run-envs.sh -n 3 -s open_world - -# Stop all running instances -./run-envs.sh stop - -# Restart the current cluster with the same configuration -./run-envs.sh restart - -# Show help information -./run-envs.sh help -``` - -#### Command Line Options - -- `-n NUMBER` - Number of Factorio instances to run (1-33, default: 1) -- `-s SCENARIO` - Scenario to run (open_world or default_lab_scenario, default: default_lab_scenario) - -#### Available Commands - -- `start` - Start Factorio instances (default command) -- `stop` - Stop all running instances -- `restart` - Restart the current cluster with the same configuration -- `help` - Show help information - -#### Examples with Explicit Commands - -```bash -# Start 10 instances with open_world scenario -./run-envs.sh start -n 10 -s open_world - -# Restart the current cluster -./run-envs.sh restart -``` - -### Creating Server Instances (Legacy Method) - -1. Generate a Docker Compose configuration: -```bash -python create_docker_compose_config.py -``` - -For example, to create 4 instances: -```bash -python create_docker_compose_config.py 4 -``` - -### Server Configuration - -Each Factorio instance is configured with: -- Resource limits: 1 CPU core and 1024MB memory -- Shared scenarios directory -- Unique UDP port for game traffic (starting at 34197) -- Unique TCP port for RCON (starting at 27015) -- Choice of scenario (open_world or default_lab_scenario) - -### Server Initialization - -To connect to and initialize all server instances: -```bash -python factorio_server_login.py -``` - -This script will: -1. Detect running Factorio containers -2. Launch the Factorio client -3. Automatically connect to each server from the client (necessary to initialise the game servers) -4. Initialize necessary configurations - -## Port Mappings - -- Game ports (UDP): 34197 + instance_number -- RCON ports (TCP): 27000 + instance_number - -## Volume Mounts - -The following directories are mounted in each container: -- Scenarios: `../scenarios/default_lab_scenario`, `../scenarios/open_world` -- Mods: `~/Applications/Factorio.app/Contents/Resources/mods` -- Screenshots: `../../data/_screenshots` - -## Notes - -- The server instances use the `factorio:latest` Docker image (which you can build from the provided Dockerfile in the `docker` directory) -- Each instance can run with either the `default_lab_scenario` or `open_world` scenario -- RCON password is set to "factorio" -- Containers are configured to restart unless stopped manually - -## Troubleshooting - -If you encounter issues: -1. Ensure Docker is running and has sufficient resources -2. Check container logs using `docker logs factorio_` -3. Verify port availability using `netstat` or similar tools -4. Adjust screen coordinates in `factorio_server_login.py` if automation fails \ No newline at end of file diff --git a/fle/cluster/local/__init__.py b/fle/cluster/local/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/fle/cluster/local/assets/connect_button.png b/fle/cluster/local/assets/connect_button.png deleted file mode 100644 index 284fd993a643bb271f76ef3e0b2cde0c6f1b1247..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2715 zcmZWrdpy(o8{dp!gd8DMv!f!H&8#rYFd@dU+(N_V7`Dk)(~fHgDLO8>OSv4k2t&#t zmir~wS0q%#NeM?TrQ^hJs$cc`{l2f)^L^gm=Xu`G`};hfe?C_TwibIJ3J?GQu*b?0 z=fL-Fd{Ytu^WQUNrAz<-=u9;;BUqW4K?%VOUuqx)0FY1fA(6Z-HB`I3yhx<(eswiS zFxw$6F2R8m(ZlP84)8ki+ISh6?(Wm_Qqv$}E1=N+;k^LtC^pDdjy&dj?E~gvd05)P zFz3J%_0~C2We0_VivEy23VLIVtw?q!5Ll(-&ihA-54R4Dq<_=t@_3GfQPXfZPV@3 z{Uk^(mRHb z2jX?S5kfI=&VA)}NYplWl}sY_n#Ua$L?9M70jsj6Hm8MOr_PFt3ww)6$4k%T_iSD( z((4}=X?^v4fX72G?d%NBh>I^~i;G88?d2l|GUb_-1$&8)2W z-q9zRLZLJL7%Y!a%_V-T$5f&V%LQ+P_F>SpyvPi1idG0MXqN&o3_~jU0k9Wgx81!=qE5Hcmf+s-D7{L_i2`z*c0)~M=p-{tMvM<^Jck(Np|78U8 zW3htJa5$UI)?({uF@k@AYwPRl!x2b05~<0r&}3euv%Eqy>CB(MH2H5I9EIr!5^Tnx@da6!@8|NB{A2Pf(Gb2n z_YZscvgFTE{+2NiL-@B7!$4Zw4SoXvzz?i&CPY7>B{yq^;%S*}UqhXGpC^fRR@u?1 z^;GIa-R-XLS5RxSA;4|pi%ACaTy_X#C0GoKmQ`C>Iips z^Z3CopF#2YDXycz1?dt|Pr{+e*{p^SHw`{C&WFxV1z5LP#2(;9&92CWP1NWMs)+vk zlKpY0`hf%RDOZz62R)N2x=VNBrBQ%LFRf<9(?|2H##3mDU1u@ZDx>Hj1}IB<`AOQu z2SKvLV!^dQjce9_=UQNH9u+UcMtVpli?Kd#&)pTBOJ|C(z{=`|)-+c!V6j^{!Bjh- zmCcZfSw~h-8WWgC6UVWN`$4f@yr1l6@}19&nM77==pW8>0yZ~G$m=Lnxc;UfMc>dX z1p9SMlM}@gPa@Q5c~7ETWvs%zJ*+`C!rPuT3oZw3d#HY+cd^a^bcFh7#}X6ijy%;- z!dp;%Xzg?d&+*cd#dV)KFgt;35{ggG1deZgf;=vbAUQn|x_M!y)XIdPu&WTJTOUsH+MLy1tf`cV|85VMY%N?djfn|$0hHmb67 zija`97cE7z-EUt}?Oz~eH)Og(5`1GUs|Hf8lwE%t9Q0Nr+jn!KF(bLDS@3ustWH`l$6P1TK`sl?`^`a-c?k$2!#e7nw!j z%eXCYk?TmQ#$zDr{-Ky6%DX0m_XM7* zu6{{|msE6f@5m${9?dvh)qwt`C8xDRcfj=4sL+N?Tr;xJYPkM-ASZFQ!C) zy>W05c5CkROIn$3)g)z-Q!Fi6BfrMU^^9=7e7|c8tg$$v(YzXV&K^T_&lRq7yWOWZ zT_gCnXzPpFh4}!@#iGPSJNrlXKgGqb&e(n!X@7p1sx0?Jt-L_Hn5s*3h(2DskPhs3 zx(!A_Flz1>w*+7^nR>G#ONmfUNXhi6dJI_q{p5{O#VJK;y5}BT?R;`u?j^;nSu-WU zo}2>{VBZhEgI-ymZf>c^tyjFoC5qhNUIwgkZ^VIeE!e{;^bXk2K{vF zxzy9Eenai~ID?B|*IHYwbu6AIohyn7{<#GeIv3Y=0PQ3?QMi{SxdBBTV%$88G zp1yN_Xxbx_ecI;t_Z|1&f1YTZXDU-rF_SzAL9$Pca|-MF$FSo*3Tf{=Lu;XH5-Iqp ziY^w}cZ9H(>ZL~ys*HT(aW4U_kgyhUuJ6cm_-mnFe`K{RK14&n73LC5z~Q^l+H|+PEJob8=7g|c zU#Cdws`;Gv)7v(MDY^^*D|{GhdR5Fh$=Uq%wZ;dH{?%>FQyDpZBUQ)NLA2z+^qmWY zL@uDXvmm2W+H%T8qvSAKE{KhE^8~zOMBD4*$tDjXjYb0aSTY=)JGa(s1`WVr6cNt28BD F`VXM1g?In} diff --git a/fle/cluster/local/assets/connect_to_address_button.png b/fle/cluster/local/assets/connect_to_address_button.png deleted file mode 100644 index 8e1f9303f13d5cb670bf12a737fd6f0318d5cb51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2933 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 S^=#yEpN&Rc(<;%heegfSvk;X4 diff --git a/fle/cluster/local/assets/multiplayer_button.png b/fle/cluster/local/assets/multiplayer_button.png deleted file mode 100644 index b315e5cf442ae2c0b836a5b865f376c6b64ee4c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2701 zcmZWr3p`Zo79T@n8p=?~mDol_$;=of#u#H}lsOTJgCb)dF;@?o9Pdsh$6H>9!`(XF>UZz{{nq~0{{HK~)?RCU-?#TU=R*h9A+!+? z2xJ}2p6UXQv!LvR!@xT%@-+?uQE(5SP@HKL3gFD+`URY1Lm*n|Ogf!mzuTzU*OyLj zZZ+PC;Dx&+CH>+;kCoIk18p^p+4VIUnO{*E-0p!?k6Bq&K8s&&{k3zvMY$ z?Stra-Dvld=A{R-;_`IY1mC%>X{h+ohLnHN%h|QArsf`$rJR9SqWZm0BDjcG_DFvVhfp~HW{AT4S1 z`I~X}`e3DhWP8y3933IMKphTII2!;_0yPCtv_XMD6fY%0l)+I06lyl~D>|F4_*LJz zYG`|eLZgA>5hjn#=I~E&1&G4uuR&LD0$e==9*z!pCN~J}%i=QFXkk$BDg{Ci;z2ct zE${_|K_@wUywDo;*#ZyhtJ4@1@YzHVXpQo4bOtC~9vd)6W6@X?5di=IfyeU0yHNLk zp@UD>s1pJ~Fdl;m4-ZF&o1wWpe~hW6r6mT7!{BfxpoIxPf+O%1nsE3Ae>?fFA1a&A zlmQtuBonX>o zdmU_2`-vQm(gv&A1eGP|w#3YbeH+R{Ik zaGWSl6c5@~P1s1L54q*G{gttT&mHdJ)6z0BoHFt~nGf7IAFbO)Cc)nuLtoKgB9W-8 zS$=O*6PJOHxo4mcC{4lRdJTIUkcrqtQR@`|0{P`)lAL~BSLk91=qpYkv zDz%9bolc4v$#uH%?yQ<#etG#@jdMODq8|bez~OP($eNui^ON;G)v%)MaCxM+h7u%J0I$gE!1(#UZnU*ukYC5Km zrhBTQIyyUt3ZImfRixR%E`wgIif`aUThz0{%H4@d&c}`!GsH;gXdHR+w}HhQ@7AVE zV#QYEqlEY5+j)7C=*dK*?1F;UixKA5Ah)ce<43FDzS9Qpy2`lB#!^oAPealhW&EKw zPd>lopkjO_Tv27fKqUuJcd8OM=j`J%J{+PZ&2}rMP_P5-uoo)s>r?19E8;&gZnpo3X9gs#7#|J{}@a7 z<2d|ZL(DQv(dYoU{xQV6(;fCnU9VmZYGzK{%Yz>FC`UbJLnh*XFd3YitlZty{gf|` zV8s$ct@i9;^_1{*rCGartV(j)&Q8K}JyQqOw}wp^t#gu!;*=PRL-O7EA(+?w2O=a> zsBFtw+<^2xPMV{pOR%U%G}qT{im!-XEX|U(8GV{oGX4X3z7eB2;J2X-{M1w=n1A{+ zsV1XwJuFEq(yM6Pd;Qi17c)8#bG?;m`Jr=nOaF{W*LoNRshi)jk5j%$P;{j0cM4Fe zr)qR4<)xkE@NC(u+=84OvYwgO^X1HTobl`F$)D2H^J2HGY`o>!6si) z;qVL0kwvv*WWn)Yd;xsK6f1w%@vKs_E1C@(<#b#4MB15;oBmn~7yFnKO)b0hR6SBT zZY}!I7u7=sX$K8Mf3mI{d}{9);q=q&tC`8fEdy0$R`UyLqW*3DK(;gC>htDv+h1I{ zqLz#1m^{|MyGA}>xOR&|0WZGW#`Bo+%?QJ2)A%>Mm`fAtsp9q3BGyf0vle)!F`Hd& z;ql<1x-~{mLe@_;1@}`^YTnOCxE_cu5icc>0Va5+{i1(ljXshHqVv()R1!!{@P_Nm z#f*8`)cs$<+~OXpPk6Djc9}9RD>PKpRTgYhhmrvsBoN(^I~aL^*|5W~Gk0JC`VOJ6 z>g8izamrh?K`!9C;TGR;=4|YRVrO_hL21X1%e|RS4_UM0hWSr~rBlml@)UOnbOkb4 WlAdLQYsz2!0ioF)q85_rKmP}J`J&wb diff --git a/fle/cluster/local/assets/quit_button.png b/fle/cluster/local/assets/quit_button.png deleted file mode 100644 index 695834925fbeee8cd2f3665cca13a17d4af1ad01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2709 zcmb7GcT`j962A#Cu#}}sP|*-rQA83$M~ESbE3km{q992~z$g$%qJ$<=6E_4yM~DH2>y>`cU#B$+S(i_P4rrt!o@!PQ=~nNqre-2 zP_v1BZ2YXcSg44rwK-3R7Y=LLZr*Ng1u4o0`i(8j$AspygXzy{IXm%rQxM&TljOn!3o@((Z!W_A@=+)l7^5}m#_E-5KWm5@u2 z8_jH8O}t~+)+bg!Iow`bYdp8P*)b|9Is2=mWJLbv=4R;n=4M3NAq++Uo%sLF#s4SK|tC85&(##!~vopmjj8E0sUfK$`JV?Z(#v5 z2cm@q$Q`^`bhM^rE7Ej0=6K43D+1D{pcJD zlI!Q|&o<_oqCR>UgYuRdjY58O;hZ%^xmw#IiA)w9sjrRE#-MQGNF>sPMe{bcC;j*t z4t|-UPIEW`#%Od

    W+~ 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 z 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≪0levAG|vN{_-sZ@Xa^v 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%0A1@aUIIDS!kPv&~BR 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

  • -rH_Ux@$r$~e>fdePR$Z7o)tHW-@&XHt;76Oryl z+Mxl%a;>Sp^__$hBU;_mCnz!rtH$g>yGnk&D@@&jY$jO|`N@87NCZ~u+N~wgME0vR zlrH7wIyNth)I%DjFz(_aTK3!jvWew)AC3HJXQBUfUY^}@w1fHmU>g&)JmT!Od3;-r z)0OfAaDKKzXlhqD#9HQ6KLxGRWR=8p?ge2Sc^S_5qr(Em+h1&<+y;>_%(bXT7NF~<<)?R^%J_pZ1X zIdYi|#|2zRy5J1oex0=Q{cHCGm}r0=V41*j(R&&QF z%Uqd;L3(s;tQL>)-{mD*r!xo=E6rW^4;SKXwTM+@Ho9#LJHJ9S+w_L^t^+Y>fThOC>!`E{zr3iGbPpO*7D+-K{!3 zxva{HMy}A316`8cXwScPb!Eo9f0^U#d=-?fSPUq7gJZ>?@yjLslRrJ(W|pq2d;YY( z-o>T32+BJH7CKJg&M*LQ(reX!5V5kO>01w+`(&>-r(*FE?Pel~xG0)Hb3m8u4 z0eVa8#3yUF_LjOfbZ}pf5+E<2Lr&HPSee(d9oSwLp0XsAk`A4;tc&Ihcn5+ zUBDn0OT{Go6mzvg7DqB)2Avu52l>6-vGm|FQCuqY)RB|8mPc6F6!#^M$LWW2^wVZq zzi_wsKZ?Zu+HWs?2|=IR_7`{cP99=8ZvAto2(&&WpwQx9PQssh?@K}D0?)PVnMAH_ zK5V9Aq=*Q|F`}i(-e84HgINaL^#VqjX*fOrqv!$r#oBz(Go+C3h^>|er&17klBeA< zoa_mW!VM*|Lr()BbLMdw5-`oC zXn3(C^!XTd56~evgXbZ!$w`mQMDn{w2mvPVfDa2ON%X1cXvN**v|9oMSJUW`k2o+x zRU$A#z;yMg-KG8ddH(}=q(|s{^0hcKrSc`yA5Dw81hv7zf|y#>;Z-lsVmXSDj=lzC0w7ke;(Vw z8T$9Ma)$Z`6p-sm%lkc+N@PevjgVbF$)skqO%$=W{UNf5~O<<^GdAtpK`#y8;(+Lc@$3r3Uo zPb_=?##LLNBHr=fyoyNAV6#Umzj-i{ug{-*B@j4gswlyX>=$psLrD>;tu5?(O8o5n zBWvzS<%SOK4Rbzi;FtoE3YWkX0*Y(Sm1eNG5BsYxQ%iJOhgst9l`83+^Xm>KpV1tnwvJMKs2hz2LuzW{)O~J=vB^P zj6gS}kw`cpwmI>lM>lf=$N>Y$E3pl-q!6i}Igy4mt z?|Imj!mxJz8h`uI`lx6i%;aR;jH`?vh9)9Zp&7LSp`}y5LD|*sn8wpeUpCY1B+!GJ zz#f%F3*Uj-Ld@dYAS-Ivt!ICLE(Z~KEuZ)GV=!AHb4j8T&P4|NB<5rnLmk;6A7ClW zI$KRZs8Sm3x+SWmR2F++g`hcc;=V9^lfSI2ncvJ5`sNzb+o!U3{V#k!)BZF>bW$Uy z3tc1}E*H@;e$HXd>*pdNvQhPx|Ji&;0AS2Rc>^1G@h#9|jJ$9197hR^?;*%8nl=ym zpe$u*%Rn$h8 zdL5G)`mn8y1T@)l&?q9oR-!!`5_Rg#K!20swqi3* z#8b)x7|}}$#7ZsK!4YX0J3eDnG&KNoo|FzrQ7lVhq0-Tz!}e$~Y+(uK!?%XM%o??Y zuTPOqqH2$}+;dx(5LQCOgRr|VjwpwN?n{cc)fyuDcaHr9=~I-E7mlXi0caW&cm(`H zT~fxyfE{)S>!>)s$SCjUAcb7rdk)B_lrQIItZZ=3;7bt5gS&7Y@Q8jDsyhO+Iomb& zIyumd^1W?VDLKb~?XIyw`jD6eu*-2ZpB|FHjphf>3y$=Gj+opv0qMhcpMoPb67K<` zbE3*oOjqg+qq~DSxNyjhq(hotB<3n?WXC85hCI3iIk0rt!0#A}UFeh|+jKNeB!PJM zXSi+4KWNS&m}VfBq$wzmhrbyqD%~@_&n{lr!R?FE#T^GcXatC!;i)4g_?bo>^n6lS zX>ijp!96skLsRGaJCq(ocB}OmDk6wtr97hjHS*WDfQJB*$esJh*Vnfb#M9AxvCQOP zD}Xf1rxJpFw?Y)Y#hq8~J$|fpZ*m>!jgt-a?4+N)Za?|al0kVp>dQr){q(eyx;6Gy zg|t#<)`8o#_3xPeDeqq^$C@a8vuTd~a(*Nva3a5bEf%ou5{99(4u9`Vq&_;Q|7Ne8 zn7g}ZedV4E|3i&X(bx4aOcet4hXvxZJ3}loIoA&~ynHbIsNwL-FLs`&KEIEuU-VVp zf8?XspSIc=y|@B>)GE0TTL6P<60eHX<-Byxk$TCj#xKL48}y&44!?|!v$VA0>9cKh zP&QyiXsnKVP0GOSmgs*D;-suR;o} zqnvLt_t*+;FYypXbXD7O%r z5~L(PrJpc&c-@=O$kYMpUO9APM&h6i+&_R>i~=3=U)VOsjupY=!;X{BVD@RJvOIw zU_Y$lv$Wy%XWS`IfPn5dGGsVxXwR=gG)#v$ z5qYDOz;)YGP!w`kAZ04>0m^qdEf^@0PVL){1bOpq@3!+m-t*_rFd zxQj<#N#xP;{fQxZocT!DyP6?d{n5thK-|%$XIix;;cf>wazBjic-k7958QjvD#{fs zB{-cL83S2PFs^(ELk_>Op%S(vN7W2|Ad+*~z0`rPROl<$@Pq7LkeN z6;yW#(4=OjU#Ioi(^4JF#RJATLB9Htkof-)r;Qb67B{GOe~-~_vdl*gTC6%QM2If_ zt$I#H8O~zCq|LNY0ULde83Am3-ki}B<1&2^4;-efm;l!`O~s-58<=b%b|(Q(3tNPG&OryF_*0kix$>lOfi zh5-6iXP#!hyoz@53n0U!-#rvBu&iWhbx)6*MvswEu@bD);-OTuv!AD*5fGp5JMKvX znHC_aDpzr)tI02O2F9b|QQQ4e11@Mj{3`6s^bNW~>3;;=DzM(EGlhVeKISIx&}J?; zesPR?0o(*|AEE$(%{;+@6t~=f=C%$rZM=lOz)?s0ZHB58;O@)GMeUy%1`PKZS z({ZwR6zO3f#8G+s_lZvRX)NVnk4o7ZH(Ip<;4teTWFhgkm!kWr*H@RzIcE`dGf#Y} zL)!iqZ9v}xe~)xGve(2d%rR>|9kOD2`nN!GE6?Hm9{AIObO4Qs@}TF;uT@j7 zfP61hD&DCX-&rI-eP2VKJGwwytfFj%`y*z0UO>$K+^+eov5Qk@*|83H-rRvbv=_kb ztbPBJb3d4#o=APY+{oM(HSfAbcNMG&d<-2UU!>nQnv&r~N-+dc1&D~Ag8BNEgq_h} z!#Avop0|D|NiaY%X~u?k58Sx=7GtuFm$ZY{UQuYd#4n->j74Ti6Xkv@fQW?yQVMx= zD&|rxLs$zvoCyn2MNhllG(S~bv^%j(^O_TpikR%(6<|o2%*m%QX6gyzavIlCZ4L)E zyU#fLN`1=8Bg*h|9O`HXYS2j@vpq9yeCFkp2m62g-!5vX8yg$oS#l-uyr+BJ+xTJt z(5a#a$urMDb7k$s2i}#Y_tGwt#c#65oQ&R%6am&0_y5)S3E{H5OhCy02xAMpu#`;d zf(xqVV*0^p*2~+$5q=*&>91cfS?^x>*cG>0AzhqW;bru?G1I}lSnAHt+9}e(A=y{a zikZCvNA5bU32!pGu8h58zj47;Q{cAo&91rU&_e2B%6od!=4uU*I;|Dr`eLy8&J*o^ z^$J`kH?%}3KghibYX@1TQ{-4pcG67O*{AkjMy?|oXG%%;LEqMCptSDmidJQzi3RWx zS`P=?%yV6wWcZuS3J7Uw?_}@jMBS7wUn+EGq#y*_La60i6$b(1gr^uustuiH$c|&W z6-Ic4odNfD%dKtvkR5$yhO2;(vqtP&ZmliCDncAyg^nmQ~70l-G?r z6JLRV7rz~)fm!^!i{6fcZ8%)jfga((+CF(M2Q?D%eY$&N4J*Ar9TK6Klmf{RuuyqE zS8egZD>>JI;_Rk`I#m;Q@I>@p<3LM7a*PixELU!y2UUAzH=s2%&ddbTB~#tQ=MLz#hLR;2QAsp(-$B131Ly`5q1%LI&GKMAO4rZFxpv)n#nst;W>E zby81MvBJlOg;RlYfPF<8O-h$^_~ymKj%0VO|KJ>mv#A7XiqX-0%G}QfN8BGBwfZg< z9n9gd0^k19+JgTH8&d=`KHb+;*N~M)l@J`GUeRAxAZcXfsbV7r#Ak}e(^?k((f1pk z2nSKSUqE^o0wy2HB$Pb`lW+2Zeh_(D$U9KYvrj|6uEs2C!1vt16&#lh)DjwKcC2xo zH|pDb$55z{U>k~pnkbk#3CeJ8uj&W^J%+jJ`r3$+S5l-f&ev-206 zzR$$EREPcCN(t^Judgq4b;T8C&X|8!YmCOdp1AsTEhFz;t|doj{_WUkWrYz(?vjF| z_s}MPk0NeX+#hCRa6~xIcC?lTo@A+&ic`rGLYgyWcqihvw!bBI(3H-S34R0l z;CCSNqwFe2NC=#y5s4NliTu02Ap>{hnUJA(r(EXmv87LiJXzqQJryOrwhEfc?B4@L zQ)W*HM`V9}+Qk)+Lh2RwYpVp6<1v9NcpmU;=m;B*A{K+hL)JH9`7eDfB52;^Y3W9C z)EC?^I1dk?*#2Xi)#vM_(Gd%^I4tbn!$0VMfW%;R!Rq?``*u(+xqypB&%W~a6>3T1 z&|iBpo)ySm^~NFP{N%M{Shi=SlGtpQ5wS{7-mL=a$g^P$Hh9Rs%AZC*6UYft{*8MA zD!|RzQd>d3dPB zK#UUfAFy~d7BPl;IvH!4+T(IAlJ6ve)4cORhu`boEW2!pPu%?Ik482!81mmpZUPBZ zhElhgoXj~d!R7~MCNb{*63_uw_wx;KfNDX3D0fcdGL!n>iQY>Br9F@uYgG-Ss zNOu$Wl>&# zPAmobW3hE`GTrKsH&=dP#_X%*-nbKfFJLf%>^_{uuwocH0Dz^8Ghrc zPWD`W)C2XNGj*^b{6afunMt!P`1$k-ohrI>qV~Z{;QNm;675L-^vlOv@TTqSTRq#Y zWGcl>l7y(kc2g(A{{F3(of20A*aJN0%vScsrz6Y4c8={)cX&_zw`dci3dHV-v=5Kb zyP@>--kSJZP+Z+OmxNNzWfCC<5P}78qVb2w4eHQ%y#eohw_QVLI}>Cc2x^%r1@?n< z%aS*0mvW*XOmzz%9p-uH(cYA@2m}CY7TviZ8tBu70+;s+-FG|tU^B#dSxu&ypr`A% z-^A|P-4G_eN`USvhdD4{uCBKNbUz?e;=c;9B&HiMw4^%T8!P!C(9O;T-ASO8G( zvOGtp4#3SMO3(X+;+e{C_^Lc-^PU@%DyDmPMf0n?dzMUO`_+G7Z|EJf%jO;uE0Ilj zz>cvE$5=)S&y}3hJSKSKmihBnhv87%>2&Os$$O+?5%X0g1)MVG)7{Nlda&=_D@{X3 zjfeCvXsY!F#^MpZKLEeL7dC52CWU{rO85>JFYgqeLnBLHwb<+}&@1D-@y0#kKiAM@ z3#NpfsTP{M#5V%>U3YG!a;#f@O6G$1p+3ce$)dtQIMMZJ_x^bfGEx#(zgGS1R{WR4 zpLK!E_k$^?9~WWX8Bd1K>LTYfC-yX+)^B#Wb>fgo2%0|XN~ID4W<(f((?9p6g-K`Y=?aQ4col! zwg-z%cU>!Z1kjxdXwB-15`;K4C17xI&>dXx@q@l_SU_&Jo9^_*@5*kWV)p~&av~~s zK3-C8b|7{_m4u>0y*b4$%(rq0$;Er1s{jeOsdaBt@^P{}8*xNq=f>LlNe9Ou7sH5= zmU7y&Ov9NQk5>D5g!L8k!+7|J#hWX}Z|mwlBzO{8FS=8&g3%Q8#y{FUDSPLXE3>YYFhDJ3M$8L-GfX7VDF57AiEUQcY0XwEoz>4N_=i+m z5dT{xa2XTeXbvq@)o)9TA|#A5b6P(|cZI(kaJPO9D&+RfK(>G`m8p{iw_`fhI0U|L zoSs_7?@pJr&h5iTI0G7KNv9RIkmQ)0y%4MBUh7U-`w4x2wDiCexqr#8Sg;Bf_Wm;V zT$zY*k$nx)Vtc>f*6{VvzSNVH_q*P5PAmHh;NK4CSmlZC=6cdRw2^h41dw8&>351d zMG;nnbf+jobsn;?c{e=Q-*c}7C!BZ6wUnE%fo4rGBjjOU&Id3Vdr^cqGE#AuewXhb&+hBj z?OX-B4pm>Xauq~XQEr_95jSNj!;uB%Y|1wW{7Ns0CFk4b*Wgqlc-cns8P~#~{JXU? zp>RsZSZ5ETIny+y$S=!C@mcLTdp@gFlDNr(wM6h^}0tx6&`!i z&BX>R#iikX?(S9{o|m9CF)6$h5xAc`rO?mV1QDHh{5JaRNrfV<3}Xl*oG6zjX@I$M znZIKI{TjkM+df1`R`J7@xhCG0(EYYT__kwLQ!Nm^D&;Y*k%>?P0w5kh$D+UY2D=2c z7@v)g@5g#rJ%Dp{B+}9o=^o`a;{>OAHI23~3*@WHF4!vFHFnRVyf5cIlZF;<~m~ z{^D)4@^;y7iqp5er@&W?1w^-eFwA2&jlCgB;d~V|N!kl%o3TFq6#ALn zjZOeocHbjX1^adAiX+t#wRc{JZp0LHyT*-@%__J}>jx?5Ia1|ur%jT@fxGLBqdfgO zjP1Oe74hrE>+(gY7QcL;9AFmR-*!EC!mLR+&v6xOZi$z!ue?NmFF5D3-c%vR&=3fK zva)yyFvy!mUc%AM%W@$vOOJ9+G&IH~9DcvSdB;bU<!aiTK;Rk@9u50?cH|g=B zUc?uoh>(UVRQe|$y^d%k=fiD|?@vDi z0a@;7&MSYt;d-dyX^{Y`XYp>2O*|;k=E#?Jdjwn20m9p~l@!?8;0(xK ze0++5&ga?={VsHrxvevA;Z3jy9%GDdFa@x4Y&CLBW!yjm^+eIRc`tPv#)y|ruQ zx~C%Ibcp?u?pjbCzojlP!`qCNquIYQbo%)?0>5QyfhsA{|AwW#_sK+38>YTZ*0Nz% zR7Ysrf6mPK3ACvFqn%GeTYE2RPPYjA{~|1%j}Z-0w-|tPaxnNOl=>2)82VnG$-x1q zKvQcG963SsA{1xFvz3_Wco<6fkgk}jGg}|l?{?M3UXxrD1*MH!k12IEf8#;B(mDRl z#%%oZcVJ_mX<38&#esdG)s<4><5}o`-TvzxCp|K4+m-ZQA;`B0qv{Z1h{s!lI;rfTe7qXI+;E7xvfkz%n<*Sr4i9MiX7AVr}@gyk2O}L zxWH**=bn9p+-r#qr4Jisvge+gTg&V{>JrUjeHHq+LMh!Nydrh<$vds*UAQJf1m^&e zZq05>W01i75rQMNQ_o1hD{&*ZzeI3L=}g|mY1ObCFYW(oo@+fe)!wfE9snF|K#`xP zV1I#4_u=pge)gVEsrb61Csaz#Aq@!K<~_v56P|JZjBP+J6wHQVBSNkIdr(soG@Q!+ zx2xbN%s1~TuJt-*ZMXUHv_59%V-ZHZ*Xv-);?t4BKwk6B;LB1D$p`lsrhrs?cW7F!N@Su;M8lDXn?h~8 z!M#`2VBD*OqZP1IsOd0#=)(kXOI%);8+mS8z3LW@vCZsWr0~9#AhZ!Xq@?gbrrd+$ z3|&gbNv|Kc-cNn3Lhn=t9Y}dgl?~^VJKJ(dn-eMpyo1n}o4=OYxVr1wt|QWXow_2o zn?qhi?2kA9M<4T%1Gor0opDQ^1-WGPh4K9Jk6aZO!EHVq1fPk=MBZH`;Guv4Z4NKH z+O*-1o9_R&5vU0pB{xdFbS@bEDf?Jd%U{ll82j_x*kX*B4!z$ zL_H5z5ccy&gg?&L>we6vaHA*R(;c$$z;*1#-s)<`ug<#Lw_f=xrm-=+MTP(2fL>yo zS0oT0t`0D%)npm0t5Q!aO=1u;9u-z$eVGATl$wRE7bWgRZc|Gj$nxjs^Ai;k#=yu& zwLHg%UKdoYXVsKT9%cTm6m@m|M$eQsj&8JA+_1-^7&NrzzE29Z>b?CCj@uC z>y|aXmoQNeYuU#&9h2Oc5Zt`qvFbo%wPN9~z#cYdjeS+U*1mx57V3h!9T1tRYY!O^ zB(2~=NOFz-TDIbNooXWm!o%4xEFJ(OQ7ve^U~t)Cje&v1v&*hZ<8c64soCT|=t?I1 zCS;EDONJ^Sp~MU#<1m#;q11Nj#&n*o@(2ITfshH(`3laX5-GtD!<$1fn@{ZL8QG&{K%l{*gNx~3;Ey|Y3^ig&1AGB=WK{TamgRM>bX0UmI0+$v zIO)y45p4lnKEqG}wHESIsy7cZEdh`+MT&Wwu9F+(y9hBeAI^1rtp3Z`EWPww0rSHp zhh>9lau}d$qitXMnLa~-xNUyGqitsv%{I#4LyN45Wu$|5&(Q)c3%|GUlGc%924`qK zC(2-IE(D8st}syOD@2It{Y+qU6?>b?4mbK|U?$%7qy+aQV}$l0N4&T_L}Ndx~o-LnoIe|^0yIvQ=7!-QTC!eP?3n7!OaxZG}EMCN*ssfpdB!)Qe7ZNjh16U9} z#b=PhpDTrjms~K<;^Y<-o&@mC!~e`BLQ&)vQj!6J*D~}dn1c`Uko}X&B4G8x5$zw@ zpfYh8+H37*L_i!BhE;1r*ImV%;B4I9y?OU?HHM27p|Sd9npTgc2=29Tl)3>3&jc+^ zO4P%${*&mYpxc#o+q}{GkHnVK; zm@1PN`{ZjFi(z0SsUac8Ruda85x_&p>0*io_FDINH}a;tOvO&wZLczkfLAP0yMMn@ zvg>jNmW09FZ`#=h>--TU$A=GmAKHN!lJ9*Tq`Z^4z{mv}5}*C@KFQh;s?AM)()$+p z&F;&+e)9uqztM^JN$0~U4fY0~(>&M=_hyO~=O?Bt;x>VB~DW;I1Y-0j%!zgVE0#_`Nv zr3juOTE@I=A?SD8yYAN!wm=NBL!H}OC;K)irGs5su2QEyU7Wl9d&8vIVRpUF!*1<4 z^E&)KLXZaG0;f7r@6mFrm(<&cJXGx%fxOz? zhMudP?x&9Ryj3)JzsnjuRf59}*z)=;SpGCPdnyY@A6HYM%yRDx%?dc@gqa7cpUl6U zf)9pj!wrMvEUu9+i3c7{-vTY-*xFk+ejY*~OC+2Zq7_K74X;}XSq4bM=4i0HXL12{qJGmME*AxMKMN3Y1U{M zu%TWWi+s7B2s8%nSfq+c)@&Dx3H^24FKAg;>gNxX>Sw z#e@hXm7@lDfv7vi1|MnMYvH<3&x%S$yphjcglu93JPeB^4#<%X;O6bW`xlh+_}LF$ zmw(HZ@#@r=t0Vd8psCZaH5d&Esw_<=6pg^_bSKO(D( z&-+ZMLe*T|n1WX0715Cvlz>S&&zX>n5KQ&lNFi4f{5pf1Qx6lRbODJ3pV{2l0O~gz z^ohU%(CIg5L`5~2wBee*?KCC-uI|UXgny@`AC4>Ci7p#g|EwPU_F-YQjEm1sn4LxC z;cKN?3!ZaY8qT3^Vh>BNa%`!XoW*nMi5}fLl7B>)_8N%WG{3Qh{dyeu0W#SNl9EcU zlTI~jIa*C=Y|e}AMp@3;Lxitqs^Lt6Jr~x^kH>`J7?FeEXDIBk-fFpX3NBb4byV2J zjHsDdjwIzY(cgl&MlqhE$*z%fDTyO&r4$XiS>e!Ea1&*%H>h^5!N#LZ?T2pfq|7j# zxZokZnf)n1p-(?@&V_21|*Q1t)F>i%Z?N#Xw1goH6cWujjqwg zVX?n7eH#(&FYzd?pxNyo^Jb&8SEEmTumP6z`40oK9L^KRIk7Ob{vas|g1{Qsu(K*- z*)_>VyXi|q7?HNzAkS{CNf}uJ3)Y3kK_qiC2x>xqtIU}`;51Be-(@I`Y{8G&@fkjc9c(8x6BqmvYje5vHmpV^E0FG1LJu6 z`Fk5};WT*HSCMhz?FK8{OftZ4r}x=eJqyOHiD<19-CrE&8s1TZ4#)!JmER9d4DnsY z*fgW+$_yAY*GJd_hwcgr5WiBb6iC+ss3^E%ZH1qqpdU?6qVEtHB>scvMPfW`P>+Ry zNA;jlKw*@4baZrR=>FiKxQyAvu!K0{>%D#Lb}h~4`ihvh`EAR&zk?bio|{a}-S2WP zZoA?l(1n^~v8MDG*4kGtE_KquickSkh6PgxJVf7NGc4Fgx2}SY9`mJ3HE6gi)A{5deyAt<6Q0@_{M8f6h@jxSuJc0i5iFfCy+cHK()peG?1~$F*(dg>!-{l7Q5Iw>jkE(h1m+VVMSZM_Y#t`~AV6%UzNB%_y(j ztFX&XC8;(tS*K?NZvPmG4;a#0&Cu9o$)KhQCMhFJ$v{Hc+%xDkzA^|#$?Cfpv0MWL zUSEvGi3PO@<6j&fu@$<_KM~=Sr83KXi`ojw5)}lhWw=6!0UPpXA2}Jofmpg4o1 zq)r++1veuF&nCeRq-zJr&HR7>{aY&KZ=6>7x-0%W?B+vCKmlrng}LW9Q>LK9a{M3g zVlBzxi?kwUR=o{M%LB}^tQdV|WosvG#CWY<%F zb|UeEVByS&x$}9${o4G@u3pfmGBlmWD^k;3$|tfQoEg;tAWE$@k}allWyY*hTc7d` zE@<_fa-B4-*$c#erU-7Zi8ZIB0Hls8BN_OpA?$3T;^a&OZEcV8A4x&_-_cnx6}&sn zG@ucpFD3}e|)xaQfg;7G4? z5o80@oZqd2U0HuoCq}*O!3zQ{P^T<^_fqp~*J%b?t;)p+5;On+p)xchS zx0z8400KfZX@aRuv_s?U?suVbf7ex@o@%jabo?9l*J^^u zBCGJ{k0wxrxX8Xc7aV3xB-(9CRP!--5h?$Ebz6>uZrg=~^F8>*ru_DJ0?q3M_e3rr zO|CXRL*`}Icx~i(i;5jm^J;7?`#-5@;Lwy*7#xn=HABHyE$k2_5y>-+EmgOA2oL+)BDy_K$liZ*$-2m7DxF4M_@Pq2ix!PhyJJY zNQe2`r*^Twb63B_k2L+aA`e*v@{t@kwzLK!XU%vG66qKP+a;v>g1IFuss>n}p z;P-uM(Qp2lu%`M>etl?S(#~N+ddU%8N%u-Sl4fc9ieug{*nG`D3MdPv?ZAV6*G1_NfOR*z#Gc2__H^mp23tO1H77qtzx&V$}O` zIkjT;`?tVapZ{`kdDCUnlNzAu?HU;vD8#vA`%gL8=dazqStq@(ng3T@k`^s8$iWVhb~o=*H-Y zB0x5eALG+x#@oo$TrnWRva=P*8rF|9X9fZJ-g1K}33ZR|y-sPpg>JDP`QuIlx3;|S zqe0AgVA{47(Ayjs0D@!uGNBj)Li0$XUURN^`U8;a|yDh;MszY>hJvf zH%_fOUdQ;%uskz?GGgZ1sxtK7kdd{FC&IL@(f){~Qr%{6!7ybFU88i^S3~;2{b!9_ zYX2#PMu6v(ek^g*lZLU$7I!K#@v7MS+HU(girMDzQLS&PkYM1}OWZRF$07@st!u@W z-PCw%+^R|+ZE^Hm2w`>aPEIy`$Nf3;8|z)=AO-tc1hUWHj6um@+ENfATy&wmd%8nv;FI^KHNB zZ1`_^lje0fKWa|--P+Dodfd*8EU=1moag^qA9=r9nk454*n(m}Y4>td?h7^{X5H`q zMkDY4L`0)(P`I=G5fl@{nj2p6`=#fb7T_&J85ecB{ZJ{<@*KUp%^KL<^69Q_D&BnH z0ms0&BkuZ28a9Bh{BnJk`6)s-VfTJ?8zJ~EjCwgFP509W|IMQA2iJJ5ZE^FogUUKdH58^SfrbMK0d2W~CWYoBjgP59K5kOJB_iy6Om%OFBCx@? z|F@x6)z2ll^|zmy0N~)hC?YD#1X3?9?Ou--$oUaJlfU={RIWE{ZG>WZzYYujk+h&4 zu5YmZ7(7Xhcf*{3GE{6GKDoUOvfs;K!z_AYGdMP@t$S|h$pvW!$vM7Lnj3R3B597< z1J`k!Vcz;q--Zu`>S`z7pYg4K{(Fjo=3Z(nx;Z+Yu_dCPy5mbfKbMbux<(Q7ZQI3D zOKavjn!v$dL1AV+y>TFc-Fw*~7&)yxo%R(p_yhJ^!ESLctkU;84|Rff(2I`M<#2+U zfnL-cv)D{ihC1~1s+V1O^3sXnO0GtkO@|=G*UxsnC#qrz?V@Du%9^qB%I4Ks(8kUJ;nMCLvc>fWM^Zcf9?XBfn1vck%W>( zHR~sa5Cp!nafr3S1_Oi6#K}$DgQu86FKLQAdg#S3lud}k<`)}8b*q())j|lPa?}0@ zjH1!q29FCWfeh?_;+Z`EgYENDk;eUTL#{O#^rT6gbKqAP;kXX5xqyAM+kKB2(xJ)O z?Ot;K2+BxIIwdyVvh(HVJM=^6F&pQ`NiXu}m_C`Lw2vog=BVB{1zphe9KQMHQEo_S zgMF!RrxrpH(){EW4tK`6`dxIO2on3}Nek1f`BTG_;baant7jaucGrm^@64Wv8g6m? zrrGPL*!F@{9RIazc@(P9A#seCJClY` zgtp4plv2|MLmJMtD7KX%a`7C|(a~os4I9x7R;C+>#VQynz-_bk_H&XAa;TJh%sH7Q%uskn3mU4ez+!L(nVt!0h-1Km=<(n zK{y{lri{wKKPxu;b?-Jh8jc(+DuPx9WhlLUIG(XREI2we%pA+45Xjqu_5|(1JZ0k+ zCj8q@Ia`8yV^;qw3M%yTA#m6q*Zx(il;XO*y(ijdUox`Ax^?9*ES22T_x|hb$+%7V z%)ji7jh&Y?hKa_?cQB+yFmr}xGV-E--FUsNpT9z`(?q0&Z9RT`pR4pHxSmi>_WSU8SMmb)uYT2R0Yv8 zgE%qA3T3%@+;k<+F6{-1r_joORafi@Or2mu+7dwqzyl5=H=%|XV(GT57||tUV+cbD zB4rV3vpjqBS_i@$&k5&uB94?E3%P|X&bl@L(`0&Ls0z5}sZS6^8`;X-M%eXZ)II-- zo211H)Y`|xeRu_O{`i_=XXg9{XRtS4UWA!Hc~@cmm-2?`8Xr-KR0Nb4)=ytYoz7ac?A7@z>k!%odE_bYs}d zE|o%`I^d)7^JueEM*C}7YQ5CtEH;bQ?CH&yo1wrMq@B&-!=YFnrO17t*46(yx|fuN zujd4XGZ2pF`=n>sDrr%l|0oB!Av?&CuA-r&^_6Y`$^oLZUnC=&AM1uUN1Dr4hG<6l zMH4I&keDXH`CVYl2do1^t1cRhZx%z>#|@d_W~F0_R@TzDGV0jh7m474CrV1`&zk#l z7q6&{3R0aH6A)4#OHXrh?=oeSJ-e-QlyHMEOgR3ba`LAEU};owlz9RDFxRd2+JOtz zCSncJOKb`HBw(n+3&Fs|9mGzzzl`n>4+V2V1U^-cVzaF&MXZ$9`DtxkJe;&xJKXxt zXiwG)96ZxordY`v@-k(kStC$MVb&ljtVQg*;BQfOwo*)S@ASPBO6o1h00Xxc-n1CDwZl&A4|=Nc2d8i{V!_EHzw(CzTUG{r#5*Ge6>(ygH1l`|x+^LFmTCGOP>f z$#74i+)v8{7$Glt**}S{^l!oaa+3pMQHseR8yx-@oMH)fc=vHW^5NMaFCVjr##Tbwp#2}ZrjiX+i#Ee|9l99u|FWT+ zLfpSL`iWJbSSoaUDT~gFe8|>a?61DAKgzbgx-R-d&I7hn$2BYf7opDh$+AvD$%EyJ zp3uN{b5y*?7$|*rAK=ol4_v0^3JH#}Rr`-f@y@(S^-viJHg!X-v-41a9y|0e9$W%4 z?H~#=(c$BbX6Xq0?)rHLk!P=9^(RT%ypOqyXY|hD>jz7~enZ)MyoDZH)xCiCTq<6LxrRcs?IP%4;j;*Ur07HDACtwWkSR1&62Q>igVEa+{sSrQ{ z!a#XhUBO_A_xPYT;*aboaX9{ui=|Nm?FHy1wE|Y*j#T0u8fwEDbT-adc=_3%ylVvr zbe6 z7O7SU^IZI_{aTon>EZiao>@P4-qGgiOwvmE>8Y&`dQb=m021=o)LZFn7KwPFky}yR z!R)m8@?t#aAvsjXj>+Sodl|Se#)DGJ8)UZJgYUq7{Lj0ucRhDQF3aEbg*c;D`v~}@ zbhKldja8)7%-?_mJuCz?%o=5zRi%HZX*be7u+yE}Sr6@1L64Rl#OLwk0f z>{2yyz?z2gfp^*js4%b{H-7N-c_p?w0~PY#Ivu620}ei< zrG=!OI%f3Pt#JzUoddgCuSklolur=CuNvf%^$H_z3Tq%yD|fl zWpo0iLjGA(04ZAw!Run)iaFQhJ5HOU3k2WQ%vP*~!YXGNAB=o>Nd%a1fwEjip$$d6 z;))I^lqch;c+H7DqsttD9`DBd*j)MMBF7^0Jy8=wGP*wVkVb8g#z+4M!52^p7R^1z zZPP=L0cJucC6aLDj-f{<3xSPFmc_UVw```UpZ-r%?eR_%<&sslPKZM>!Gur|nR%}o z^m#aljM)a|XRG8MI6EyOJ3kgud?^fq{Hg=pRn}gN_hMtjq~ICPaE`~o%N|Y2_m(*p zk1yNF$4NQpF|^`97UNB*HEgBzX`Cth-ig`|V9C6oT>nwF_Yb$BAMwa@0 z_L%x2?+M7?rXh}= zp+XlrmfNY9=&;4I;+p!L3o=kw&*3&Ebc&6NBqu@yQDUt%a`k{ax$p!2`Z)lnA3Stx z1#n*v_<6d|!TwalRDk=zg541c>x;tXNcmwT>-YUYK+fpl3q@2Y5CW_n9uHsk^#ugP zKXMGIl|2IW4Zc>KzDOflNaB8XwoKA-o#CZm_@~VLaj{kD=c}t8RKV^2VTP~n{6@*9 zQkXj{;9OW2@p>|8aWQm*#Y5c4+YSgO;BrTKKLp6*4vH&L;3FtRnVM3E~FheTl#pw(S~X{gi=zlUEz6Y!_PqH zv#p)6Hi%NxvERV4h&w7bvq7gsj7wP?p+}*~pV&PwBEl-L;1Uu-D1Mn)U5_lFPV0E- zm{HsUAtOa~=X_F@AjiDr>}9zA*H591*|FiS3!&QORAsCeELCN61dlq^jB#VY*w2F8@@hZfjROLr$F!T!~~>^P$O+LORuSY zj-7mf#O73Q;5VtT`+xhzlHR@#7@gqJ#^wzQeUrKAWwj%JqAlXtNq$Sz!1KLMzP&Kr zv8@O@$VRr$;P;LZ@Ine{)d5E0f69T0IiQ1;+W z@`U*LXCSRUM=hoNXQ#hA%+nMBmzkH785p0Rn^2aK>2eFj-X+XMMt^HXH%Il+zyvua zgKrRXF9U+?K3N-PxMQQ>0lR6fc%FGBPr{^rANsN2E1%< z&aa#y0-K*y2y6cO@07U~P0O@lUtz5+M=W4^{Pia=u9Re<4Uc0H^`PRRydlGnq~j29 zB7i{LW}#3bI1m$Djop|0c7+{KLg!E|$SnaXX9Gdka$n|g5)Uy3!AEmq-Qe3{YCs3f zp4wl%IbWR0xD|gKus$%cJIqdgVNOA{5P7yuLhb!V@4dyZWwFnSGqpdyHCHuxO;nL4 z+0Q`;-V~}AQ5ChQ^e=!YB$g+?8JaT%)DdU?22t^z94DSN_(LB3kU(~!3AJXNMfy!lc*fAczE}4X;UaJA!kSX9&2W7c_&Sw zR%lCmEIkEi?_6qK=Np$(~oj z-AY5SN*J6(5EYuAOxqu`PI4%5a(a4sCdb9HOS^qc>v@Mg6qaT`ahoUjsetH*5%6H4 zH4K&7 zX&j@NFi`)k)@G`i11zPX8aQZBh&%>_!P1Ot11bWj4|r>rd&~-_@aktja@RW72@iET z-RywsFS|G$z}N2jK5`X5jeVEGh}oOE^pjQr81A7+794Bv*W!q3x{8WR@36Gp#y4Va zu2N@ryz`Ij&t0?q&~~eL>y>>}*km`RV!g&9l{DfcO~h!e@Mbk zEbpYk?e?hTP|PX`FwlO!u~VnN^E8LPbIe<~o*H2i4Z%TBPxM#t?+$s1KEepNz>Jgn zCBJo)<%am9qMnNZ=f;J^tJEr!mG;Jr`v3+J-1D1^=eum+C94V$%jqY!AMfW{fDc9v z0MA~SD(cdF8L<9*pRwtpng%LGj0z9yvl@7M8049>?&6FX5vs{*yZ-gKs5}-;1Ejzo z5W#L4OLV!`FA?(8AH{*1;LGhw9SL}S*u}h;v&Z3@EtSqFEGV1SU-lJi1_2Eh%gz`i(II;I}=jEZ=1Ef4>D zm9uq}Tqy1SkoX1uQ^_fhrKdWMZRcwACY{r(>mXP>NCSUAm|YgQA*PLJq2X{Ve{9_N z=-_t0{@^mq!e=gUxi6SsM#4r6CdK{vq74{!GuxCf1|0eJL-uB&tCEq+ORsR`0?+?L52u5h7yA3 z#apjkU4KzVt{5RSHPIf;^&86*^l+`Qq{ml^>ozJmN~u(aG|%1Ldt-Z_d~yXF=Vm;QR2Xa^9@IK zXNKNfe$(JPOPbxOke1Ir$~WUx- zE*4!K-rBw?Ndx@YRe^vnXNDHww^^=V*3Sn1!)bWB2YPrQm~t{R2`)0}&&6au_wp~@ z_GfS9ri1JBk;m7R^AE{yTs5oR5u{xojj6viFM6YEF5!{ z(%(FZQ~yf-#$Gj~w)S!9gF{pzlnk2xcq>>I_2}iT|0t{C3B4{rq z&%~%Jd`?4%!d!MvZu)1$5*i-FxxSq`@H|)33U}N1#%E?Es*W! zP6SJYi08N5P^&oLqbSr*_6>-yWF&wvXibU`LG2p!!KsR$`^A~qaq6xoH&}cPZB2kc zA*ujQfFAgRm#aRHo5?SJdrPF~0QiZ0$CRzPiDj+b8nIrsv@c#w*8JJ68VRjul`?ijW0t=e5E#g86&&4+s2yRUvW{tJ4z~H`AMqJxn(F&EY<1UJ}~G!rBZCS6T&U> z$18@D_r~N&31tl?MU0#?sH`0@GE5S(@t-*XQv>4d0p~BsnIM3aa6SPiycuJ^FR(;P zLT3NWF#ZR-Gt!>0jSegkAbA$1jC^s!nk?XJv@WPa+h`^920>8UDO#BH2!fYsO$_;w z65&?t@66}GM14{4NI$WB5z?3ur5v3K6LC^+Q^S7GYY7Y<()qQ#Y?Rfirw#V>(mlq;g$&hZ7?0Y|BQY&Xrhf24y3N5x8>zTwL zz7m)bl7vmYC?CKjefxX$5c-~UVX*IxAU^cg5q#50?cSoIZTtG8o;3)uKD#3CbATQH zdHQ6RaXX67Q-&eo-yIq%!`9;H80TE^Z(%bsuCzR}(*-m59m-eW<^(K9*P0tM!fj@9 zt+*Y(5mm%;k;0&YDIP?nY2Q0e9lLJ9ONu0}=3q%s6t3lu8BTU{syHw?XTeCFji07s zs94&~ zG$DsW0Pw&B?=V&mk|0;Z$xw!cz*`IqKmy=Td?3QA5gqO~TuOg6`O zY*Bh5-KM0pVC+1eB%Q;6#2#`yFvx$!d zSztduu4pC>UK^E{`tnX3PLUY&<~n$7Ail&p;GBN(tkH$21B$QHmM)tuh_3L??7q3$ z-N!BlI0kz$s5#~>ww>g-Oz2Gpe_XIO`Z9~YS{SRli1}nLisxMhd@tfMO;6_UFtz4I zF|3u(FLw#?RP?Uxe;`ePIK8}Cn>B(SsG%5m%>NpLy@Eg^>yb}rL8D;`8wh!0f=L*| zBsV^T_90-9kQ2BjnG6PjE$(5c>2n`Dw!;oC=Vqkk;lI;p=ve(bJ=cj!$(A>3&^r4Mlv!c z&f>0{p?h~SvLDbw?w3=ZoQ!}InZdxpY$N0OM;=P#^#QimTJZ6g3!CkQ-n=xHnCg9n z2yMZ5{xruHx*y-_j1J#6jIul=)NJD===QID=gs?eC@s_e7n-F?=J)fNKYUM;oa~$i z^GR(Sy5l0aPiP}~*qXdK>B1*z$p*F6)iIa>cXguN8cg2BU8)6Rhrbhp=*M_6sP>R` ziddTU?(h8!U_nR-G|V>X{t1TR74Hd2#<}y-B7$h?4yBYXMxmt3GDRE~zY$k=!CQVE zA1~uTxs(rqTDMpG0-WN{Q8NeNdsU|#Y5;3n`_@#_yC4)J=HPbjjw|4>%H0whrliIk zy7U4vZ7R4oKS@BPdZ_$#{53m5*A&UR$I)u@E9f&P!WKb_y;d)#TgV4WP$OJAY$ORe0;z-KnHdk>M1UvJhCp=%atvH7og|TVS)fOt znA`kNv@GUN#CKMr31n+X8kewTrx-}n-7UNZHDb-Rnt z=<}?*m?3sziKS`-FTeZk)7OAhc1!q*6_&8&EYWeSN?fy%z5AwDw0dM|Ij} z1Vo&KT_g#$Cmp@c#~}RcgSqOBS%voyVGLst-JUSBcyU!%&YlEWI5;Xc%lnmD9-^&h z`g4)Sc_>?%f-dzWssb$hbF0C&kJ&UcDOxpE@hBwJvEgZ~!yeUdBtB;IW~?QX0-;g4 zlx(;8o|(A>`+Nk&3t348KN_Z-(0XngMs<}79JNLgw@(dRBF`EBZN8dwsG_uxq%t*iXQ{jAiu+t8>n z|)FfI{0i8ed z&HZ>Yc?{Fo-sUfsexJ)?i+ybCPRny>=o4TuA3YX{$=S6Jd8qpSYJDA|(g&90LTQE; zotyC+_=rDJH9H_Ec?>@^>l%=(Dh~uokR>^j^Mm-O{?^{1n zh*^iM&ZDmL$q?hW{+P*&&&{3#<*xJ01bni>cTmnKmlNn0+mrQdmmU0MZ6$K?-7R-z zvbOH&b19kw1pyA0lYPs2WH13Db^J7EZGH*L{Dx}JB9yaBjGI)uC~uXLq777I8V6ZH zpL9Xrn$ivGfJYfH&6Es+KMi|;SVChGsv8&u5gEb&5YOd;q;2HT{sCm{pvBxMtPAr! z!%tN8PC4bTQBVZ_)dB?;_}fI__9DA6;7COW@&Rd zZ5hur0Y2zjdI(to)j#2&k`}e0lsK?l0f@Wh5W?R9&>?^LKtIB2n@M% zEzx7lywsTNoK{ipTuvZ&c|tqTX%C)(ex{@VzX%ybW%n*kBKQZ2_McL<8sF2J&0+m0 zPxSk3mUp3guP4k=h&A{iprYM}ea$Y(Jlyo3wls|q*F3qSRyzsP)}t=!L+_D?#WG-M zns*dUQTR~fh{Dnr3N3#vRU@kH_;m``Zz2M0#3%l0az!XV7v5|>>YWV|i=a24xD$?V zLuZI^74VzMMq7|APVvF@i_l1jtM41tP{;cD`)BBznx|_5M1%1qR)64)_?%?|T+ul- zHq%C5ViO{cf}$dzvuTaol3j2n0^6r(8{{%bk9DDZcSeR`kJVCot7T?`v=T42Kvb6V z3zs=#E*IOvh-^y`fgxn}YkSA^PLk7&dR{;`UZ~>NLf{K6R|eN^F^~+i(s#PDsHr`- z7>lnFzaJB!{$4Wneh4cZAYS_#l|<+i37Ji^;}lpXdX|MyqKGDWow5kAyGg?Ah^xsEqQH``fU)0X(L6E}1>FJ3d*|s) zly-ugo8hC%LRb;5%@@vq`guL+CxRl9dM%M$5yW+~$D^!K#)@KRAaGt_QucvTNaR~8 z9ohr(#r1~khtwz#l0wmwrj+lHXL`z*g*52jo;PyU#&s{Y6nxR^C6OvItvmo}gx(K6s z>P`U>_kYHLVi0loj{kXvbpS0CUa=hF$-qVYfCg|7@_+!Lh8aDWk<5)j$fCY+@sVaZR~ibKttuCcV6k5H{=ZurMJx5^^>fBOiF+* zM`#YmoEZY$e=C5G-m}~gCGNnXIwWk$c2BoLvb`(7f@QL ziuID90&cxcqG->-b1)56mVz)WS1z~p=20`cPoo?8L@^nSlhYJ~{TImvB=T=@9MgISuXQ&`OD={aD}xGnOeldt%A-t$24PUFoUQ!j9b09>TvbeUFy}Xb8|q9A)Yzc- z*$(9BAri=33_sQ63gpTQ!_Q^ta%_dQYHD^AYXiByuKHjnN(b^z|5j}*mW0-AL0iQd z9g00*V7f*`Qei4-~9A_Ev)+$jjJg%}gwL6=eqUgJ*j?4_~`IE9M`5CkhN)Fj=e1khje`UoU!0DjU0!}ePCx97*Wu$PQ;vEy_5C1C zvRcW(Vz$3s-v*NsDSm1|-hf76MfvZB5|o^C75q_u^PTaB)X1G%6O?GM!OMzgZwW-# z;1hQjkW7jcz`TS6=rG6nzn?ij8w=v+SWhm0Y?b@sKL`?VGuaeGvq-eOSp$jFMk5f- zON2x4QBK_RPpadiJ3dDejve4hti>pV!w{0#Asi;drgXz_jdr_g#{NC&`!vQ4cHbC% z<`7u#0my!4AS&vJs}>z`6Ai;{i&Lw_G3=Z}ul}w>h#`Zg1Yyu<@WL|iNcuo|43r@X z`laj%iUkGl1l($Y1vG|*6xv)I)aMcM!u_XL`Ay`#P-Ug;h5(i}pPZcsB;L|&9t)%j z%SCpZznGu7PZ@}4hM7P8{7IA)XFzSK>~;}q)~-A`{k<<{=Uf}{pEjC~h_d?H+9Vj| zl1Cry48*oX%Dq0@A@{qr0iAih#NXl=Gb*3Oa~INGP{)%j1V?~}03q~$r1T`c&0^VT z0cZXNa#!ffD_yrIykD(DXe;=@s77GZq zeZ=Wi4{no!(9WSWAPLg8!i?_!`l2^D5)&=)>UbFX>nPmi3{N}ae(h@IAmU#oFqXrj zM||^v^1htC^zlI$CHx}sbYebOvM%l6za6&OO}XB$lJ%r1FUDq#xL!YfRh-rx>0o&B zHlT2J&UiAmp^R;l{UN_4RCPwv3BkHcMDTlBQ+0?l3w)OZ)=e(ZR>K^HBA5mT5w8*l zsYrFShEN1ZIcrREgSQ|IV5W^vr`M#ExkQKyB&OhFc~=$ZEmH%7d;}X@!e0A)1erbM z7&1`DuL+)u86>ast6%O*j-Z4X64{6jWvEajz$`b>dk-8j40GcyI@(@DP-eRz@+-7< zjMAOJSY>0O_LYnxu}U=BAzQJGnVSyk+jI8qq-`oq2Hou+r}9FS2|<}dJK2N#={1oI z9oet2KV4#AAu_BxK;01Kej^V5Ru>!k>o^2qm0k>&ocsXP-$;^B5orT9vMrJ1xAL`z zODMn%v;r?d!Z6&0ToOo+@Zdd#__Jwyi+Kf(e~Q+%wh-6xyGA?0R80!YPH*D=EU#3r z+R#anPZ8ZEfyn;le0tZVAs6;IRW*HGPMS=mqWMk+XXxOM0|F^xrz~&F-Q$B8z6nn= zh{}84bIZB=K{rbmgGf|N&r!_f)F{EC#9Ny%kp-T)lKka=RCkJqZSRm9H;{tOLlnNg z&!SAEUTkGFzWT(O$4$UW5+$oPD(A;B!${(kSu1b>YM1KgsSl6;sKx+UNRO9h#X)*y zY)3)yk7{s;xBNvHTDFd7(+Fmb{~NmQf2jXB{<*=OdG;QM>>VNN&PZmdWJEa)WcN)r z_c~u?%$Bi#NM_<5B3-2@uxK$6E3{c91QsNeou_-x!Rl}X7KH-TmCOw$62?rSQJ~7)7;rEAB|C13LGq7D}G@(eZ znf99`X9`xqBvX%CT}YCnk^&TgWcVO<&+PP}-=3*4;ymo2a6y~me)@J;2@{NR_-z+s zH>jApGSLxVW`JB%9_@>u!mb0GkQ(po`>eoQvZS}cEtDewql7TBAQh+Tvlj@DfU23< zggCr?APY9&b056x8Q1}U!hBZd-GGrqT*VCR%umeXdDBOXIJ|RtDuy5j*Z1cVs3Uybimb@Y0 zgMJue50i|c9j4?R?zQWd_R9Qp8UFzp(N`&x5MWKr^jSU=)m~PM`zRFN1rvH~UF-3I z@3Hl8GgEq4dh+lr65`Wh_$e2mmhy01WiPp(b>_aZhGC2E$7A_MPXwI#1fBg~_CLm2 z&CEB^)f(-%!`2S4{2OZ~N1vGyL{}?28wm!iT|fccq|+Mhqz!s;{GlS{bm@w^;2D^H zqY&`lDMZ5RL{v26p8g&g5~K{#{AdsW|Md4cV*JZ1NC3JbJsXW%x=Ji#DO~dzLfhO1 znjy-ly>yC17S{s(`G@mf`vg}?iuA~f!_)qGO@DtA!evrpY!d4$ayY@){Xje*u)k@4 zi07~}*@k77C3|g=vIUaN!-mA&1PH1V+sM0cbU2`z46%g2lAX^+3oYM(+OmcH2+vVL z;SAC9bM#rQYo1o@SS4~SAl?q&VdyP{q|~kRxp1Mmc}K_%a{8#Rp2cNX?+R(pOMcR^ z&V;Asdls$CLGf_$zv~{q7Ty>8h*&4i*PhyP;L=#sJ<-Ibx`vQ#OX$FupGjj18kUTa zZ7J8o*t70)?2ndir>iEclk9A1$xLAPm&xgPf#=93=8ovhirz~qd`rDIb+Du(>;w-o z#|;^z(USW8B2<9|eV&3g-iRi5b?QKXP*n+c5AQ2iIF^OXfpNeVa7W&k>OS{?btAl2 z^m52spz069sHcYE3^lMl$a}Ke=c@=Iy>-%Ls>@Z?!E3=g1d`UKhoHa`?7AYnsQD&6 zCSpnMEB(NS<2mLlAI$#DXk^Wi%4idy1duw_Z@b!Lt8){(-zF-28+^~R6h>vwL` zxEKXUb>~>Nrn}BvO^m>_MYBI&vTWy@Lh*7Z=)G*aY*~iwkP*gFm5AQmKlg;0p&xvY z&C10eB(IJ!P79A;rhfZb77lgq6rhq#OB^k~GnSOZo7>4rRP=Ot2YQS~%HUhDwnn&Lv!3PM_I{ z3BBV_n3YecRg_u{R6$Y{snf8wH*Lsy6;_-vN7!uM0m*=c=EzO{b&3UYc-td&iM{!E z72pZ8<$vFdrUsf5tH1)tI+Ow^Fr^A1HNkr@hYLg>=+#gzZ?Lev|6l1u#8o{k4yu#Z zzV4({nT7o|Gb-IUeVkgy_yLblu}s^(_HQQg8V|XIW(~W@9`8>?hoyXVi+pS`hFlW4 zle|JQk;l){ltCvg>g1IK74%Qc0MzFr}&S)Ah*^X<~?q1n|+n)aqlZm8zJNg)@=l^XZ3Mq0ir=z{w)HgNi_4sQJDOM zJ+a@f)#&f)CQ5=broCYJC^Pe1vq|}5?~Xhi6s@l>6M=Bd66`wJRf$!bawpmiuIg-M zhq375Ou+Q2*dKxw4(#9agVEQcS;24=q%ZQyhir{O3=BB6yBlG+_D!|n@Lyx#;`WM{ zZZ+HEo{jCc_zVv?h`-zpQ|S(oiqJ{w7^^yTZEWQ7->jQXu-C4lDTQ(W9<_N{>ud1k zS~6V0^*RnY!zA?Gx6Yy;l|`rgN|9gvjxLz{DnEm8)$N0cJDn4xvAL%a^XPf2{LK}ax`Mf&@3)* zb>DoM5vTn*^g#UP%7f8=L~!Q>L`ensQfb_2sgDi> zJz$<$1CSK)^1eT9_gPe`yOUa8Z|Hcp56le4pw2?p!QmO$_iO zcN_M2*4hHC1`~KcHLNI0-#hVGahCV%%C_FA{li6duD}S92c_=xEM83odOv*sv?a-P~ zcJ!{B{MSyM339d+6usSV1NV){Z)^M#=Y-X!C4 zY;2_V5<;GGxq$xqg`e}@{pw=}T_^v?h{WZU&Q3=)Hz`^B(#wk6bdN0fWJs&B6I;vp zHg=W+`(+j$;>nIJeO0njh+Zja{l#oJm zj}>GaHkApN-*-N&i$#NXELM_I67!3LczS0@}I%E;LQPDEyI0<+!ZF4mef;0B^LlLt@*#d`!PCzwIA zx7a0=l8AjY!TA%QD}&Hx?ehk@K-Dm;8z2Y=qdS?$hopr^a-M{8%AGw@JZg9&4W+E5 z9P|Q$J0W_NHCk72TI`idAD3i}&2h})xDWm9>U;gED`5qs3%Z1uWLloz^M5~ywBy2> zZG>6U*xxIq8mFQJSwS9guyl~LhflWx;hXO4)jw8mU*sA#cBlBCjNx^T`7C%|2A=vS zeTn*_8Na|4wDn}a2P;+KxJBv$UWCA1Yw1qS$4@Kl^(See~I;Y z#@4S-G5-iY$sq0rN)N9+Hw(gB!%VkdS2ktAEYDN2jl+{2iDTCa54&}f=;@s^i#t-K z_C?kBr3gMV4Z^RL7fMRttpdo!m_(xRL68vt{9*FH(`{E{IjG*Vsa}4lQvi1p-dX8m zzLsp2UWexbsC_NP;KX>09g5^^cCi zFj;$kh)iIIZu>AMeKJo3_nC2@(d+dHUKMlI@eo$cu{M24qx_p2Bp`EdS9zJlfdnh+ zueQYM=utTDaWw;I;q)NqhB!T|jT;{^_&0lb@wp!%%rUYMO`r=UKq@x%SWLKyKr-*~ z8zPz{qfzV=f+;cka<55h6Xl^M8s~>6hQCIa(Yo_qL@;ZKry7JAu>t1lI3PHkI~4-Z zXQ5ptf3MDIK{)r*QE+;|HDVm5Pd9~J3s^HF=_-21tH}xUbsXm<=ra~R;kMt@6ecc~ zR7a+Erm$9hxZI^J@^r4Tk%@hGTsRK|zDf>;ULqaXj#CSRPG$I(&15$eeo$vi-4cm0 zc4-S0OtfW=dj#7v4bC%V1lir7Rlp?62+~#rZ4Q8CUaqvAG=*Z~;*? zpmm7&PU2@qzM@{7FUDIQC8&-~xnx3I9mUB`cI*xYQ;t?r61svyIb`E|)T=PRj zNPpo&-806;-*TQe{ubMA99mAS_1;^Li*MPPI7a$ose%2N6g3DGwa5r!{ZPm^;-07> zwCq}`UJB*f%d51Tcm%G4?l*wOqYz?G^q0+Bqws;6xZfBgq5|?0)u478O#ctiH}1>S z=(>LckYdn;eNWu95srDls{P|^mT`=WC&(!kybJ%Q2chscuEClPFFl3I$wq0fte%dm z)&@WD_Oa6|hwm~qh1}Cezgpz!`0pAZKgQS&6M<2CPiaAJ(|cdT{e=1TTH!n}DsX66 zCeh{sd*=KumL1aD5u%hXIUK!*$#anxHm;i(B) zn2wu;xaaQPAsZacvv6MQzQz_$pi%7;Mz)3BGP9!g%Dk$OxqYR@0cQ=FbMH93d)*+? zXHAqtd7D%A$3N!Rw2Ve~c4GhbI2hpv2m`gN583<*Ga&Y+*o^YyJav$r;vbBO7i*0o zVp_LbR{~8KnEZZoA)Y%A_f5v(D+ku{Khlh6^MO&jDw6@{rFqLIfZb2~Gj>~j52uO! z0foULI%G^WI?46${bTl@G1vY~eQ4gRczOSUy}4U|vd?zHZuL#*dl5j+zRAeI;K1+| zw9sPar1K0QawMBiZ{VYU&EJ=-i+wx1XBX%KrwZ7T)2t&E4`n8t)WE0{N4*jOybu-j z%caD;0?I98r9jmewG(m!PFsdA_HnEh*D^Qz7G&rw*2%9yy1c9>lZCSQI4Gq;zL+02 zd@)$qx_Q*c9IQy{%N{Yx?k=CZEt>RJ;o{C*KJ^A#gSUBd|M;qUBFVBM2-H;c0}Q$OZWmZZ8bqN zs~(KYYVqK4v5E7qq4ICD33sO|_<-JE^AZmJLSSTOzsxD~ZDQ3V=`HVevo|E*fFd#l zJ#{n_xX#GMtAk7TPn|s~RZ6`@JzoZM+6`hRX8~pmNvE6SboQKG;3BQu^&D51of0xf znMFsFDBi=Sp_Xb6GC|*pS^mcQq7-z1E?$-(9T9RcAlSIh?DZScrs_oDqj7P6Dr`(42$_N7RQV=8#d^0!@HRN73jh-(0UB3*_THB#whI0NQ_Xl6fb8dQTr-+x=e^`9eV$UVW;|7@ z>8?jl;P^WV?_;6t)u$gNTxLAb)PMlR(`!^Ql?I&eyHaS_1wyo>0XpX!qva!tjvwf3 zU+>q>N|k;t8dQWv`@M_Fte!eyZ0dYA*uKk*c^c=uA+dUH5!0tsQSigWtO$0mOlgeX z-~$PHc8gBkzAON?fG|+C;_0_Acn#ZgAxI=Uc2U+Q29P4fNqpm6;%>e7uT*-FzcMQW$uS5jH z6Q}^#fw^FPiOl`HHn6xZs(;qzkBM#*X_StKd`3(Z?;;$b@c9`;mo2$xn8Y*TtB=He zQIWcAv^1mzzTO?l)`Z3G-W^iY%jNBug3~c`4+OB1Mb_BHcgfv5AuY{|BJe&tI1vLP zzSD|tkPC_KX~eVTG;%oY+?sq)_IRK%j7rOqr0mdzVH!&;CC^-Y05asI0yZ(o7tVi<52#z+sVuZvkwRA zT>DON4c|olH}4#4JY&oQ8_R^e1Rf8)vjo~9Y2=KL6;Pc266>MM*OS>AOhXS9Vl+ci z<8kkCuNuTM?@rb`>Xi6cNHpuA&g?4*r1c=5P;H-%>x{K+jJ5P)^G~la~7PdMs72<4K zpV!Wmg{ER}atC!v_{A|4-f>IfM5PTQuRXf25QQrAG9Wmm8e=B9yj(N(w#*y$ZG0ju zH>QQ&-}>tr1iU3(d0^3MbFF_m#m`s%7OF&00}{*$qqAkW-x1fAw4&P_+6WFs-+F{- zUjR#WqF45(AznsRM>dP!tfGM;el(XoRlW3M@7gp{?|WR-0Ax)07G&oV;uvP@0`_yq zJlL^iGaM#baH7fBHruVG;Z3J=rpCpA;iFhck0(ThEpumkG8RVe96SknvNJz`5u-SM z!kPgUfSM=>r<~g^{x%Bs-lDJkD07+b{q{#OD##k!0`xF+Ik9bX`P;f(_16ItdU*iE zNelJ^@OCS?3n4d-F;)uxq7Aj>uyW*=u&103rW$rknS#U!U=WysAfd+9ocSM=a5>A` z)Bh-zk7FT|Rf=;FA}YRDG9-;FhrgXB^G1inb!$ver_XVnpy>^>6pTgk^~Q7bI$bi} zrxBds`mOKAKjvfZM~QhyLHw){O2iv~OnyogDE-ugkuTY=Y+9?k#75_BCwIW)3>=uP zL{40xfEfOwCcKR-R6wn0T+;oc++>ucwLNJt^fy*IhW@6!gEIY~ON<$COpuLPB(=k2=$>aS`U&|asUqe$5UcUs+dNyu#pPZx@YexylP&)5 z^0YaHR(;+yw$N^?Ws!@6PjP+SlGT3MJz1JYNA4Q7Yd=_wLwi4w)@QmuT(Ib}_42Xc z+7s(uMDmx7&sFsD*e1QagcZKaLnC~Zo`g# zUGXX2?Xh~F!4>d|?(BpwA(Sw|0(3zE{63YOvQ2x>3^!1lnTT5=H3Uj{ba-9B z!Svk}-{AN`hP8l*dJYS1+&6n2@yS(@BJTVL(88Df$Mt0*7HG6WaPhK|iViW^jGrji z8Ly|m|HVBTto~#}$@8+*zW){V4H%W-^lmJXgK?Q&z0UUvqzn2wDTPRTGAHx_>~vhV zB*$BZtXa_Rn7W1T5Ru{3`Hzhz_saC7gyAkCE9F@z%hA!Ad*VzhoXW{)&@HwKWqb?J zmB0+oz#JRDphnLpM16v|X>i941Prf*=nmf&tG9AZH4*#Xof{wyA#D4T-SCe-_&{Cxiw+frfulQ?pK6iUSK^7rbkF zg3H*8J#4Zk;>s0JP>qc^h11pSvSa(F!5c&n0xrR7?-0C3h{Vk@Bkx5fXEgDf8*q~F zK?Nxj#1R|z3Ada~Hn;h3asVpItN!XknMq>VT4GwCc45sD;+lH<_vd)?2EQt-nGrOm zD8^^G-GM9pyAfG*$W4nL^oC@8HMXH&qya0-XOAJj*=D|xEMSegVnL4cdPH~xF2pYL zR{I^^^Y1--^>+Bkzw94yvCaI;>ZJ#XXMj(V96S(h-sS-6s}{lib`~)y9xOk`%r_{v zVjEK*+bEhso7w|A1jh;I7F2t1WSi`+B3vPm^Xdq42Zdz z+brky?gXGW-`@hup6|mLYOs=F$*v1v1+6biiF@!jb?r*jbyF&>pR?h%J7TUaVPW4x zl|g}r0gPPB z*wX!b>80#^`(R)wt z21RVk7%%w4m`jP$QWmr(7Ot+&o4BA%7QNE~Qy#$e>0>>mCKGQbE19*z4qtpdCdP7* z0a;R(0d9Q^zCC?L;Wo|<-q}|^$G0E!6EP7bqKn>DVdo;6BUyKN5K7$OF)~nliGoVM z&HnXca+t{_+jsZw9KnIhM(30;IJi$U#-^C8Rwa1Jp#S81(jp{06{h#w?$Z6lL~}qR zcFZIT#dk1K%8C{So&4`>Ts}sZ$r$sAp_BInI0E z*tY|7u%P!XChw(%DN4{>wX4J1a@-ujkss$|+ZC?M zq3_=3-#p*k(6FWfnMEG6y$8|S4*58?sa^|s8cL`?(N)(>5s@wFqKX~+iXVX6$&T@d zwXX`V7N;vx_?PKt!k}*9h^~#Dcv7V)G+OXY5bMtYPSgE|w|EGoM)e_7%`Vfu5APx^ zfd~d#(<12Pla$(IUgPBrnI!37vq?7@t0K%`_~fb6Ksxh#GKCams_l-Mw1mg=G6OxoLI>7&KZ5GSBGWsrPo&_O9b~9)E;S6hOZC5FIYLTeHpVi6kwW&UfCYI(_H`H?{BY&UE&wip1b6h9}>w4 z8dECdH4>$&6LN5PLy6E!duB^(#jEruutqX+=bkbP?j>q3ww!}cx#CFqdhyP%9Pxa&o0_;nWVQaNfN$Bt)(5SNQD8 zSa=USm7c8*VP348zOICeiyRfW`3fGiC(lOB>$G1xhu2}b&_}&_`R+X`9^=NlbQiIn>G(=?YK(mSqyw`~UK2m%#Hi#n zm7RPsgF;V=^-%JYvy8seKi%jO>(CYW6+DNyG)A5Tx_R8T*iMRj2sc2_fl_1Jz(?y? zC6PzQ>G73CrtRjP)#Day=9e0RP2m-;<#}ib z&$@d)J5oA@mmByE4$h?jF_GFG2&tofSvInY!Wo5bl$6=N|6jBY`a*@9TsdjGSvM(h zk1rhLM=sT{bTeGp52VV^PD`ruEx*XgY13Y4EaRVxE)lwVatayOw}+RGEzy2Rx!F$l zDx5P|@qFSbZerj)`%rd^B=G{{@!VC>L*b!0Oxplm>*X6gF1~i6Srfr#N*P17T5T}h zIViqy&4Y{C8gk{BvqPi01LFxgp>ulF)Aan$^a%08HFoA=Y@NZZ^vF`XNPR#4BIFa? zM~i8YCQ3_WRqz)!`~i2COHFxX7`zt>2W$i@*f$7O|0UkPXH@$i4*2^oM;~y9-JYR= zBz7P)>OavLGbFI3N&}uO2+}`$F5v%8!Rl?)uT-j0D(?Uh$HDr8{hkD6wql%cpc2Z4UZ!bx9fZbq<}# zbO1YYbsZXojr5FOJsaj9*Ogwe4c&aK4O;y-sVySsZ*d)a=e3Z!A{VoOYfAWhrD?u>3W8|CL)1V5{%9J_{9H8`H< zEL`x~W)>dyw&gIh4;+IG6IHwKQ%LaCiqC-H!$sGSl|f8sCF&^ zS1#%r2ElJa=$BdM9YFjFm+`P*8wKMeROAEx&P>IO#xGgz1zrGy0Ud>;XD!f$A}&XL z9d;O&3ug#)4g%}kBb%8hV~VTPHDK_`l8I6x+V01|7J6H zckt*-MD>vM!I2Uv#VzldH^4-%Vd{Rf9|v*d*}DlF9fbbmM>gO@PgdlM0DX${4?jB< zt3$y?iZ8w18_U5)79$T!%13v_txWr`99`s7V(ANxMZe(YVkt^!+P@nGu2lpKOp8mo ze!>ZS>5~|BYHF}gxpm`U_-m255Axj6PSB0eFPzKwiXd2o6Fu8UJQ*bJnukXt@uo3c zD3?)(K)5i0TfV``to%}$!fr7o|9;f@B}6|2>nEOq<)}8&2I_MR?aB6^jv5CnzW3U@s9inxnJXz+nLIU z93gOvfM{0Qoi-9@Hs-C^f9w{(6!55f_x$(zXlh4paWP*N%rI_M**&TrR`)rA4py=N zNnGT*q=vQNqs;}6Mgm7Z2%`qy0#VsdbmM;guRE+)p9USw&3C(rV58W z2Ojt7gt@xSWpGCfnc3T)AA@}Q*_Ss3lo+o|EBTlVSQn_1a*=OMW^- zbwBhqIe-z$3Wl!8F0EZ5A{ahE&tcxZbcxl)^y$OXwMLR7abYL0){vT4patZ={v>;Y z_p;y_MiBlVSwD zKNNo6P>MbgX8q;5z(|HDr4$8IZ;oo~IG%y-r(!iKVoj{+^FJSGD6rn`29+qjHsj1^ z^z3->|ByJA3J&AGIYoWRtrTAAkmPN`LjB^CcVpfvSwg@BfCTf&B;JdEgUbVskJXuT<{wOPvHt_xmBFk4 diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/script.dat b/fle/cluster/factorio-server/scenarios/default_lab_scenario/script.dat deleted file mode 100644 index 6ed02ae17325594e0b2647a9296391e92b33cc71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1441 zcmb_c!EVz)5Vh0YHcCiSL6CYtqDWkQpf@hmH$G#vu_x}T>s@PiorLlO`~v&|7fxLG z9R7emfalmr8=6E#!cro8X6MbDH!~YYabzMqF@9Xrob>Ncy!q_xe8@(bBN-?cDp2Y9 zF&ijnO;TwXkDAyhx=0yYSetjBzpmJErF^jZ&TuwUfN^C~%0k{MvkrZovS-uU8FERf zj50-%JEiXhj(}q*ncL29VPx(p5_0<9OO3GDTvcX)sRyg0)3iLgie%-CX=$F}>=?5N%gwNZRRbm}jNEFK+SPzKFINdc zSp!E)9|T77Aq0S5>^k`uU{k*`!C3*4$h0Co-nLw#j$vV?MjD4JkH>d3M~yvM-LTeC F{0E) None: + ... + + def stop(self) -> None: + ... + + def restart(self) -> None: + ... + + def hot_reload(self) -> None: + ... + + +class DockerServerController: + """ + Concrete ServerController wrapping the Docker-based FactorioClusterManager. + """ + def __init__( + self, + docker_platform: str, + config: PlatformConfig, + num_instances: int, + dry_run: bool = False, + ): + # Mirror the mode so consumers can branch behavior + self.mode = config.mode + # Underlying cluster manager handles actual Docker orchestration + self._mgr = FactorioClusterManager(docker_platform, config, num_instances, dry_run) + + def start(self) -> None: + """Ensure image is available and launch all Factorio containers.""" + asyncio.run(self._mgr.start()) + + def stop(self) -> None: + """Gracefully stop and remove all Factorio containers.""" + asyncio.run(self._mgr.stop()) + + def restart(self) -> None: + """Restart all running Factorio containers.""" + asyncio.run(self._mgr.restart()) + + def hot_reload(self) -> None: + """ + Sync scenario files into running containers for a hot-reload. + Only available in "scenario" mode. + """ + if self.mode != Mode.SCENARIO.value: + raise RuntimeError("Hot-reload only supported in scenario mode") + asyncio.run(self._mgr.hot_reload_scenario()) \ No newline at end of file From f48bc221def5ac9f06abecd00a5ac02afe27d0b1 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 20:39:31 +0530 Subject: [PATCH 25/53] api change --- tests/benchmarks/test_demo_video.py | 2 +- tests/blueprints/test_save_load.py | 4 ++-- tests/connect/test_connect_transport_belts.py | 4 ++-- tests/connect/test_connect_underground_belts.py | 4 ++-- tests/connect/test_connect_underground_pipes.py | 4 ++-- tests/entities/test_assemblers.py | 2 +- tests/entities/test_drill.py | 2 +- tests/entities/test_fluid_processors.py | 2 +- tests/entities/test_inserters.py | 2 +- tests/entities/test_nuclear_reactor.py | 2 +- tests/entities/test_power_generators.py | 2 +- tests/entities/test_rockets.py | 2 +- tests/functional/test_auto_refilling_coal.py | 4 ++-- tests/functional/test_chemical_plants.py | 2 +- tests/functional/test_defence.py | 4 ++-- tests/functional/test_electricity_unit.py | 2 +- tests/functional/test_multiple_drills_multiple_furnaces.py | 2 +- tests/functional/test_small_iron_factory.py | 1 - 18 files changed, 23 insertions(+), 24 deletions(-) diff --git a/tests/benchmarks/test_demo_video.py b/tests/benchmarks/test_demo_video.py index da15eed51..8f1ebd736 100644 --- a/tests/benchmarks/test_demo_video.py +++ b/tests/benchmarks/test_demo_video.py @@ -40,7 +40,7 @@ # #cache_scripts=False, # inventory={}) -# instance.speed(10) +# instance.set_speed(10) # time.sleep(5) # profit_config = {"max_static_unit_profit_cap": 5, # "dynamic_profit_multiplier": 10} diff --git a/tests/blueprints/test_save_load.py b/tests/blueprints/test_save_load.py index 567488215..eea7747ee 100644 --- a/tests/blueprints/test_save_load.py +++ b/tests/blueprints/test_save_load.py @@ -89,13 +89,13 @@ def test_save_load1(game): game.insert_item(Prototype.Coal, furnace, quantity=5) game.insert_item(Prototype.IronOre, furnace, quantity=5) game.move_to(Position(x=20, y=20)) - game.instance.speed(1) + game.instance.set_speed(1) entities = game._save_entity_state(distance=30, player_entities=True) copied_entities = deepcopy(entities) game.instance.reset() assert game._load_entity_state(entities) entities = game._save_entity_state(distance=30, player_entities=True) - game.instance.speed(1) + game.instance.set_speed(1) assert ( copied_entities[0]["burner"]["inventory"]["coal"] == entities[0]["burner"]["inventory"]["coal"] diff --git a/tests/connect/test_connect_transport_belts.py b/tests/connect/test_connect_transport_belts.py index c17b83249..7a60a60c0 100644 --- a/tests/connect/test_connect_transport_belts.py +++ b/tests/connect/test_connect_transport_belts.py @@ -21,10 +21,10 @@ def game(instance): "wooden-chest": 5, "assembling-machine": 10, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace - instance.speed(10) + instance.set_speed(10) # instance.reset() diff --git a/tests/connect/test_connect_underground_belts.py b/tests/connect/test_connect_underground_belts.py index b3a5a7328..43f2985f1 100644 --- a/tests/connect/test_connect_underground_belts.py +++ b/tests/connect/test_connect_underground_belts.py @@ -19,10 +19,10 @@ def game(instance): "wooden-chest": 5, "assembling-machine": 10, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace - instance.speed(10) + instance.set_speed(10) # instance.reset() diff --git a/tests/connect/test_connect_underground_pipes.py b/tests/connect/test_connect_underground_pipes.py index 0c3a0ba2d..49fa6988f 100644 --- a/tests/connect/test_connect_underground_pipes.py +++ b/tests/connect/test_connect_underground_pipes.py @@ -20,10 +20,10 @@ def game(instance): "assembling-machine-1": 10, "pipe-to-ground": 4, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace - instance.speed(10) + instance.set_speed(10) # instance.reset() diff --git a/tests/entities/test_assemblers.py b/tests/entities/test_assemblers.py index 621af45cb..6657c5b30 100644 --- a/tests/entities/test_assemblers.py +++ b/tests/entities/test_assemblers.py @@ -19,7 +19,7 @@ def game(instance): "stone-brick": 20, "iron-ore": 20, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace diff --git a/tests/entities/test_drill.py b/tests/entities/test_drill.py index 39d1168a0..faf424856 100644 --- a/tests/entities/test_drill.py +++ b/tests/entities/test_drill.py @@ -21,7 +21,7 @@ def game(instance): "copper-plate": 100, "electronic-circuit": 100, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace diff --git a/tests/entities/test_fluid_processors.py b/tests/entities/test_fluid_processors.py index bfb628f38..2d7fbb0cf 100644 --- a/tests/entities/test_fluid_processors.py +++ b/tests/entities/test_fluid_processors.py @@ -30,7 +30,7 @@ def game(instance): "copper-plate": 50, "pumpjack": 1, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace diff --git a/tests/entities/test_inserters.py b/tests/entities/test_inserters.py index c25ab2966..b46aff7d6 100644 --- a/tests/entities/test_inserters.py +++ b/tests/entities/test_inserters.py @@ -18,7 +18,7 @@ def game(instance): "copper-plate": 100, "electronic-circuit": 100, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace diff --git a/tests/entities/test_nuclear_reactor.py b/tests/entities/test_nuclear_reactor.py index bc850d05f..2f01e3551 100644 --- a/tests/entities/test_nuclear_reactor.py +++ b/tests/entities/test_nuclear_reactor.py @@ -20,7 +20,7 @@ def game(instance): "inserter": 5, "iron-chest": 5, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace diff --git a/tests/entities/test_power_generators.py b/tests/entities/test_power_generators.py index bbe65a18c..f59b47277 100644 --- a/tests/entities/test_power_generators.py +++ b/tests/entities/test_power_generators.py @@ -13,7 +13,7 @@ def game(instance): "steam-engine": 3, "small-electric-pole": 4, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace diff --git a/tests/entities/test_rockets.py b/tests/entities/test_rockets.py index e996ad2c1..75867fbfa 100644 --- a/tests/entities/test_rockets.py +++ b/tests/entities/test_rockets.py @@ -23,7 +23,7 @@ def game(instance): "rocket-fuel": 200, "low-density-structure": 200, } - instance.speed(10) + instance.set_speed(10) instance.reset() yield instance.namespace diff --git a/tests/functional/test_auto_refilling_coal.py b/tests/functional/test_auto_refilling_coal.py index 44e1e5d20..68e0597ee 100644 --- a/tests/functional/test_auto_refilling_coal.py +++ b/tests/functional/test_auto_refilling_coal.py @@ -15,10 +15,10 @@ def game(instance): "burner-mining-drill": 5, } instance.reset() - instance.speed(10) + instance.set_speed(10) yield instance.namespace # instance.reset() - # instance.speed(1) + # instance.set_speed(1) def test_build_auto_refilling_coal_system(game): diff --git a/tests/functional/test_chemical_plants.py b/tests/functional/test_chemical_plants.py index 022c84d9c..2a1ee2adf 100644 --- a/tests/functional/test_chemical_plants.py +++ b/tests/functional/test_chemical_plants.py @@ -22,7 +22,7 @@ def base_game(instance): "burner-mining-drill": 5, } instance.reset() - instance.speed(10) + instance.set_speed(10) yield instance.namespace diff --git a/tests/functional/test_defence.py b/tests/functional/test_defence.py index dd7345548..bc07fcc48 100644 --- a/tests/functional/test_defence.py +++ b/tests/functional/test_defence.py @@ -17,10 +17,10 @@ def game(instance): "burner-mining-drill": 5, } instance.reset() - instance.speed(10) + instance.set_speed(10) yield instance.namespace instance.reset() - instance.speed(1) + instance.set_speed(1) def test_defence(game): diff --git a/tests/functional/test_electricity_unit.py b/tests/functional/test_electricity_unit.py index 2de409925..ac1df8859 100644 --- a/tests/functional/test_electricity_unit.py +++ b/tests/functional/test_electricity_unit.py @@ -29,7 +29,7 @@ def game(instance): } instance.reset() - instance.speed(10) + instance.set_speed(10) yield instance.namespace diff --git a/tests/functional/test_multiple_drills_multiple_furnaces.py b/tests/functional/test_multiple_drills_multiple_furnaces.py index ca5d625a7..831157600 100644 --- a/tests/functional/test_multiple_drills_multiple_furnaces.py +++ b/tests/functional/test_multiple_drills_multiple_furnaces.py @@ -28,7 +28,7 @@ def game(instance): "assembling-machine-1": 5, } instance.reset() - instance.speed(10) + instance.set_speed(10) yield instance.namespace diff --git a/tests/functional/test_small_iron_factory.py b/tests/functional/test_small_iron_factory.py index 6836e1e57..7cd3139a9 100644 --- a/tests/functional/test_small_iron_factory.py +++ b/tests/functional/test_small_iron_factory.py @@ -14,7 +14,6 @@ def game(instance): "burner-mining-drill": 10, } instance.reset() - instance.execute_transaction() yield instance.namespace From b2844241cd9d9dc0a02ae5a68786cb63dd531987 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 20:40:08 +0530 Subject: [PATCH 26/53] minor changes --- fle/env/instance.py | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/fle/env/instance.py b/fle/env/instance.py index afa7d7382..768d18922 100644 --- a/fle/env/instance.py +++ b/fle/env/instance.py @@ -1,38 +1,37 @@ import atexit import enum import functools -import importlib import inspect import json import os -import shutil import signal import threading -import time import traceback import types -from contextlib import contextmanager +import uuid from concurrent.futures import TimeoutError +from contextlib import contextmanager from pathlib import Path from timeit import default_timer as timer -from typing_extensions import Optional, List, Dict, Any, Tuple -import uuid from dotenv import load_dotenv +from factorio_rcon import RCONClient from slpp import slpp as lua +from typing_extensions import Any, Dict, List, Optional, Tuple -from fle.env.entities import BoundingBox -from fle.env.utils.camera import Camera - +from fle.commons.models.game_state import GameState +from fle.commons.models.research_state import ResearchState from fle.env.lua_manager import LuaScriptManager, ToolHookRegistry from fle.env.namespace import FactorioNamespace -from fle.env.utils.rcon import _lua2python, _get_dir -from fle.commons.models.research_state import ResearchState -from factorio_rcon import RCONClient -from fle.commons.models.game_state import GameState from fle.env.utils.controller_loader.system_prompt_generator import ( SystemPromptGenerator, ) +from fle.env.utils.rcon import _lua2python + +from fle.cluster.factorio_server.run_envs import PlatformConfig + +PLATFORM_CONFIG = PlatformConfig() + CHUNK_SIZE = 32 MAX_SAMPLES = 5000 @@ -124,7 +123,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): class FactorioServer: - def __init__(self, address, tcp_port, cache_scripts=True): + def __init__(self, address, tcp_port = PLATFORM_CONFIG.rcon_port, cache_scripts=True): self.rcon_client, self.address = self.connect_to_server(address, tcp_port) self.cache_scripts = cache_scripts self.tool_hook_registry = ToolHookRegistry() @@ -139,7 +138,7 @@ def __init__(self, address, tcp_port, cache_scripts=True): def ensure_rcon_client(self): if not self.rcon_client: - self.rcon_client = RCONClient(self.address, self.tcp_port, "factorio") + self.rcon_client = RCONClient(self.address, self.tcp_port, PLATFORM_CONFIG.factorio_password) return self.rcon_client def run_rcon_print(self, command: str): @@ -159,11 +158,11 @@ def load_tool_into_game(self, name): def connect_to_server(self, address, tcp_port): try: - rcon_client = RCONClient(address, tcp_port, "factorio") + rcon_client = RCONClient(address, tcp_port, PLATFORM_CONFIG.factorio_password) address = address except ConnectionError as e: print(e) - rcon_client = RCONClient("localhost", tcp_port, "factorio") + rcon_client = RCONClient("localhost", tcp_port, PLATFORM_CONFIG.factorio_password) address = "localhost" print(f"Connected to {address} client at tcp/{tcp_port}.") @@ -292,7 +291,7 @@ def is_multiagent(self): return self.num_agents > 1 def set_speed(self, speed): - self.rcon_client.send_command(f"/sc game.speed = {speed}") + self.server.rcon_client.send_command(f"/sc game.speed = {speed}") self._speed = speed def get_speed(self): From 743921a85a5c1950410e1bce3207d7807f1addbb Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 30 Jul 2025 10:36:32 +0530 Subject: [PATCH 27/53] moved factorio_server out --- fle/env/__init__.py | 2 + fle/env/factorio_server.py | 165 +++++++++++++++++++++++++++++++++++++ fle/env/instance.py | 164 +----------------------------------- 3 files changed, 171 insertions(+), 160 deletions(-) create mode 100644 fle/env/factorio_server.py diff --git a/fle/env/__init__.py b/fle/env/__init__.py index e0045d669..2989782d4 100644 --- a/fle/env/__init__.py +++ b/fle/env/__init__.py @@ -3,6 +3,7 @@ from fle.env.entities import * # noqa from fle.env.game_types import Prototype, Resource from fle.env.instance import DirectionInternal, FactorioInstance +from fle.env.factorio_server import FactorioServer __all__ = [ "FactorioInstance", @@ -14,4 +15,5 @@ "EntityGroup", "Prototype", "Resource", + "FactorioServer", ] diff --git a/fle/env/factorio_server.py b/fle/env/factorio_server.py new file mode 100644 index 000000000..e699bb8c0 --- /dev/null +++ b/fle/env/factorio_server.py @@ -0,0 +1,165 @@ +import asyncio +import threading +from contextlib import contextmanager +from time import time as timer + +from factorio_rcon import RCONClient +from slpp import slpp as lua +from typing_extensions import Any, Dict, List, Tuple + +from fle.cluster.factorio_server.run_envs import PlatformConfig +from fle.env.lua_manager import LuaScriptManager, ToolHookRegistry +from fle.env.namespace import FactorioNamespace +from fle.env.utils.rcon import _lua2python + +PLATFORM_CONFIG = PlatformConfig() + +class FactorioTransaction: + def __init__(self, server): + self.server = server + self.commands: List[Tuple[str, List[Any], bool]] = ( + [] + ) # (command, parameters, is_raw) + + def add_command(self, command: str, *parameters, raw=False): + self.commands.append((command, list(parameters), raw)) + + def clear(self): + self.commands.clear() + + def get_commands(self): + return self.commands + + def execute(self) -> Dict[str, Any]: + """Execute all commands in this transaction""" + start = timer() + rcon_commands = {} + + for idx, (command, parameters, is_raw) in enumerate(self.commands): + if is_raw: + rcon_commands[f"{idx}_{command}"] = command + else: + script = self.server._get_command( + command, parameters=parameters, measured=False + ) + rcon_commands[f"{idx}_{command}"] = script + + lua_responses = self.server.rcon_client.send_commands(rcon_commands) + + results = {} + for command, response in lua_responses.items(): + results[command] = _lua2python(command, response, start=start) + + self.clear() + return results + + @contextmanager + def __enter__(self): + """Context manager entry - clear any existing commands""" + self.clear() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Context manager exit - execute the transaction""" + if exc_type is None: # Only execute if no exception occurred + self.execute() + + +class FactorioServer: + def __init__(self, address, tcp_port = PLATFORM_CONFIG.rcon_port, cache_scripts=True): + self.rcon_client, self.address = self.connect_to_server(address, tcp_port) + self.cache_scripts = cache_scripts + self.tool_hook_registry = ToolHookRegistry() + # Initialize Lua script manager and script dictionary + self.lua_script_manager = LuaScriptManager( + self.rcon_client, self.tool_hook_registry, cache_scripts + ) + self.script_dict = { + **self.lua_script_manager.lib_scripts, + **self.lua_script_manager.tool_scripts, + } + + def ensure_rcon_client(self): + if not self.rcon_client: + self.rcon_client = RCONClient(self.address, self.tcp_port, PLATFORM_CONFIG.factorio_password) + return self.rcon_client + + def run_rcon_print(self, command: str): + return self.rcon_client.send_command(f"/sc rcon.print({command})") + + def send_command(self, command: str): + return self.rcon_client.send_command(command) + + def load_init_into_game(self, init_scripts): + if type(init_scripts) == str: + init_scripts = [init_scripts] + for script_name in init_scripts: + self.lua_script_manager.load_init_into_game(script_name) + + def load_tool_into_game(self, name): + self.lua_script_manager.load_tool_into_game(name) + + def connect_to_server(self, address, tcp_port): + try: + rcon_client = RCONClient(address, tcp_port, PLATFORM_CONFIG.factorio_password) + address = address + except ConnectionError as e: + print(e) + rcon_client = RCONClient("localhost", tcp_port, PLATFORM_CONFIG.factorio_password) + address = "localhost" + + print(f"Connected to {address} client at tcp/{tcp_port}.") + return rcon_client, address + + def _get_command(self, command, parameters=[], measured=True): + prefix = "/sc " if not measured else "/command " + if command in self.script_dict: + script = prefix + self.script_dict[command] + for index in range(len(parameters)): + script = script.replace( + f"arg{index + 1}", lua.encode(parameters[index]) + ) + else: + script = command + return script + + @contextmanager + def transaction(self): + """Context manager for executing transactions""" + tx = FactorioTransaction(self) + try: + yield tx + except: + # On error, do not execute queued commands + raise + else: + # Only execute if no exception occurred + tx.execute() + + def register_controllers( + self, namespaces: List[FactorioNamespace], invalidate_cache: bool = False + ): + """ + Load Python controllers from valid tool directories (delegated). + """ + self.lua_script_manager.register_controllers(self, namespaces, invalidate_cache) + + def cleanup(self): + # Close the RCON connection + if hasattr(self, "rcon_client") and self.rcon_client: + self.rcon_client.close() + + self.tool_hook_registry = ToolHookRegistry() + + # Join all non-daemon threads + for thread in threading.enumerate(): + if ( + thread != threading.current_thread() + and thread.is_alive() + and not thread.daemon + ): + try: + thread.join(timeout=5) # Wait up to 5 seconds for each thread + except Exception as e: + print(f"Error joining thread {thread.name}: {e}") + diff --git a/fle/env/instance.py b/fle/env/instance.py index 768d18922..80a864c3f 100644 --- a/fle/env/instance.py +++ b/fle/env/instance.py @@ -5,33 +5,26 @@ import json import os import signal -import threading import traceback import types import uuid from concurrent.futures import TimeoutError -from contextlib import contextmanager + from pathlib import Path from timeit import default_timer as timer from dotenv import load_dotenv -from factorio_rcon import RCONClient -from slpp import slpp as lua -from typing_extensions import Any, Dict, List, Optional, Tuple +from typing_extensions import Any, Dict, List, Optional from fle.commons.models.game_state import GameState from fle.commons.models.research_state import ResearchState -from fle.env.lua_manager import LuaScriptManager, ToolHookRegistry from fle.env.namespace import FactorioNamespace from fle.env.utils.controller_loader.system_prompt_generator import ( SystemPromptGenerator, ) +from fle.env.factorio_server import FactorioServer from fle.env.utils.rcon import _lua2python -from fle.cluster.factorio_server.run_envs import PlatformConfig - -PLATFORM_CONFIG = PlatformConfig() - CHUNK_SIZE = 32 MAX_SAMPLES = 5000 @@ -71,160 +64,11 @@ def from_factorio_direction(cls, direction): return direction.value * 2 -class FactorioTransaction: - def __init__(self, server): - self.server = server - self.commands: List[Tuple[str, List[Any], bool]] = ( - [] - ) # (command, parameters, is_raw) - - def add_command(self, command: str, *parameters, raw=False): - self.commands.append((command, list(parameters), raw)) - - def clear(self): - self.commands.clear() - - def get_commands(self): - return self.commands - - def execute(self) -> Dict[str, Any]: - """Execute all commands in this transaction""" - start = timer() - rcon_commands = {} - - for idx, (command, parameters, is_raw) in enumerate(self.commands): - if is_raw: - rcon_commands[f"{idx}_{command}"] = command - else: - script = self.server._get_command( - command, parameters=parameters, measured=False - ) - rcon_commands[f"{idx}_{command}"] = script - - lua_responses = self.server.rcon_client.send_commands(rcon_commands) - - results = {} - for command, response in lua_responses.items(): - results[command] = _lua2python(command, response, start=start) - - self.clear() - return results - - @contextmanager - def __enter__(self): - """Context manager entry - clear any existing commands""" - self.clear() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - """Context manager exit - execute the transaction""" - if exc_type is None: # Only execute if no exception occurred - self.execute() - - -class FactorioServer: - def __init__(self, address, tcp_port = PLATFORM_CONFIG.rcon_port, cache_scripts=True): - self.rcon_client, self.address = self.connect_to_server(address, tcp_port) - self.cache_scripts = cache_scripts - self.tool_hook_registry = ToolHookRegistry() - # Initialize Lua script manager and script dictionary - self.lua_script_manager = LuaScriptManager( - self.rcon_client, self.tool_hook_registry, cache_scripts - ) - self.script_dict = { - **self.lua_script_manager.lib_scripts, - **self.lua_script_manager.tool_scripts, - } - - def ensure_rcon_client(self): - if not self.rcon_client: - self.rcon_client = RCONClient(self.address, self.tcp_port, PLATFORM_CONFIG.factorio_password) - return self.rcon_client - - def run_rcon_print(self, command: str): - return self.rcon_client.send_command(f"/sc rcon.print({command})") - - def send_command(self, command: str): - return self.rcon_client.send_command(command) - - def load_init_into_game(self, init_scripts): - if type(init_scripts) == str: - init_scripts = [init_scripts] - for script_name in init_scripts: - self.lua_script_manager.load_init_into_game(script_name) - - def load_tool_into_game(self, name): - self.lua_script_manager.load_tool_into_game(name) - - def connect_to_server(self, address, tcp_port): - try: - rcon_client = RCONClient(address, tcp_port, PLATFORM_CONFIG.factorio_password) - address = address - except ConnectionError as e: - print(e) - rcon_client = RCONClient("localhost", tcp_port, PLATFORM_CONFIG.factorio_password) - address = "localhost" - - print(f"Connected to {address} client at tcp/{tcp_port}.") - return rcon_client, address - - def _get_command(self, command, parameters=[], measured=True): - prefix = "/sc " if not measured else "/command " - if command in self.script_dict: - script = prefix + self.script_dict[command] - for index in range(len(parameters)): - script = script.replace( - f"arg{index + 1}", lua.encode(parameters[index]) - ) - else: - script = command - return script - - @contextmanager - def transaction(self): - """Context manager for executing transactions""" - tx = FactorioTransaction(self) - try: - yield tx - except: - # On error, do not execute queued commands - raise - else: - # Only execute if no exception occurred - tx.execute() - - def register_controllers( - self, namespaces: List["FactorioNamespace"], invalidate_cache: bool = False - ): - """ - Load Python controllers from valid tool directories (delegated). - """ - self.lua_script_manager.register_controllers(self, namespaces, invalidate_cache) - - def cleanup(self): - # Close the RCON connection - if hasattr(self, "rcon_client") and self.rcon_client: - self.rcon_client.close() - - self.tool_hook_registry = ToolHookRegistry() - - # Join all non-daemon threads - for thread in threading.enumerate(): - if ( - thread != threading.current_thread() - and thread.is_alive() - and not thread.daemon - ): - try: - thread.join(timeout=5) # Wait up to 5 seconds for each thread - except Exception as e: - print(f"Error joining thread {thread.name}: {e}") - - class FactorioInstance: namespace_class = FactorioNamespace _cleanup_registered = False # Only register cleanup once per process namespaces: List[FactorioNamespace] + server: FactorioServer def __init__( self, From caff3c88d7e4bb9a9f55e1ab815801f1070d35a8 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Wed, 30 Jul 2025 11:21:36 +0530 Subject: [PATCH 28/53] moved run_envs to fle/env --- .../factorio_server/server_controller.py | 63 ---- fle/cluster/local/__init__.py | 0 .../factorio}/config/map-gen-settings.json | 0 .../factorio}/config/map-settings.json | 0 .../factorio}/config/rconpw | 0 .../factorio}/config/server-settings.json | 0 .../default_lab_scenario/blueprint.zip | Bin 0 -> 215969 bytes .../default_lab_scenario/control2.lua | 81 +++++ .../default_lab_scenario/description.json | 5 + .../default_lab_scenario/freeplay.lua | 299 ++++++++++++++++++ .../scenarios/default_lab_scenario/info.json | 1 + .../locale/en/freeplay.cfg | 4 + .../locale/en/level-01-locale.cfg | 71 +++++ .../default_lab_scenario/paperclips.lua | 81 +++++ .../default_lab_scenario/planet1.png | Bin 0 -> 505684 bytes .../scenarios/default_lab_scenario/script.dat | Bin 0 -> 1441 bytes .../factorio/scenarios/open_world/README.md | 1 + fle/env/factorio_server.py | 2 +- .../factorio_server => env}/run_envs.py | 55 +++- 19 files changed, 594 insertions(+), 69 deletions(-) delete mode 100644 fle/cluster/factorio_server/server_controller.py create mode 100644 fle/cluster/local/__init__.py rename fle/{cluster/factorio_server => env/factorio}/config/map-gen-settings.json (100%) rename fle/{cluster/factorio_server => env/factorio}/config/map-settings.json (100%) rename fle/{cluster/factorio_server => env/factorio}/config/rconpw (100%) rename fle/{cluster/factorio_server => env/factorio}/config/server-settings.json (100%) create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/blueprint.zip create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/control2.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/description.json create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/freeplay.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/info.json create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/locale/en/freeplay.cfg create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/paperclips.lua create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/planet1.png create mode 100644 fle/env/factorio/scenarios/default_lab_scenario/script.dat create mode 100644 fle/env/factorio/scenarios/open_world/README.md rename fle/{cluster/factorio_server => env}/run_envs.py (86%) diff --git a/fle/cluster/factorio_server/server_controller.py b/fle/cluster/factorio_server/server_controller.py deleted file mode 100644 index 30f661cb7..000000000 --- a/fle/cluster/factorio_server/server_controller.py +++ /dev/null @@ -1,63 +0,0 @@ - - -import asyncio -from typing import Protocol, Literal - -from .run_envs import FactorioClusterManager, PlatformConfig, Mode - - -class ServerController(Protocol): - """ - Protocol defining the interface for managing a Factorio server instance. - """ - mode: Literal["scenario", "save-based"] - - def start(self) -> None: - ... - - def stop(self) -> None: - ... - - def restart(self) -> None: - ... - - def hot_reload(self) -> None: - ... - - -class DockerServerController: - """ - Concrete ServerController wrapping the Docker-based FactorioClusterManager. - """ - def __init__( - self, - docker_platform: str, - config: PlatformConfig, - num_instances: int, - dry_run: bool = False, - ): - # Mirror the mode so consumers can branch behavior - self.mode = config.mode - # Underlying cluster manager handles actual Docker orchestration - self._mgr = FactorioClusterManager(docker_platform, config, num_instances, dry_run) - - def start(self) -> None: - """Ensure image is available and launch all Factorio containers.""" - asyncio.run(self._mgr.start()) - - def stop(self) -> None: - """Gracefully stop and remove all Factorio containers.""" - asyncio.run(self._mgr.stop()) - - def restart(self) -> None: - """Restart all running Factorio containers.""" - asyncio.run(self._mgr.restart()) - - def hot_reload(self) -> None: - """ - Sync scenario files into running containers for a hot-reload. - Only available in "scenario" mode. - """ - if self.mode != Mode.SCENARIO.value: - raise RuntimeError("Hot-reload only supported in scenario mode") - asyncio.run(self._mgr.hot_reload_scenario()) \ No newline at end of file diff --git a/fle/cluster/local/__init__.py b/fle/cluster/local/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/fle/cluster/factorio_server/config/map-gen-settings.json b/fle/env/factorio/config/map-gen-settings.json similarity index 100% rename from fle/cluster/factorio_server/config/map-gen-settings.json rename to fle/env/factorio/config/map-gen-settings.json diff --git a/fle/cluster/factorio_server/config/map-settings.json b/fle/env/factorio/config/map-settings.json similarity index 100% rename from fle/cluster/factorio_server/config/map-settings.json rename to fle/env/factorio/config/map-settings.json diff --git a/fle/cluster/factorio_server/config/rconpw b/fle/env/factorio/config/rconpw similarity index 100% rename from fle/cluster/factorio_server/config/rconpw rename to fle/env/factorio/config/rconpw diff --git a/fle/cluster/factorio_server/config/server-settings.json b/fle/env/factorio/config/server-settings.json similarity index 100% rename from fle/cluster/factorio_server/config/server-settings.json rename to fle/env/factorio/config/server-settings.json diff --git a/fle/env/factorio/scenarios/default_lab_scenario/blueprint.zip b/fle/env/factorio/scenarios/default_lab_scenario/blueprint.zip new file mode 100644 index 0000000000000000000000000000000000000000..e603e83157500bd58d9179383dfdfae913250b94 GIT binary patch literal 215969 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

    W+~ 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 z zZj?9ex}gE_&7(h7ggrJ;+gZBG&Nq8bEp2LzZ~t zKK;stN+)M`PbXveXKTg+G&_au)2+ybc|#i=_egY}U{IXcI_{+xYLJX0r0r~HH1E(B zZK8ayDr_uk8(D_~yuP>g{%3=}XjQC=&;!_C!Jju1{V{_=8jLek62Shec}-MAxaAb$ zfTqYAg0W=JtUg3Ca4c5fDjWv>n8E=La0fVu6{y$&Tm&GWC!}Bz1YSc}sO$3MR$YUO zAh^Gs2j#}$6YNA^)DSVCvuL(rTX{PjOV?`{{p-3O*C3yx*fhwwTC11Gw0p}H(cTXn zIe}rHW$3llnvO2Ja9a|AQzn4aZH*F8y08EU7!yhb5%FJ90ywA@#uU_8!AUQ+=F`H9RNPrRy4xymM)$9UQUkGbhF%oKYz~)qBX>KRN*?Kgz z8Ve_5dMKdBO%VxzEn1pcSX|CG=e4dW-d!eQ|K<6XX=^BdP!F^UW**c^bD7B-oGbd5 zzQYt2^KtwxfxcUa%aL;HaXd~#D-rkK<%=O>RFQIK1wDJx$QAtp6_ucLvRVAA=unV{ zGLV4?SG7}NAb(LJ==Jsl$~+CL@Fc<*y55SYiWOCaR=7ALA^eN51@v=r4m|;~1Z+UO z;~F6TYzR;)Spq1+@23*N@((pyL}Bj>K>|q0YLvK!21DKnEXV~hnB&t^haXHDrwIUmgXydPn>2tksz2`DVNY(;yXPBp7XIfYz%=rw z&&U8%7hrD@*tMwy@XQK;bOBHRle!-ez*GR_RRJUc5Wi1yKW6~`-N6gM|7ETI?CHVb zhcDyxy$9uIFB>obg5M_-fAVtGZa4B#4pe*RLo@cR)_Q~guOM<)gk8GXY zZ(4sH$2+z;&NyR7$>E`JKsiU?0EYvLoE@H|q63o_i*4EzK}oAIi;|}CLg#GK=*(`+ zbnRl3U98b7vrJtt)GX?SamHycOqNt#OpF(Lvpf9@e0`yHKGBx4t?M@ZeZJq%=lyv< z$ib%Y<_1U~;@W+*L;WC2wVZ~F2TixZ70Q2hW2}Cuf4nO%wLQPTlQxG`7EAivgkBvZ z`xD__xSMm43j`Ad;nu0r70_4fxn#o5#aHVFDE7EHS$}Z5-`alu<36f?$uMvWG0F85 zcOU_Yl%WlbAh1MDVE=}z^Zj!}j7ZJVE;+f}UzqZ?6Pu6It@*T3wn8FXc!%ZqQH%|q zDx)G+N>Xu$JT0m<)?k9k2C%<4Kx0@~Znf&R;1P-Oo?$-I{Ag{a*~` zo|LqG*MbpX?h7ZNZfVL0WS6sIYz(q-<9%yP1E{IeHJ{g5nI{=x70(2m6Z6UW4He3* z%0EnVLOwT425`|sx>-TdDDrHDbp>lXGyyTcHxiFACk0~hsSRj`7J!~VTFDS5QuN@i z0uPdQ;8Hl51RY4y+Or^_T5%8I{P1u9cyJ38SOK;mF$(o^s3^nZ6Wzk4Dy2+u`uyPw zkOr|34TUeqV$>nuPR#c>48sa$GanPtVG%3=a0-K(i0h!(0U#Tm$$mIl&Kdx63mF7v z&LE!9|0saoMgsB9#|-{E2LUdhIRNoKO#2Md6w#|OVYJZSR)`M-W}`}Xng(~pJw_w#nT-+BDFGhAkQ!!o*G8o>s* zvp`F|zIk8_+J*I@Yn1Elg5JsHdPA;BQ#-1`-_u~h)N7huF!VPkzc_-wKN_d>UpdB; z*GK#pHGh3?Gg1Ef*8KBC3zs}uB_smTo5TjupZ6MY%Gk2Gc8~8ckl~Do>V=P+!`Fgo zSo1eY`u=0zx4#3B4~iyu{#cNn(D|cD2Lm9LPPSG@IRHchn40tsz5;_n?e_?cX;5Ik zQ!EDKDR2R|f5RmxXh@Yt>$H;VhOe(%J)MI~ znD($cMBrx z^zvQ>XT7UJ;*Un?=6_g8jM0?wN|MJ=vHLiWP#0pWh}L_9gK7^U9gjd9oS zu?F(JwfuaJjw$CKjja9DWW;tv(+0WGxToO9(d(~vooIYTH_EGph*oO%oz;S0*HxT$ z*5>9_gVCA|XpDTr(Hu2hk%Igz`$vpDFeIMKCZbu1e+@f)ECGDX`Ej(-qWm-SaZd=?5ZCxWo~ z@nl7t&+$U#Gbdy&RW8v7P?Mtm;rbv85O~04UWiLQS>$PE3=kGG1CQBI5Sy_9`GWA- z#a~GxY~l>~&m_>dL4Z8-5D?SCh8;*i z#LVBS_v-DH*7lt{>xDb@k-kuWJ+bW-U1>eY<xJ9u$|T4NYunDbhGh*Ww(1f*t4GsH{u|x3l6h~ao1gAtXlteI7`Xp2+3tZZAt!hO3MLu`o)&u$V2_7w|>i z1>ge>06bv90-lBd%>n=g21pFxTNe;9{`2wcZ{0oo^|G<|YqEU5|Mana z`1@N=J9p{?dl>cg`tab;sk@!JYnYRR^}f5Fw}0F!JU@84u--WUAfz#9>()a4_Vu+~ z4jA1Xd}wIG{J`?EoKo_&U!A1PQOXdmG8p*Xxlf1$IrZ1?fT}-{uMvz%vdBa?jZrDB z_PV$EPu^7~__$z-nDbhbUBXGndfe|{uusPL0ySJMO?%Mk7c=o?kUvPw z+3h{u&g5lli)|#yfg}UuV?Nic)J@tS+Whr{J5Pn-N6Tw-ROl`hZrNaFOuU$3bJuTy zwdM1O0BKri#w~D&@wkD!H3iNGna7kVFcH*WNQ=F{;NQmmhDpPZ-gg>4m`gFAH>*W7 z0G4Y+D{{36go(Qk6vwjP=O{yRy%_r&5|aRzxL4G3QT7L1kOYWJ!DKNd#Bpr;s!Re> zKni&a3J7O7EZ70`{V_%jP7WrZh6Xk$42ZlMefj(B+{wxdyzh$faY*(&S- z5tvepal%9qRyB5mKD#i7eM5b&P{8jf?DbU^S{?hz`@fjWeexccg}E~5*>BsY;cq7bEY>nIf`>7l(L#r%da6&%kBm%yZdArhw_h+ZHqTx|LLmH+St=6&W5@-OIriW^qN6BZ(12mjgr zi^`rpp92FtQ}q9B19*P-UwUf+aCJKP3l8u*E?$EHG-dzHl0e=in7}-y5Ws8&NcoEm z0M|bp;Jb@!m&FB`cINlxz5{ao7Yx#-~QN`3jc z+Ay})jm^U2mSImUvz72K`Y&?h86ZKm8tKpZPMQ6;+Tkv2=1GXnCl>Oh*RShV>EyKG z4t(3p7tZ)16yiJh<1j4`$>u8NudPX6-pq%c&C&o9YQQIBD@k3FD zs1x2DXLvl8!Q^NyrgmU^H!~b zzH1gV;2cU83Q-YR8jW2+*O5}=nk12EQc5TC`2yVl&U}7Q$sGv*&`f!e3P~2Jee!Oq zQq5JZrji1~2qha@af<`78AG)fV@ANkH^}J%`9%U9u;w$u4S*Y8PHALqAp3QdK!cMc zqd0OwP2+Z|q+3avxs+s8K?INjNT88Qk)8q#9=XH*SUTIX=OgW~zdhITQQr~O%XcLM z028+%ct#c82at)w z-$={s0HFvOPO>r|ut51XhRA#r1h(Eesg72^AbaOR7hAq)7>@(xDv7|b17Q0{f}jBW zD>3q518Difo&bhNRUrWgzzOE$2|{%yVK_V|X;22kR6JKaLpsh!Z2BwH>I`Iw6X1(+ z|D)0~j0p|kM2{yBh42RpAr`t+iSV-mx61@2+Vtzm3lWY4akq0k%nDfqfKE$aFoMpz zduW6e&rBnTsd!lAoc~+#^Kp&?;`zg?nI8CD0&sb@2;eb&nk9l`0?Iq5fbamgKJ@{8 z*SoF>7l3I90B>g$K!yN80^)h*0)8d|6iGmL5APnd>G!#ouW6S3|LNhIKuXj=51 zZ2n!=`$UiozWB+qZtk2Gu^h&~mX_VfS-t(A{Os417i>MvAAd)FpmFOvX&2BA$8g61 zazo=(U+Ao^?;RdKc>4R}y$;D-O65U`v>shk6^kG&4SdVc4U5s+&#BkW6ZW6#j^*lB z!;-r})s0Ek&hAw0=Y=#RTdI)#5-?Jo2XT-m(2#``uNa6fpcH_WfL}nd%0E?Mh?UFj zR%;W!f1%F(1@Uf&VvRM-V!#CBH);F{GS37d^&lCPcj^A+Cf|b9BB^4gB*!o`r&DM^ zp|AM3=q2<#!YKgFpDdy8jrbLpl&|UEfxo^gd;-;cILdMpGj(*tAeTXr%k5XN455Ee zIKt)=0Vfbs`FoJ(-74mkB!)z)A9Z!lAhf~EF+b^DN`GvY80)keh&p5z5rZK=X2>=^_?>=3A z`t$%Mz#bpzmF0s%+uS<1Ww&txsh7=m-6#*rgLJ2kcUvdjN#nVxr8iYLAxAq4g~1>V z$uEDa4RCL|wNcwOI=yy!bCcRB<|zjYYYeBmzyG)KsnT*Mzj*z6C~bGx^NCj3T6p}t z4ZfapZVz|8o8FR%J(n@aX#k3={hpROIW1oEo2L`Tdq#Z}Gv@y8n=J%JC#l`xYv{t5 zcLn>OxO?YH+r)et&mns_EQx{KRn^rYWZ(%G?AA)RcJJm&8u`g2h3N!aKf7MWzJKp0 ze|kmMj^Mp4;DT$=>ARW&UELCwzsepzV5{j)v|hV?eE8_)uYdjO_>k((Rr+ftH)x8l+uVSLd<<^eNN7+%9nACv&Pq5mV* z<448JI~VdYqrhaV$VkxSvlQG>(+i1Brnl`)!>WQ2SSG@Jf5o(DDx%d~CP3Y42!6@m zFQ&YRnHv*AbTBY%b98UR zb)o*I_V>*dP`*JeZ`C#wL$Cr=?#YG4jas>mXj*l;Wwka(Ic6lsUx^3I#$2so8lCdg5XC=TLzK<#^I50 zJPG#~$y4QWRKy6yuSVjTNKBMyfPo?#L{$U}04t%8habY5!X}W|Afy7b>zr$zYKANv zn}PcgcmV&A5%xc#z}e`3{?li>Kj8%SFABhwtLI7Jyb$O=kC_$lwOI)0>+O!sx=KGP$%QuIvt#Pz z$uGJmJLGPm#+k4I*uG=K{K(Tg&)@mcyE;0ASxP2RH+QiXUALj$mCB`+GUEpZW5L}C z4F0Vqrt=H}#nH*soX?~@Ci{5k6zj(?{_yb2zfTbIGtQ}-b$1#7T9T2Sm&O_ZgJ$jM zi%T~@vRKw-Ldq1!vkh6llP9CdIR3+*`bBK@)9t+Kz-rYTxs+>KY&@EpN^Y;J#_c?} z;?UiDdiT+zyU!jy+A9NwhBj|&7;Vx!tmv{XWPrj5K;yumi9r{nYa{;F6L9shl0+xT z0HXoJDGHuEnm>gopCk}zURKQ0W|cOZX@G6>f|e8YPY2KtW`PaWU(L7Pk!UY0Y;Pu< zK~Mrelx$gn8BAbzIiO5-JcQ+_MT$NJw^<50GL=vHH^hO^kb^*r{D3t=0U`~S;*y{U z$~>_^R5Tbuf*~Mt)hF@!fi=wEjh~m1*ph?_L_k*(;HTq(fJ`SBoAVd^u#6ZV+-6WN z`h)qXg0WD+<6{Cy+#x95%rOPH=z>ashB_^nJEZf!=k^?g0q#BBbD@2+rq>Yun>9rm zDH}J#;wJUmg-oPajLj`z-!$)CHyj^Q+;(`r7&IdRYHM?ob!FzlJF}|mP5#((XW_z? zmaw|N_KtLdNm1hBLdSaQBBNRAn)!#OM=94mV?h0~w4E zHAo~#UIwlAn+C=?o2{~1U`e={-fpYb zreUiFAr51WbL)$~5#`v;GgpWdpMe*f#<7oO+H{(NvNS1;Gi@@cueuJ_x^ka2tK z>zZOfVQre$=Juw(&|Yts+vXKJoo=++dPWS@MnK`E^*~bp!&ov6Yiria#%(Ff~4~X$#O1!Vl^B4V?Jo-BTuics}eG zPK0LWs7gZ%ulUVL)9p52H#(pA`=7q`{kwno({G>=sjZd`8E~kWz;TWYN{_ZqGq*Mf z9K5--w%D6Tdtc&kzn-|mYE$w4_$R+zwpFKBa(dip6AcNmn{t7$v9RQRv`IKdAyt1z z=it%ZPrMM1XUA9}m(-Bj=itysEFF!0-*OoJaRY)3O$vBW#nD8~jT)&Yo?1AV68#^` zaWRAW6}=jigRKcgS@fHY)M+!IHk+dU=M&UFk6nrxy1))Jqb^vE5x%1ke{j{By-ERK~0t`NW7|;xnpo-pH&FsD6>?5sm;M3WA^n6DiMf zkj){S1ONg-3up%M3*OPD>ftKQJ#u1`IV6(-^i%z^PQXVtsf~sAenPN|Xb&{opN+Q` zyz+X1Br#mw(#d$#2jjO`Bqp>w*ys-2ku@v@N8PSg%MWthvR7ZCI+t!bayQb|{rZiv zVi;>Heu6gx@k%8iAxVzb)w%f=Sw{I@5~9q^v2T=T*31x3a;jDfz0B@;J%q!JAW z@f8$Sf)#LsW@8@1z<5|2R`_{g>*tANytMNT=J?oFBs8BCnPEVA2#c8lE`^vJDs%6| zIh^vQi3ne~6rlr&Cy@t|C7dK=AbjDiCFtw`m}P&R83Km>L-&7f{CA!Pz|+hH%yVV} z@NYtZvn&wd0z?}?@y|YZpN0U#>U1MH>9YAmC<-V`-qo!6&akNzU zkqjQFiRK{Kh=mHBL`5PinG+t5@vIj#*oUi6qW_IZJey7Dnn7t8K=U^qZNLhy8HNgj zs!O+4Q$`!zNPBUmBz3JWz0zP+HkO?7iXEB@W)bEoF(s3f;sk&w{+%5;7!F%ozvjER(J$3I$ojhC%Tm^G9fe z0vrHQFB?!!1aE`$AP}xx4#-Fc>GFlyAUng|-7vXfID0e$$sprE1^@s<@npEjaNubc z$pwpKuZBPbAp>AD&OJPH{vs}9K8rsfF``6@yfZ=`u?Q0ejhIG%u=WjTKu~jrHY&!$-%z{`IqG4}bRX z%U^!=;?U4(5TGhAmsP$|RJC1^1G?EUiX0m;nLJ(0udo+T;Ri!Sw5k$*nHKkHQ_KM< zgw@7yi`8_|8nWo~7f9O^_rISaQ`^(>gX zCA9zJ-9{4wF~$jCe@F&A1cwOCp~asqRk_~9w{J2RphaqgeAIB-mH_*u0>vDZNC+w9 z{a#Dr@8_k=%|?*J2a|G^>Edt1m%6xYDpJy{2BHCA1H{MCwi-}#7Dt9R@AD8&E-7ln z6Zf^gaPgWiDmTCx$b);*9blKHmsVB`t)!&7_neZKG>~z4^K%0u+res0fA-Cgr#nR6LhS3axNrz#E#sEHXmC z{K5XiWRh||ATr=3VuH{tA`}{OL{vAgij|pn(DVoYV*k$SpU<-?;M@mXaEO*z1s$mebIZ{ zw1hIa1l^Fl5~q|-+g=~d{pRMup_u+3KK$~}I0Bvt-ZMGqzQ}Dm-!o;Y?ojR=WX~S% zfq$tbClY~01R$^FN&QvWtD(dN`7_}$@RiCg)w+dWnG7>WCMBHo5ix6XQgC&%&I9F? zWq}h`8<42(Tx=htZ5jGbkXc`=P|gZBF>qy2E%=lY+#&%Y6j2jQ&IVQ$iZ#K{-vJx$XDE^s5Hp1jCb*=lvF=fhrYY5ur&6@_Pf6jmQa40ZGzG z=jSu&s6XHrv;j|gzQEdNv)~lG)K9q3PWaGHB*I>M^_^_#q?k8X9j5eq_qu}tav&Bn z_eOfDMmnhKDuY2b`nC5;jSuM=qF%L?S}(ui*Ly1)_XSVZkmfQ(xkZ%*|73JL&D}r% zoz(78DhSpF`7f?O7#l$U;r0jE_vL)qs#_e(GoUH;cA06e(+&#Rw@ z7y!;U0F*x(05|{#zd!hX=?i;L4^jS<%WLb8m+e-)zPvTj>j(AWRv9*6eYa$nYdNBS z!On9-LISkSPNHS2^`1=H^}OT7LW0=W{chJNtFo(|Xp6((HSX*3LSzWvFksGUs z^V5a_t;bmD>Pof}if(cirLBR*+>B@!Iv++IfLeNh05AA!cIVb3Mt=qXvd3OL(up-p zw`^b3P;`WbnA^1O?A48fXJnK2-Ens#f7#z`o;E-rS^TL@s78LkbjdM=s|aBmr2L7F zUoQr5=+K%xQcY@|qO~Yzegtzxl_B7%h^?M)*Xw?fQ!X65P(9MqDuO*==c?626zbggw3}MCwN}C1u&H;hzrPE zXyU2R^m5}WaoPiP!fXMNWogB5wjeMFS_I%Q+G`}kcYORtDKO{FlRmCG=}7DL7b2*c zC2JyMvD5$1Cs#^H086n!trnyeu&@iz2fcfhVtM6B*BThwKwssU)w)WlyAdyE;rJ0! z5|=y)0|G-#98VlS0Z%0e5afUTblqd>8YBwZZz92Y+VRFMbraU}2 z8O}t3Jc6PBCjp+K{cp1XW-h=p8zAwL&ms%#d<*c_2|$DapDh7k0?+}7@t=1w{!{tW zG@$7WF!KWA1tOrc>;LCx8^FWky+ftlKJ4CS06=dx>Kng*O8(#SvdL*+>h*fPZyQI3 z*)hhht!?HH6x$kf)J8gAV0d)gJ9zRr(RU^Cy5G?2wV|`QyshG=Nflb<{gaVj#h`ab z$)opE%{M1+PKK`EY^RN$+IrG#-FZG5A`mx^h}|%0{Yy1S!Bk-*H+louUmT(+NTvN? z-MEXM?ywGI1E}vGq5Nr}H+cCoT7Rlv1cN^ssx~;Hl-DgCKOt}nOC=H*SKxg83tzj3 zOU>Ha;*EFLP@Shcz113zd!w}_XAbu}-?cj(X9$(og?(U|{dzuM9&2`~gchh|A1oi= zz5A_)A`Mj#D{Y2dVn4Q%ThuDI^J)d^D+S zD$JsotQ~+rhIMb!Izo}_lN$(*iee$SBli9cu51{(xNES!$E5hq<{jHc&qkMz9XDHk z4g8a?L4sKoh-mtR*&ZBl!p#rRvBep|0FUEel^%k#0;Un=osvoP;8@89*!&e65+&-# z0es%8aO?ynpAbYSU@-**j5v^6V)OvA2of2}ixgkzxD_G=>FX5*fG?I!<3LCYC!|8T zw1<}kuk5IopWj*yB?CTIwE>*;diOrMfwXHwmb)tf-5m}_E4h+(Z?qAK$(eUs=2En> z(PREE`+9mmZG?L1^qMyoipG4kSBuDpveV3WiuLW+W3dU~?U=7cMFOo02bDw0TEL&U*9JX};gnTp>FQix}ua1Dq6 zYG(MH$$|}5lEEaOJR&oU^+$#k0+L^`4D<#(n1+Y&{DL`1CjzREJFqC%r0D)VmqU~| z#eFqd5%)Yo0ihT0K@gytg^`$NCja$?{&nGp#s2Vo_a~nJ<4BmHeu6>3D^dD9oe%&2 z(g0qX=>T{o9s&X6!Trw!AnyNjAK;k^fHdGU3cy=@9+_m9h|N94% zQSbZe_hAF}56c!pzk2(YJv4J9>2DhDa^BFMlMSTZIcROB)A9oGz&o25gxYWcY?I}h zIjAca6Wi(Zoiy7ZRoj2kEs>tPm0AGoPmhMLPZS8@*7f$q&A#6BPu#+C;mHYvo$=Au zX){MdJL>70!QxJFo=jwQYyqobuetT+1oRKWZ_|Pe044w>u!%*`1B&hsf1><<^P5*c zd+FFiS_5xib7Am+;?pm%h$|2J2giT>;r*p#}&bJw}NRKtPiR`+2gF?n;7A0DXwWztUD0zcT-H|0ShN^fIY5i zUWmRxMbsmub3ut}#G-jK?TWjc!WYvxvo%^{Kzu+zi)&i+{j>m#6c*M+jV(}ulyCv$ zpsC7~eUrzIEP@6nn2pWF{g4vS4FmZ#gliyBr7FqXvx2^;#?VG9e21w@Na9z8tSC^- zfD|@&BMs9FV^{d22m``Arr^+ta+$c}jTS`CTf}S3U)o#^N3df^p`r2HgL@z9^#)3< zhYrZSXSn!<_F{Ue?BK8Ay)u(<7IXM8~_XPDycwH3NRCgpDI<2Z!itL zILcz7Y1(fXt6#`eK@J6;E+sQ;0b+5E14{l9~?Xd^6UTpV7Y8G^Q}V&e?zqC?{MtxT|S?JrR_U~;6R9rTlc8jUM zpkkb(ysL~D=2n||+k`w*9*hF-YCX64zoX+et?}!7Yj6V_UgUVgU1k5x{-ofv>w(1f z6$AuF3g4|$8*NRjw2ls!*y}-Q#jU35=^QHko}xh8t(SD|=m^=*=~4B~>8`o6pOS~C zr~n}VuRQp|qxJ5O#r^+>pY1(*x$ZK;rwu=~;NloEX__L?tN(>N^&fxbt1I1&+S=OE zdv4q>*Qoy5#uo>@FY4vRd!yPYF6X2q%j^;(Jh>h9w%!BE0AhC>Lo49!^_Z;Bm%n}X z>Stem_UwUEuZzHMDcytpgQd@RUp#w(J=lXseRsG2gWbr1VycD8VLaM&O}bJ(Bm`!QB0-a1U7k!$AZHD0|hPO{VJmZ$Ue5f zsE4r)LS>tlG~x+~5L5hKRKaAFy8xJOE>g6L)mVxtSn>P#LRIBolkuj5Ryyp*pJo0X zEm=+m`W;WC)A8j?r-7^rS*P~idrl2&;1ZlD@Z@oAMfVT8+yIq1ukr00E6x|;0&N%r z{J^eke(LGn-`^|`I1s+k7I;}w$sL6&DgGfvA`Fl%XHn)_*!j)fG3LpMWeHnI%9r1n z-ew)WdF5gu4eTHH!_ku@+=Bx?i47mJ|4=gNR%2)ZsVi)|Db-}&WO`@MQH+K%Yye;i zFb+hr8L+)-Ad(GN7%*@kjzmZj5!1gf%oZY;xy%6(%!KFXWFmrsanB_xIdeG_35CV| zPUT;QAI!b}PJjRk8IT{@;Jh35deOEwgEi+?(ugI+ut`2 zZtZnQ1U-Dp4sCPuDahBLT<(?or8?j{cbZ)u9+~a7FuPjFkBPH zK>+svcVeWi$x=T@F1F^hceJKM2#6Rz%r`)CZzj8tf42w(Z;p!Q1en8V5Mhn z{a8!@6yC$%(!OKCt+U8yQUx-cK*SsBD|_ww+ESOEe|2M#`oCXWUAcMl-r|Q=Y8(2> z#wXYI3`y39fy(^m0&>TmPJ_RKIZ(_u4kX|#p}!Ra5#iy-FW~?@dVo^`mNhHh_TSeX9I^zQla`uMu6ETNyf{A%CO!Y;D1V0|sq*$Ob8}FF|7!~Ljic}^hA)p4HOTloZihvk-FAGCx zK1>gC;}Tzec`o3|1TMrP(F!`i2;VX(ECBdaxhfWXA$}&N3VMG8n*eDpD5OW8GoSwu zB_6L(-@$)l0EQtizEuKWyFB#(I$sFPVu5Fl0B1r#-gr6F0GvAk^3E1e3;@$0ARgfV z=OLitX(sSl17Jeen44)9-eEe*oWiy;NVvtL5Op zoiGrW`lX?h?%3&V20wCM3rJHHhg*8H7sT*qaw_b^a~q@sZ9Nef0OR*>#||Vs-~+Z0MHd8M2%1l9 zSFLwdA@9n=<=7VkqiE4v7Z((im{A5UxDDUn)W zy|cA&1qyz;k<*>BQNFczyK@K@_=y*fzOc8>T_q*HuzYx{bNK8FYl0~7=*1uY@QlC4 z!)J%<%gcFa^$h(YU_kS#Cfzu*tKNRp#W zb;x{CFifk#c)=DFHh-fTlP%IoG!O?rd_hG*6kt)sGC!8&v|ogD8J?uFmJi1zZ!U<9 zrf;+3@XsKIgf9^^jpWe>}n+*MiLzggfC@q z90&tcgf2lS78wA<5b_zYUkU{!ZhC+QA$xpfOn3@jo^|^n;UOlt zNpI(VRsiQXAm#oaJb-if|1JJ^={yF2hZqDz5D-rg0Hz$k^X`920p=9I4D|Q!;QpW1 zKea!T0e+ViVCnncU;b|YaB#5s^l_(i`27|TV0nOn-tzKrn4e5w1NO#8!`wmJ$Zs?2 z-5v-PpoIsfdZ+U|@w`><^^7$(TEO+=?!tDy(;<<+|IwkcnNSKoNlRUH_m8mWedoo} z(MV6EmEr5%a6v?a76|ZsNA1Zt)u`{as>4tA(BF+qabmQMom2P7e!PqanCob1i_f|$ zTs<%a42%i;(H&K?N%h*f_24Ie|I=?Dy?pVrFYH=EEGWpoTQ2$bJaq-uSxMMj5bjEKoGJEaOYK9*jT#I ze#-UrTg%7%Fg9k5u``a(Pd~x^1E0%K-?`U6i(v100H907rw%xt6=}g;Lp(j9#}-*fOjPw1;p>?<6)8u@~eaHlUi<6TPsUnLkHQw5_l`Bpem{q7ORU4zbbry8S~iyTw0$GZBglT^C=tvV_uC#KY~GiHCCa- zQ4eQ90b>=qg&@kH2Aw{7U#0+@Kwu1K1mjl=R4!x#6}}X||947ofs(j|WdfckyFZ~k z3QZ$Gy+T)X*%QAk8iPQb1;Kn29uZg}^k5!Sr;r&TD2&@6jH7>B2%NPFyw3uFc;*%F zJXitD2mqq~MJ&Mi0^rg+XHH;(1vEwf-+2}SaDD0n#tWwZsR`KjTNe;9|4%glpEy45 zA0B-Fp!|i-;p6Y;>bIS8yT1MX@9*JqrQ6HGz`49tAC!7^*WFmQ+X}vbAh~H!|2v8W z<3I0TI57IK{OYOkZf{H<-+~&@DkKscB?%Sano@+iC-0x^=Z=mLuEPLoMa!@EhuPM4 zZ=&kz=Jn1e_lrtrbt!}dR6$X%MCIX5ae^v1kK1;GXq_Y2zZ&v-OK-vy!~sOa2I`V8 zZx(k;-9P>P9}mB9n}y}EPUJUuA_IWATmv$|^`Ad{IJ#HU;@Fk56BvB;-rC~gpmgIs zYY>8#mbzc8)vS#gxpgXh{Wc8>b`O$D9q6_pZnaRzUPH0>NxB>MwgIr$X$v ztHZ8G>?7I$!b?B`I9`N#3Yy7=I8mxOID;G+71>!x_{17e90GiTQUoU%M35CDbU#^k zgvGcyl#-f6BhBK_AuuE-J6|=kp5(l@paojKxUI08Aaj_dpAqr(v(fN)=pm}V6VLl# z0n2`)9F8Oswq&oZbU!&@&q6=t)=EUM5+dc@XECU3*t%Q#WY1O7ju%7jp|0Fpw0n*Y zFK};fZ*{PkPn6100l$;Q6UR|0xHHMdlwvqwMI#}FhNN1IMsWipJWx@63yF&kDn|~j z`37uVuszOzs@IG9pO=+6JVHDbTM+sh8DavAMktaL)lZWzKC>>U#FJGKD8%Z(BMiHO zK!TA7mO`xYqr&Mc++ZfC1haw2EWN zF#J;0xvXds=9v*V26zoRFoXU>lK2;j7ypq6#xw8y%727`UOHC+%rL-N4?y`}o)!N! zu>+i9e;9$xNWcOHobmsspSR%vGXnga$1i_=76gb3$gS@lKLrOoI6UYKZ>_ICJuLtJ zaofG!ulEo1@reNihF>{$f4p+UZcWgA z*aG0M*?;xNmtPoz0K7h#jKe|2@Ry7a~SOCMRhd1I0PJWK2X$|^*d z(LfucU8;hA7XxU?d>0oOUJ`jC0@G#P?cG}5`@+j-51|6Rc=e#JIk%o39zXi>&lviD z_Oma4`O6QVy?FK8!@a#*o#QV*{Mj+Hx`qMKa9ZPDcnj$LhIj#qtGr1+Z_=<6bqi=` z?lezRSqa8K5vHCnyq0WlxoyN^K=ooY;El0iM>GN`rby^S&gSUWgkrBEdRnQcIT(-< zvPKb;KIsNL?}(2b_yOWXNCwj|b$CcqFo98CV+$Z5_fwrXS3RSC#`bfw$0Wz~1|0-P z96f>^o$ye zp`KsSoeV`FM>1jB4af33af#rL=v*cj3Nwz(@s|(4@^`etJGK)5o7q6L$c;bM1M@D- zlw#C09X2tB5n=2ZP&{w}REvdwKnw?B#^=kh?_>(#ci<8TvLA^}^F^x={gRnzC>a#F zz_bPQ{@ExtAUKJI{jn&!#IOJq1rZp4Kf+)jpuS{`Z@^#>i54pY^3Oz@o^X=F^;E<@ zFCLsi|1+B~EQl&>5-Ifmlmnl40CWE`0P=|9=V|5wbYA>3BXG8WA`9Tm1?;>5n0Ea$ z8?adiAY8!CZGdJbz|=oEfStQ{@c?;HH=hoT!_wyAUg!Dm%j5om(K#4{0q8pU+4|Vr z>J2&u%k$j*~96Gh_>Po9@N~{P~qdg+u_RWN*ew6Aq za?bux&yQc9lLw?|#0`=O1Ed(7%GX(q0^K2XLk zUf9HQVl^9m=k;wnpUy__EU2~qtp~?1UQqu(d-3AY;lbYVcVE8v@*n;H@c-~3ETP{X z?>+d!Q*zVuW_|B)Q&XS-G;l#`(B!M)?~PiQz;A3mSo(nk5;lg|&g4kL%A8`Qh&2)7 zX{G%6LUCn1ktuSQ)5QytzsB{izyOdC12BT)+5>l)UyD)hTO=2Q4pet1vuKeC`3dj| zc*T1|eBU4g*TTa50`q^t53K3+!V5&M!(ot;U%2W$d)MtB}s*#T(W{A#3qrSX1x=_FEyRcdCCGs2-QhEg%U{_Q# zX~3FtZ>^*u5BbmlQ6|}8Ya3qRe<}NZXJgUMdMvtz?q>$Nz7e4KOFd@F{7O#^iKf}A zc(Pv03YsAW;&Z5?SQ$@T>>Swed$JY7n*d0@uI4U9vze4c`GfBlsc#;%_aJ<%;-sP{ z0>oxIz%}1F?N6K z(m{7lO0?Ozcj4on&y|$zi;uV0+eWEeMtMIj!*O3ebmY7%C)(-uO3gZ%C<=+*!`Dad z$u1EQd2RH!UALWUwceSipKR^^B8?9IT5n{P?LkvsDV2LkqX`eJd-7_oJDfno`R#9h_M2A^*C!{Z zTjL|f0Cy~GZoaVk)vtVeX>Dz7XGs>i@gB9go%*k*Mk@YVQN5LdUSr_`t?NKv@c!YkIEbVk`Z06jk+J|)--p}9hL0T zhJ{o3z&HM44x*&ZYWJSu4b=wKkFE_i=5Z^Ui&!6ZHde|=z*p%4R=xHhufnL6EEM`q zEQVCaUsaMJe<3j!DX0{25PZ%{NSP0>qy?ORl=)0`HriAE;Cy2K=eL3WRf9W4TKIT; z?h=^A79jz%vd>ibnh0M4VSq;{L;oPAgCz5RrBbYt^+k6QK1DOYBSb6!YU3C|fvHI7 zYB(GZU5Y^a;Tdm$>2QzKUs(Lcguz$^3J;S$Z+MP#T43kF(I5^0>OC~24PzO^FT)Ht zj|4lX|Ne^+I351aBtUQHfT<7YGz@r(|6Sw#>X{D+eBfyg_;dp})d2E?83}k>tN?fj z5Rd_Y$MM18x1L(Rf4cnO7A99u5B8oPu6O*ceE+aMaO?K6uDd)?yE--uVYdBP!|x1D z<@tc+x6>&6U^DT&)6%xnuh^D|NZ@mIh0 z({C@`+gPf7;3J>9vG|e2kKX&_7fYCgb)R&Xx>i?&U%hzs>e(}N0bKll_>+(ZJQPjAv*V|_J}|HX(JbB&11~>XzV!(2V0P>1 z=J6ahIgdCJ5bI1PF#2|yw6$C@wk=#{_wu}~;@x$Un6O0~B1|qO> zB1CvAs1himB2)rw#}W`$bQNQ=rb|6Gt6K<-#Vnv$@c+e05Wgxoa|w`8Deg@;RuEl6 zUM;jXF=A)qFS3G=&K9&>kgY=yR6=+I#yJ4;2@&Y;3|7`1OhN$(Eui=y9>c;*ARk0b z3|D7&E^?7=Nvi0H*X!3VUx_YsQ1N>ED{Bxhxc{|osq2nP=9AG#&(uDuf6~?ly;u!d zP~=14D>#UW`ii6)%KoEKvDm;;@2?$bFT-g{#R3O5or- zBVp>2BB$121V9?d5?3m0gy95bsqf2kmm;B5MqKwo2^^>DDSG^YvVz~oOR)+3cRc`~ z&fEXDdw{bHaPi_S4RDqO$koqhKIs0Z<6jE-I2bs2qgC&RWPqyS)YIvFjU7XQ~crRwlkd-yV<&4KVBfrq&K3mv+j(^qdVJfd~+RmHT#Qp7a@t1V+fn1>h#4u;{=48w5 zt~Si^aQ^y20qd7CZNy~1v~#i}wDcnctHf##Pu^evmM_u%W2&!GrXs< z1>Of2fCGTLANmo+&#T}3frH=+rBspPw=|Nxh1$N;K6rY({P<29lVmP_{$h}r1ql#+ zTa#v=^Tit5D7S`KuAR2&mq8R0{zEQwW0(xV#X-@gsAXRZZ6?9w2%N6C6@O#H64)C;1%hX zS2mkL{$inkpYkU>3}t|w#t}@fl~n`B&3IBIa?7%=**eXhJei=a|wlY>kDp z`O^Br<+*4p#}`?H8(-{hK>sNLvYL8he}j8}qZX_U)b7Tb+0#m;;L3+IeS=W2r6p&{ zn9F?d#-QZw)k-U?WnmR&4*Gs zJMoT#+nQEL*C_s@{r!{IC&ST^NvPjup_4ak_syFlrOZ7)(S~LsvDsfK*X=QG-d*!# zQrfrMYEDI*;D{W-iDj2gcZ|Ng^|tPIvb{*eojkS&UzVChSefQ08EPZj~=F;Ngy|I4pJxfa; zeb4H9G1(fU@hx}n?Uw{`5wVai5BlY@omlT1b-Q1GeE5ln-2YSB-?Kmd@ymap{y6}U z2mJA8KYQ`&8{;gOwy1v&07aupf&Z)RwuTx}tn0bDPq~_LH#lj42sX?uhIalvr!ePf z1>Q6QK&iQ)HNa+M-j|cPoe3yL@`&q}J`_2|$SdduOvQ`t(l5oa2*;5d20zJx4LYmqc_I_59*MLP$cQ+fN$NtrUdXr8UJc`r{O04w2mQmxfw1g%#|ow3 z=vvaOt~#?VyxpDQC=+1OOa@%+pU$TjBD>e`+p@HC+`?No)pfG=PHID03sTS#=N4Sixsdx zF$VAhF$u8KZzL;%$>XFVNz6u5MZ`YUEh`(5Bq0+_3dS(Hz7*3!qyjR?IuLgdVJH%! z6Uac?k(0@225KRsU}pU|gyDBy2&wTv{89w35EO$U@;^BUE}BHWHjaDERY1KoI{#@q+`-*8hA0 z0$>3kRDgV<_}Tvn5g=2*c@)T01UStBc^d@sO8@{m)bIcP058z>$1DP#*4K^1yUP2w z`vYQ{`$q0ye3Y+`2QIHW`tbI;(@I0Sz~Sq8JDtE=sLsk?u)#gbn$ zyXD7qSLIU5>!5$KX)?l_?nM0J(VJf|534c|b!Sv6_f5_3K-SVsIUG%QgUj;|>Nrp^eOU`bIw8M;v*0_q%tWy?S}~fvvG2LBI}u$RBRJ@Muo3j#kM3Y$3^@SO7aJ!srwHV;8ud zen8|M!dF1&!z?Tx4F`+8a7r$xV@<3KpzyPuujd$WGy?u|ewCPD0Eu>Vo=aU)6J&wM z6Kzd{`x*f|hiVKh5SPTGX2udMnrxs6^$+ZjM4}0lIL(~8k_ePQ=AcqkLXG_jG<)Dl z(}Lv#(kPdrTZEtR03B(kFO)i8W=@`DqG-?7W zE|@@3bp1u3J8=>Os$Biapr3Xi&g;2sKBh!G>rLi!u5#&WQsETA<^a$T3=nz{exO)~ zGeKkn3b=6qHP z(RdLCaim%iw*Obd7s5EG&;s~~5M(Ej<(S~-rjFuN|Fq_Leg1YH21vo5SpmIe0gEI+ zM*b-PAX)&xf8MW&5kQ0i%rpQzF3to1XAi2M6(9%zC%{<{_{;|E`1ktP%y# zuj|XV9$$Pu9}nlj|Fki5*%`KMeY3o6Via80M?=k~zfjWky3t>&t>(+U zZtX^%!f0-d>yJAo)D?Ye_s!{6I%l3T1klKjtmX;6JFLtNA6BG&ZaR&9SY9s?I9x)% z=MG{1G@7F>q4Z0i{?z;4ed9gv{?v_|tM^ws7t8BQ17omq|Ni}N+*rDKb8(61Mb3en zA6l%fFMaUl(#@sbej_6+tH*0=R=KQ?$IL{%?n!Po3?KVs4WO9C`7oWGk_{EJe*T1vj3`+N6&@T10l7qC|t<_u|)8=IN z!}n?>*0NEj>|gBVJg@`h+~TJIRzRu0ZrkbpZSShThtr}mP&M^}Q(77LJlu4x?F1J) z?@ZG3F&Us%pO?ZOYFTXd!3w2nrn=@@)1$_X6c{ z?g;*0K0q@MK&oH7m_q+k1n}xC51hikh5|sC0nc0j*a8S6&~3=T{~iPa1&AO39=~3H zo^G!_eoDoc)(`7@{l^!Xn{OLtxkUbc`DmAz7Gpx|UoW|(j+&#_Ec6%NLE3eqZ#oP4 z=bLwGqodI{r{qh^E#_o>Gha~4Zh03$Q5psvg@hjfq*3BQ`elFJy|S>NkB`#0bhV$i z-gT?KSX<>=mrBMLzM-7B?(Pu#0jzxuu#-O*=1HyAZ!hm?PF@gq8cpu44>z`oLj&H;gkw#^vS zTfP0@yDz@=-B&;P?SlumbWM3@efjq7FT8yD;9&V@fBM7U|M2Ylb*f)rfJpbSv&lim zlXpzJkOPX`U%MX(ig_RUkIXFJ6d8roKb&T95bz7aiUWny{%XNUvD_0#zO^HAIjV}S zrgX(Ts)psf*H2)6@iz?0HFtENMw za7&|dw^yz?Sb^!E)SY3@^X_hWuvp96ivkC%4blsui5`A#(N@;fjkM$p)Sls=SHm3N zYfb;1rC8vhNWDhgfS2;7-2nb715QGY{03j0)kpQ>^29pu&#I+2XLBc*fQ2=|7Xf?8t zBrmQ8FJv!gF0-S9NdUAj@_{`Nd_^W1h+x)>Jct3Fai7d!UI{G#UqjLeh5vT>2S+e| zHUaQ48v)M2zw;0 z2H>AuUF&aKXIOW=X4tjod9!@K#XiTE-zVKz@6Mq4}*G?GVG)|T# zX}k7$-{<)~zsu)f|3d-zPq{Dmx0>hFrCk4;yLGXSqkk>_MwM2z;?>`BE3{>oNWKkQSn(ySI0#ZOI3~nqFL5Offyu9gL1MR2t z`3StZy6VU4ZhK7M=AuB#g|{C+e%<8cCZzi^!bS$sOWa(SLJk3A+58%J)9VV_Fi?PP zfZ#XRSE*N@=bd7)m^C7c1H@PotcgLy$-+%SgZA|jsF#S;pKmtHo5|JLB~M}&n|{y5 zVd3;(wg4L-R6c%eCVd_<0<=ni14}FYLb-JybjGD`)K{s1K2_wUmhY;!jTS{8NwaQb z8t2nnF_4{PAPUv}Kl-ab`si=>&yH(6mFWdBFi3;r<0{*2?*GGI?LSWw#Xsry@q#N} zJlex1AGU>JrP!#Somq@=5?~6~E@I(_swaA#UXPprI2n-4L)1LuMomkabo?|fZ6X*l z!7w-qZWN=o=hndL1rBFWxuJZ0>#dw&W?4{2VGX3J?h)=U%3SeaeY|b)BhVBUt|q1p z0wCZBVn94eB@5zYAg09Io87VZVVfQZnlX?Vb>MdgpcS&~jNVX;Ym|yusaU`e+i1zG zEVPDL@N+x`QLw&6rN9rg8&~O6L(3H;S;Ml8a-tE-+ZE<8*xOY$6^Ri3B^kv{7!46R zPM|XceyPz~2mLy22n2+ffDs0PpH8yO$D{){K(hZs@q-KC$00xvJ(B^EpLX7BQ*5yS z{~>f%Ib2RVa>DMDb%51G7{1o{V$fl60PTZ-2a~-62L!l;ibVsme!Zl!q5Xpb7-2#o zs}(t5_dxkWejp_IA@3yq2Mqx*0n4I`0Dhr`_>36QTh`h?{Xt{B`Fx%0O6$)hYAv_jMD9P9{^_G0cjSx&+I+3L72|05eyC zQ+)!9&mNq&kJ9NU?kxmJaBMiv(&>G+nc0)z|8ofsk!#?8?VD|VH>=SLk~0G4VGqHg zFW+uQxe@UBFwwqdw2?g83O!4M7yyv%hPa7!i%XuRrP3vOk7`ZnAMY zI2*+xE{7kU=EU<455m`rlXu8q&~qMj@nuedV_>HrJ<1Twkij1fGk_oYe0qrQ?QT%9 z@_fW*S%wFn8~bmh8w3f1H;nn7A!~nh4b~9AlMtwUl)5eMZ8M8|m&AP#n}#gm1O(yb zg)L{u74Bcw zKt!`_al^mQ4VIzCu5LpoY(hFj3~obqfl`_S(Upyskm_*Z#j94a--^_5!b(U3kew|K zUO*TGXj`IMjzww>S&-{hzLu=9^~y4C$INh^S9s-uhE^#Yq7m|x)q7+U7zY)|KUo39GZ+O>#J~nYyRX{pinr@zh2U_g4Nlgg zM9>LrKMX+g4a5>8h?>WE5d;Is0v4goq;xDQl^Fw?5@7kbr>y`A6$XF^ar`VQR%=_d zT7nD8_Z0920tAR33*H&C5)hyTzX-^O01^xutO`i-pv6X5z`z!*sBg#!Cv(1q%mdcj zM8N-@2RcOr@81rdCK7;w*u?gB_XBH>2fV7f9c^EO9A`xIXOwQ=ghhq5)(%`8p;6#3>X?)uG>Gf(p5i-U^;)V>F_ zA94VAN^^w@fWX#D3CF;wFuz(ZUVZSe_|U_N5s;gux>T6o5U)`4ECvh`ZWyKop(^n# zyn(;}{`fa3m^mFQf)n{xvNEA?` zMj@&;f{+^n0QlA#Buv2wR9NBoMC}i;F%thFG;W6%5pK2+?6jc=#QqTz$E2Ac{Gi34 z+ljb8;1K`;yCz z4`>lX0%coj2HbUCs_V*?GuBvcG^ALd>~dMn?Xp_A&@r35x_&tl5X|^xu=+ReF%C08 zOzVQ$8$wDDq|x@6h6#ntU{y~D0RG7?Fm3<`ARZ5*!N-K}kK;+HVqFT?Q!nuRP!Q`; zL&)xp1Lh|ag@SMb?*}I%gu_6#0`LWb_5Xt5UNF88go4P9AP|d+LE z{-^@R^N(p@lOy1c1vq){f6AZa0L|d(HWPdj12h=`umQL;0mEBRfQSpgg#R;H01_1d z0>ECbAHV)`e}C;*q^idV2B}ur*IQJxliuahAl5M1E$N5LE>#;r3Qy6v$${MoPu9+{ z-VSK-_n+&IWs2w9M@H>FYYP~up0Ea`I0C^0Cr*lRG(SFypuC^B*Q(WG*0RoDe|M9O zBA>5D(_8ysgQHv`xwZ8tYkCz2zFs&Y(%CTpthPwXU&qH72L8WLjKBk55V^tkf3!kzj68;(R7T(=^0|2L`fH-mg->0@8Q<4Gho4?MHU&CP?vYhc*S2hn0 zpPda(VKe6JG3sE30p>?Ydf4&n!qei1EMR;DVuMp1Mb(0DaRk2;nDfIOh!qS*RWohZ zhBYxY%1)EB*LvJQ#-Q1LQ|9tPk5j2}uOJi#*w7kD7TITvsoU6LsyP8NfNNr1T@ zBL%SzCs2S1ykA6^!DwU|8liwb4lM%O06_gA16YG<*#2qK%@hV5pWTe}78sADvjk{; zENzVO+OSHgx4#8%*SB(Ov!iI-HbH~rQS#tTSbI{z#AA0V2KA;DkWFy zv`{4UAUaIjm!v>FEi{|_V1#YsaxIZ|;U7>VqEH}LRb{nohxCm;jQI2xFL*uPOgds$ zE`-%eMD^P3UYo`qj1qqm>jE%8HOfC@Ky5(-=|E}Ye;>j0Cda`d^!~I)VHdpU5MU7$ z1c?#pp*Sqi=v!ST3(P~{pa^|X5I({8gJv)X!3Mmi^c$2Wq6u!6B*AB4bQj3BNM z5&gytF-*oA+jA5G- zI6c;ZVS#o&$p_ZJA**Ha*Gfqg>^1|ktP1!ejL7DfbAzow1g$^-RG~pHwho*yPqQ%f zapmdpch?#5sK?g^^zIL$*B!bKx-aOg>* z8Mrt+J@FizZmuSLg%-+x^{`T^BwNWO=s)FT;lq*CdU&=25_HM(5FV=}$Y< z+4jwwQsUy-;j6QgW*IlUQ5R&e`3Ln+jay#3cXJ)bWt<7d{q;5;YCUz+1Dg-nKpUWa z&}DSeswf-b1*DJ)WEc6TkGI}J2o0=8qfq9;0T{lCh&RAUfs{e8jN`%NiUfw3tB=2|9=vR0|r?V;CcLY<9-Q7V1|^ zJEOb^$ki3$Xst{csJ7PX)hX;TxALv!t-Q{T@RE05BdynF$8Ko5Ckjdhd^3!lmDH8o zN$KSZU#2)-)>Exki-i$O5v+ef5u_e^VFqTQD%(L(gv@008Jcp!{119ROipxZW`l zDDt-ZVtfHSAxQgze!wLb2fhr(J!FA^;hqYb2$*Ma8SX*)k6#u`d-Ma?4*+2Bdi{3v z3TVJxpdbc+;wK3BuW=D)zy&+*dB}oH#O!MZ#cd&;!YZ`-F30kO?Ms4!AWI}pp(=}F ziUOK7Y)A+H1e^82kEXyA`@g9a_%;S)3Ik00|GVLTDg&6NfuizJ{TTk=o9_QG3ZVL@ zLLk!rzyD(x_$>k8-w6ruXFocB^8BCvmZ#63?^dZQKw7@pTzd@O3oViu;IKYRUw1FN zT3;r%2$nCL0n9eOf{&efHko6mLNX!H& zKa$Tq*{`Q%npWkThy=_q?IV`1)@yRV&h8zh@_)*wZMiKH`Pu71^g~_WfcsB%w?%=( zaz%9j`B<96J`%!_-Cpte=l`k*cSeFKO&eLEQ$)me2O@>S#m>b=aUu2+JbY-YD~6wc9O2&w8vqK56*NvI|&<70m*|8 z5X8t)up}=pi7*(UffHfT|klv<$W8fYEyBVe>G;|PG8i?B!>f3S8#UP26H12l3p zRJ=((I*i6e4f4KfC{2!`Ko1^Ysez9v8NJ$}!8d|`y5=X|4gj6Z{lt@9WPr4hL#kW^ zci(86E8(sv^TLpoiY-ABunsIDK~^ftb>k*i2gIssH0sbBL|TLVrgF2cgjm4l1+`8u zI82b&g=HRrUbY5Zhd4o?bpg!M8C0$-gy93$UusDD0Q$j19i2dM0ni^f0mKxVO2H2U2N5KC@Yks~o~Ub5I*x^b-1?-?V^<-3kmp zXj3~D?qN%yn0e1@@J`q5?}8#6oO#EKeSz1abj&Qk22_b3Pz-rVNVv%xLVD;#@oPpK z0BRs1_5t!UlXuLTcS+sbCs_giv-+E|gUCBm4bW}*lcy;qm{k8e6C0pu3-BJv1D;xd zP0YZj@c-MlA|N~_^?#QH@b6>;i2DDd&)2s8>7PDtaL>=16eI0&z2!{vD!DL8D+=7xoqGyVRu`|*=l zBrF(_q|1**o?m|ZH9JaiG^^L~_yQ+()w-B)!gH0TbGw)RQ<|mOV6FWF3_q`t_ATNK zO#k5aNdo^xD1PI@8aStXck~Zme)+e+;6DF+v4^^MuL=58VKpf@hjW$s(#1t$)3cPo z`0q)SSC9i5}wLQB9Tb?N-I8JqA>KWln4=^;;U>H`t!-z&UY(G z-+1oXA>Hpg7oxm``d?|)PlWX78Zo6nxTI^B;H$DEPVEu#J}QN0ssGd6ZgEkuJb z4E4)6KTKh$8KCQLH|j1$+=+;FTBkp$Ds+Mrj*>F=gW9)!eTjTv@CHCS;zT`Y69O}a zFmMtl;#b84c!;3o_k-%v1Jd8_k)&~gOH36<{tWDf3He{MylmIx0ydR<8P|3WU7JRS z0{}umBX)tR(ED4F09}{(vV^P$rXGYfg4D(cuco?T3XrST>5upHQBkWk096aB-7%xd zAP5>v0;z7NGvq)341@f}E)BVIO6D7={gsqV6M=vbPRYd@ud1hNfg0_VE9GY0s3~J( zo2ym5suU|rDi-Rz1+4-9QyLWwx`1{{t8)o>(9qU3t$u>DVLEa;{0mVOK72z#*dy2< zT7awp%09C4LlGNL|CpAPj+~O;Lzlk@Xy1>VL{xWh5&vl+IH3h#yca!%$!iGdfEi%v zzW@@C6^%VG0GC4rzaZ%Ug28uP)|m%CfeZKDpRl9ILy-9x0RI~>_(K?I2HNO=KnRRM zi&djEJ5cG^ADZrgE*SMrngI>YhH1%9LBM-I1_Mqlz^3or|1l11dII47$Ajddy-VUi zZ{s`xYFZ=+ zKzFy=U3)sm@0tElhOIQFyO2lxzIL42yHA?!Ie|!D;M}Pk1KmYSBQXOkFS>}rM}{uu3yk`3P4Hu#0GBU)l#C@H03mTCBB;D}m0N27 zf)+@-)Bz2!d;tmojn1gUB9JhCS)Ia##Gv$G(5TTkh=z5_sY(^2p!ABUZc(3M@auN* zOC!Y5sfOD|4E#ZtMnEPqI(ZIhK*c^)OsPW}<^E<;P6-#mlNy(ik)u*{eg|f&E zmIuI6Yn3%4BR}GsBM*QT!UDw6Bd~xH1L1gv8dk9#^p}NzQjM%eR;_@h0i(VNcs(SH zAV3j8FJ!f`$hR*e2q- z_F96(8^of5p9oUIAPrLB48b-4)eQfj1yb*-8SDxP8yIrIX)@4V44~pi=FjcB_?EX5 z!0jTC5P>EyWCD0FwE_kM5F&s{ASgKiZ~_n!z+?)*^J8KIa0>z6)jtk^FF*SH%R#O7 z>3X^i^AC=rM9TeIVxufB?wc@s{=@hZm-cnL&2!^oM1cK&ev1~i1_%gdC#yaXWuTJjT#NyjQ6851 zE3gH_2{1}v2q?Gun{WlGoFN5(CO$ zw84%-a6&IYsRU@8GFCsFgi!xP+wXT`uosc`?G$f@6}nb$C7aFP{dV)@mCF_Hf$u}g zAD_0UmD@LH6BbFbKh^@UQ;QOZ4!Cl3yG|N;rN^IrvAgzkO<^pD7c;soTNuX%a>Ux0 zKtPymI{}q487K6-0gPYR8^Cuwt|IXsVEB@6<3zI$oseLN!a^hf-(m0q{Nxvifufm# zS_#r#0jXq0Z2hoPROMA_fd6)w>lvXAj&$R3vn^%}uyrUZ8WBS>RqQ$e;uljg(|3a6 z+hPp7E8J${9cCz+yy}oj8OUpF?j|f%t5S-urF`LjtEM0J6_&tBrDAN=urJZoiXvB( z5gLrrmr{CrS*pRZP2y`H8c2;spxPL)bi-Dy)AdNbhF;kOV{k(@(_$5-OzLb|VGv_oa7^!8bambTV+%PxF#L@f15!B=$(WYj9X3>Nqhmnu6>0H1n)MA3} z2fQIb3)mQf8AuXDO&Io7aWhdGDhw!Vcp#af6R?0-Y_r)Haa`PkV*t1Vss|7O*+omp z6|~OW(-8K60iW&#*bn)T0hpczhY!*Gn^Z6pL%NvdK288=h^&jmGr+zG+>kT_|HlXD z-d!ZfT_&h<>H~(y9SC@b0FoB~0n$pkPd|62s` z?_ch|T>J8q&!2za0Lhc*fpxEQBEMx!i)@;Hjs);^u~Xxan}2!9_nKT+fCVl*Kw$~o zF7~=vF_K4Zrw|ALeEjlKozqo034y^tsJaX^{7mi5Wj0T5!6{Ut(mCDZsIA)Gb~5M| zB#s(TBG{rpd?|bzn0rqvAK9OP|1NuDJd+?`IOhR?SlfaoG!XdYU;pi2{^0YU{hdDw z>>U+lNe274b5$%YCClai#Ck8Wx#~%v{x1@VorC%rF7uOTC$otq(EXEhE0con`+R8n zAp|UL5Ej5_R7swlc{VHMM5~qb%{D3*2dRxx@q?mEh0CY_)C&qhsncQglAK~N=o{-P z(UgI&5pH~JIB9_n+ueM}t50!VqZj1gOcat~P!y2R0SYZJ6hM=!`?1<+^OTmpJ$!cZ zr)Il(?*@Q>7B72_I71>&As^I5nGAq>OzOCZ)vJj@5 zc1OFN1&2+qbLHEF)Q5y~i2w8fh?U4tN)oSD)#T3j zO6<-R5&VzH&tdeT9PUR=*yM;;kbEeyO0xxGP${LcCYET<$*q()sXL_l6(a(*M!rge ztqpm^1hk+4F14bKGR?M9ib0aCf%sQzj`&8pF{tm;RTbNeH27*=YDzKOkntOX|H!f! zcEscD3atz3XpKs5bSb??6_$Qptr4{h^8|~DP=xX1AP0cML2v+c`VjX|xx--VszbYb zsBeb?xB!U?f(-ykTJ|mi?+3pCKM&$?#^l9hpt1zDc9{R52{7Scu{!KtlHvizfZ6;W zMg|B3!1YBopIYm3nP-A92Z8fXki-VwgQEbjeGB3Tfhd|Sb`XH?nOaVrP| zxcng@x*Gs~)B)cbf=zb-|7Q_M%6~cqObLJ(0`NS+|1k(mC_v<88UjE%f%l0L809~; z0l@7)wE_E+&p%)Pd_BKjg*Wawx6ZF~yV7&!{QPxTWSMm;%}~_k9j zp@pV@F0r~&@|DW?WHA2{mDWnKaFQG~3Y#aD1m0gww1z7m7S0Y(727}008taOfUv&7 z*8?53(bsdS&96RzPu|))28e+D0)&7rHk>`elmI8;47tUl_^#U-C%y@e6A{oUjKEDA zF-)95NJw&&VGvD3{KNEWXHRVo)H!W1s5jc-`s-Ig4yf@iBe5+8CVAT1* zx$GGL_zd_+r<0;E_DN{<$KYC0!&G}*9Kz%OsyjxOz?+UFY;2@d$iY^zknqy!Dvwjz zsg}7Fkp7C2unIPhCAwMa4#5JTjVo*;40)d`KxJD%?zLhcxCm6hiuOVYLZJ74NUOFJPHyo212}_+FaRh_N*8elw?gv+dvOK1 z#wXc9_a0bC#sJ>601ZS?p*a}3t$)jHa=?!cKxiYBcYz>xS>U%I05$-4m-PR4Mu52d zN&Qd4fTqCTr1>W%KzPFaKQ#iJ8i5iYkdHq7@^8}Jzun(IKIf}n9#gy3b^ZC197Hcn zGo5A9)yp5+eD{*>vcTY_3N*WUCy1dacM5kKA){5@c~OmZFS^xqJ-;OBb5MqL-cUSL z1k^aI3Or3m5U7`j6cx9-y?(PNbAgmm+N!cg#g>$o$iEbBjK=?_+v{AuA@~0T3M8-? zCIa|d(#9MBz+7H0)v2$3^>6Y~s(n>#6$rlXaCmsS znVj7@0ql0V1L<$2?*lK;H;ow7Qun%|R8gS{`jAPB%LbKOFY^ zD<70r(6tVItzxMrZc=r6Sn%1BV|^f`jxdf zy>|~>9I~3fxf#zMoF1N@9o7fUgKrNHn|b8|!QlOb<3v1rqM~Sm;fI(7Rj-%b16j`m z*DtCuP@3&%(skNSXWQhDcFM2z_tzi49|gXK<>R}{Mh3=cK)j<^lrr!M1lq8T_(Nd) zxama(#69@sYlS`1XoS_qh=9HXBLWhOsXa&m)v*01TpvL9KvJNTb~sdU0)`(L7L+y$ zIEE<-mJ)=OFyD>vG>5K_*rLklDmue%znH4E#Lh;inCgt9S7J($p;)RQUkyc-(h-)8 zV$>)XVId>|nIzdulsfDa$`YD@M&c3LeRil}=447n9!RPt$#p?d7!7Izez4XY3?5X= z{BkBKMpaYgE85mEB>HZII?(l$HCqTTN5P606=JfX#J-EAW;BJR6rR13Ho;j0KP?xfbSr{znqQ$PyxOC5_DFs_P2xj7XKx^ zD^r)LFV{YGKY<-co1vNXn%uo+x>ZE=;7CZP8cY`UIIjcPa7Aemi?I%yuWfBb@+9{n z5-1x^PIGYi$=8Y9a;I=7pq0|5Z*aq4f^RbdX^9iG{QZyp!=T!P4X5+1bh zKF`j@>TCk6A1wP1PL?nRT$~(Y=3ZLG=d{YDzHgok3CU7vzEmdlKdMxc{laMigdv}A zkQ^0!n}^^8Y`p0A`*Z!)%0|ClD$O1uNF4GtBU>*IA5~mkPS#Ok*W?7(oxwUx7xm`S zB5vd2HZ-Or`?f7;m7vRNf93bt4W9324zkV!%|IF*$gD7 za~eFxfZ~UgF_kgmbP7?L3I|mJlXY(66VT*OyabV`8r1eEofeXSMGK&QWO*{^QnJAy zi5XA>1OSFFL{(W)Yl<>zy^}}ozm-De7aL9|hml=$w1?wy2YjiHSY*b5H9|6feTZNKm!(RD-2tEdQk%C2}oDf4yM;&!srG#Y`7`GWLP$4jKfq43M~5xmwd{ z3Tz=?lbYoK>!Jm184D?7PrG;bcX#Vm$i={|>wZ&q$z_|~s<7!mSys@xa7txUDz`$4 z1*Dmh9UXkBV6>Jg{%i42sSwO z;#Tish5-ISum)fns)#RU9I*JrLJzG4J_*_4zyoIB5d;;EoA+YYBR{ z2D}RbozejCil?azn4o_TNc%t00uUwu;Q{~X1vcRUlLxB*PbL5W*#b_|!0G330o?EI zN1s%|c25kI0M{2U zv+%ad*Nn~0*xMNA$a=Ww1ko;R!G|G55Pk?B+so|}rM*QA2IOt|~Q&GqDg(ib6{hXr`J3(e?Sty=_g4QBklE_2i>CmZFR#2ha4g73q` zN*}lSW*Nl&%A;RP%wqe$nLIr7c$Q{A-njr1=zz2Z2mtA0007vRnA=zZ46w4gc`^br zP)Jsm@J%W!^9Tz)IoZJyp}5h1@tdo#u-NeJjE29QTzOQQ?bfJTxqtOb{VOI~gGj2Q zvk7cebgCI%(7Ix4v^qnH$o&Nl&-9Aq-ye!Q69(G8m$?*MI~U1<=it@Z;aT$Z@Zjvk z3nd5&^=5mb0)TC`k;XL%I#31%0z`)&hqfYBrf`%A>OuHZ zKqeh-my0P@(5P{0I5FpI89$j0Oga^k2ZB1L!;rCPeVg7!Yu(zdjz3YD@W1)!CrQN3j! zmnzGt2v?(UKycVFHe^;QsYh4@)PV_DDVA^4ZLxuQdF$!p^l2SU0$NFzSy}=oT;&8} z7O9%eA!EMgva^bd0KDF606_to(0`^`00az}W&!{G zpG*V*)7R%O!2zm1KdYx+zHV2a67`?N(~;%(MVf8WG~a`db{oq%nxoIdX*N~tL?XGz ztdOIP#%k}a^%~9t!1@B+J;L8N6<}8*u!H2Q(08}TV;OSL9A70zc-sySh~ckaY1P%; z$LIX86FY873e=Dp*9T{(hbN~82NxPmNU^9Tfu7J$JB$b#86g#iY453#MW``?$qSByUbHUu zZn|S917VS#cIRk>i(wuoo~&VK5NA!cE8*A{oF+{E8GU>)n#~ZpFX}r=WRg*!1Bedy z+9QjmXc(Y@5Ct4)1X(}qFh#XaFaf(q3tnUb>L5Q1TNNSjfOSLz7@>McL4`r%&ZS>H zeSV%>->&eb;27dEQbV+yNUu1=&QQLjj-6EV9BZ-a>GV# zEh)(Yom}??Xw_VjlcQi&6pIGM!d$6FAsL`13LW$a@c(2}bvw-ecz(dsWxFj!Nuz2+ zsJiSk`&;R=LAqXp7E`T=79`rCXDFQonh3YuP9IvALzZ1@F5p^LJP6<>hvErEZ&<*q z1Kz+{4D*YNlLG(>fjBUMU_cQPAQA$(3h_T+6+5z42bllnMK4+ePy>(=2t_~0fogvU zk>D;(9Fk+pJ!qyK$OwQ4fGqi3K{Ld{Ggi2REA6V82mW^m7?1LIL74xpUFi+M7KoUB zz6Ymps|^hS>FiPZ%kLmK7_mk(Gr%68W}Fa&t!7{YLGSG*U`hf47;vWqCM4kL(=-ow z`j`fSPr|@Z`FM*Bz|;Z+VIUI_VB!Ugw~vtoG~oc<27!>5&v!rh^2?Whb3QJ<%BQ8{ z!EwFG@x0Uh_%-$cT$JX!oofj6@prPG=7gJrL-1T?y5sbyxRTYRw;n%{#@(&+bpzb>&fS6&4xnW3 zH*NecZ}5I}2;J~C>ff(L7gud#xKe1OcK#8!JsJB~JxehWL%I5|8W5D_BqiIY(zWDipd zxU!uBTOGYR+Yczs5q)qm+O4a=Ax4dKIT23CKeSabn&qQv2$E^6{UA^FZ-=uz2EIKE zq2h=X&_As|gDaodcoD5X#J~(@vPCE1{@~VUC{SnwxOj&l2f%It%R@($Q7!sK1}-b$ z$zfxFul8G%Zum3?F^{mRcGjYBgT{0A#qRTCE8^ZMXtya|fRQ&{;ozLy@1WB+a7AZP z?!Z7*C_a=^yn17$673he(ET~b)Cy05>if6fk6^;Sn@mcYid12TWh1>pfgG&=V5ohF zgH?nYxKRTOP{&IGU7_=GBRz=38da&9uOhPGd>r`>i% z#4bbwycbi9&8p#T7Nsl4IuD?NyR?jtGyUmmw8vDrrwh*e`IDq=v8 z@7nAd`vVQ3;JoL0;Bti?ghKBTU%-3TNfdC%0V%M{FirQlZ$;+03m?LIFsJ|>083VU z*r5QYU=kQGp#$7yf!wVCly`RE(=1RR0OUc+A8|la4G?~HJOBccV_;$h4)Xuq4iHZh z4gg-JPT)iZRNddL4?cR@X{7Sh^OxP@Di5LO)|M9cHX84BuGZ}#88po^=TtiysLAqH zEXUI@|I+4V=UZ-B1{*N9m2Nr%tf-HyNCBfd;^i0~WXd2*m)^1>n~C?!}9r zAGS*KmD$SX1qA*HU;w3+`PDfeW`C6UYzf9c6)=EiVFBW~D0p^uk~^pP2#$i)gG#AX z`bBbLBoY-;`IS{Xk{&D$@qH4+6dY}V2j-yj-!xjqL>UNSY1m&H$-c60t}{2szj(M) z{LMyj-WT0Lj^IjPwj1PDQI@45PmSAVAa&5jN|5s-?+d&<5#c}4C=vD68+Gz~*gQa6 za&UTfc6NG#cL7n@%R-QN|z+8OCYPujN^X zDgtJXv(_aDAEt|N7WGeY98`$B9Dx%?9LXb2910jL!T|2br)0n~gs%^TgIa{JM%vLh zetUZ)G7Sv|28@i6h=&<}7MEsLi`F@)!TGMS+g*Av2IH26t$-<08&%U9uM>YE6hk*C z290=&mWr7C5u`%JJH>8vm_ophI{vT)-%tEa$yXi6-f#NC+744m>8Z-gpZ&g3D6?|E z+>mI>SS(R=31MnVg?eSk!QhYT*VtAg#X@WhqWFleVdpn8%4xcS1EAJOYa%DGibFyz zD@YB>tRi3m&9$ll>@2`Gs%Z>rOC)FUh6INOiey%;G3|l%kE|~|n}`#Lb>a(V%J|_6 z=GWo|&p%c;l~x-xLtbP{@QoeH>aM?Sk5blK?~=8qr@!{Dz+h zu?sd6Sr5Q}t14uLJjtX268PXJG#J5cun2w6PAby{=nonJ68mMgU=e_6Kmmy$#sRDY zQR6O*0|tfCfplRde#SB-0+??d0Ts9Ry#i18p}Y$Oz<=E~0Mi`6J3CMy0Mj_2w*Uja z4Hf_?{7DwbR0f2%w<~}N{x=~4zx?En2HzhypFT}Vf3SN_H`8l;v%2I?#Gd|{xMIZ7epT4{#n?m zc~YgR^yBk2TE4vLUSGE8{bHagU-NH$nuGj84@x2S$(Q8*?}^bOsvqz_6Io-q%N~Px zd)COafnJemsaPaL_QzKnb4$MYrOnao>fDE(`GhaopPxhhqlo91W)q%S0{=t$hj|;t z|M3on`+A{KnA`by$+J3JnEP-Ql{ldaVDjHg;5bOGuFfZBAsPZHXvK2^YoLB&DG6Ti z&^Pq?Rth`m&Bn%jtCDO~I`gYVaEA_Z;}7~+IwZ?(Rb!Yc%HohA8DdqTt+sDH1qTpb z-srO-+($Knc+V9pB!%2_cv?T%JUuwv#|!{+0(Jri$6oizX?@Gi*I#{c_|+?g33%Yq zRiml}FxcxxgFHkoQBd{P+<;lkw#Q)PMJbVdQ*8cG-+NtDKgV%laA#=2hy@MYLqrCq zVG~~g^a8z0fPYbR_%@vz5TetM)*zENja;pV}xg z!H4t36fekSS?8m_{HyQ5AnZ!RR-<0yC5Dz;^;QGcH^xUp06gJ7#WiRM4QWA=*%}Ko zfI3}k#Oe*Y3JZ#6fNu(;CJVoxaIIFO71m;dYSg31aMS^)!Ea2{Y#4`MXFcV)B!Ui@^-kWX#@p23OKe+#+8`zIx z;JfCZcmYoy*Z@2rC%|L`Aen&V-X|o$=?L&62LQ!CSpiN|0DtlP_>igl(r2_;^v zMd(~UAkwj2QNZ>f!Vg}_8O;;EgUGxbyZz=3`gOnah%%dznGE>D7Jwsg7(lO=fH zCBR0m+d~s@Bhox8VgkT2oXfkvf6H{)ZG64EMUThDE_Jr^Xl@RO{_HC1eID0#>Cv3e zhe|Fj&EjKAsQ-(D+0~EH{U<#av!1gAX8b|{rvnNf+CM`7U9IdSJOxt!9uK}1_J=qG zJUjIj013(Re8IO`nXjP2`x|rp%1-T9xZm^DKR&HrjX&%UOYjft&abvSXN9uID6Lc~ zH4Y}0L9HPuiBYv7GU(!9%QD!_A!QThg;<~!i z56<@2|J15Ze*4OWHD6ndQ?U7pBR8B8KoypqMx`Ax`y9yn7|fk|AbnxtBo-xM_18c9S=~xFU`KC!~7WjrjmY zyh)))&cv;{0EJtXHF-5|8uq%nTnwTBtpN(#C>2(R>ouy~N6tr)s-=dY@Nry9r!LZU z=8DWKa+u5oMd#uNpb3_=MYKuS^tU7djNg}EuZtCls)bWp_^wM}2Q(@Y9abpnzt}?d zm($duS-D(;sqBcYRQW-(8Ifw~6wTJt$LCvvQKTVN(=y8nMVfD@6eeGx++dX~rBZ}! z5zW?awW_H~BSqv-U286z+pgK40b?hKQ(z+LC!z`PgiR)M&@Y;-B=Rp{C8-EOdr*rA zkcA8;{sohIaRL@9Nclr0I;z!-TGvNYGEI@8201^f^K>+ZW zvH(&4pTB&0e7xV?{o7{k`9H0%@iKD22Y+)8K`zWbk9$qT=+Ng?Dh;%rYt#{ZTm3Xo zweS~H=Tbc*WS)#ZT};tUcT=VVxj>pofEkE0C^j!O_v^;MNRB*t{21W>Ut|4unX0G? z6;qGedV8%6i4eTG8J0-luxg-as(AJ0hwbYyGUkdz(!YH}actz(-!0=elRi1KQ5Z5? zwUGxGKI%8I^r5GK(S7d2x%v57PYLK=f-nFM4rVWAPnTvP1Zd1AXO}in_j8!OlZkl{ z|4M}tJ_s8?7=dB>N9UjP6ma_!ec)1g&XY`FlYr0c`x;;Y^-G>I)_^M;OZ8Eue>LnE zUwlyMU;sI*EN!M%R+2)^S1IJF3OuvJ)*#iURMx=y%P^7Igq?u^biyb?Y|T^k!sfG+ z&14dH{)s{P%-wU6N8vZzZK~_lN3nXQ&Ss8IrKlqL^U= z1ffP*mWI3EAJ^gPor1A9M3L6+;kx;FwKFO#i#8G$9QM8VN~L*1IT6=<<199Z$6z?+k!5;`%lfbfd*#0g zP8@Q%fd9eFM@bCEf$V`DV89kp0k$D9z0MX z2Lzpg0&RxFi$R4<_HHvw!jLiy2B3<}1V|xP!Zmb8t#?`9$!&S9cl1BJU>LaN0TUen zp7DD7n7MTU!ZQFs)IT-=qye}y117bP!{C2pz>lW@02+X4`C|xx6Y%~gPme{ofc=vw zt@nhrZf*nOgXozuhHL%QttzU0#)KrE?NSkG6RY<|7dA+#N-1F{d5a|=?c3VVOn zxb--f53_6Q;GMA5L7ErYD#o~S(XDec<2V2D!{wOjzK8}&lAd||_}H0=tToeAyT-v2 zjE&^!TI%Y%tMA_IW%FF`W{mugUX~iLy86?6wQkQiT-`pIge~)L!gMuo| z;W+s4Q34nqynj5iC$sqQ($c7~^N)lJFbe|kD)fLmJ0J<*t12sVNgvLMSpahA{(%uB z&q(bjD@k+%g-5fittBJ{_^>N1ZeU|bxC0yghR0K0t*16t8}08_uI6^WekyI0HhjMM zO1cFiqK0jF^+MYKwrtH7aw8}e+Ucl(#+rz2!@UmNZ#Hq~*FjA<`1&x3x<5Sx9{|sI z_Z7N;ldsRvhRrPKf$m+B`+3`8!FD@<6Y+|n!5$2_Lb#1#$JIH?BJybah{+PO!R>a3 zY6NuL5J;p{NA3?DRx>Pid$c-EsD$7n6Gh^UspnM_hQBeObw*&X26p9k+$rfbHX#aA zA)*-CLrVceVm}&6^@OjN-S60eJ;<}zdRdhafCM-skFKwf$|@q8Vznm zrO?(MrQ}AfR<8-VS{Y300}Q^scF>AJ z4zPy++}nd8Gc*J?O#cs--@^&;;3po~un7p=_j)m*zpHTJvu55?7?cVe6b2=X1tBnU zsnOrtY%l?PV7K3cIB4d+3RI)1AqZY?UnW@~cY)wPT7kj}9191$;PC(?fC&aTx%;Qr zKiLJ~`8Ep_6Tl=3XzBw#T>*Uby!pv~`uUgZ&(9+@c+RcE4uBQuE#0U2ttx%lmN=S5 zV=(S?`Ln0_wLu3pE>eP!uP6^+{K9v?{_AggdTIL)81)m0UxZ+I-Uq-Bn?O9R;%7IBGRWh@7l0nrpZ^%E#YP`24na!#0$YGXJWf7C3-Ia-Jbm`n)8lo8p4mgz`M9mY)$-c@A9$gfEe9q+*$;CgXKrM{~kkRe~3rw)|y5}PDL51-r`d-3rz)2Lz@%@ z+20TEZ<0y?UBR|V?F{Hh;+JtNZ-lxyuGO*)`0K0v2=`~BLBoW%d=2zc8FDRaum{v* z`gf88z-_f`X;f7GywKGL zZe62Jvr>y!HPA%BCJb`}tq~v_l4NYx#ktL@IVfs_PNjo7ORKBI4Mb1N)j)`~m>QsE zUd%Yp*{m>q9%dn517#}sy@3Rga^cY+v#$PWV{U;;MG z_$GrEA|VS3Kj8q#m@Uu@;2NJXEFcv)wD15LLF=RxQH$@AXdXP<9?Uqr z)(4LJB)Sa!z1i-v0-sp8uT;)J8BOfKmjj3ybem&d>n#-+=-}N1pq#>hc)>fjBETPM z0FeHo0hrDJrMGd4JLQh z>hq?Mro*|wx(Jp|wgUmw5l6w#$W`omEtP0@dzG;?q&Bt$zQJXTDOx>;P8K>^%AdG71pK;8o%dP?#rO z1irlGo81^f01QlFvLC!SEGNpFn`ed1R{ytJ$&+$_fI3cL(J-oNYVwx3@6u8Jj};v zB%`Nld^fzq z&mwYIMCv}sX|A`HkpdiZV?eJb&Z&_})JCd_c!oQhk~ywXgjF9ea4?7NvP=hg zYSg&T4Y>|>e?zIYq7}JeQG4Yp6nXD(SPppVcCaYRazdh!^9Sx_RRF;!C24D|N)5vM z)NBJ6JAO(Cj7IenYvAKL@2+BWz@R|uB;p(>6$Ugf*|7lxRv2PcRHYKJIibB~3`*xP zNuN}0ZUw8T6+%W%AIqDoisTyR>Ka9J zsdP0}r1RqA-E_JsRR&-rrT#2CrV_t54N* zxnG29Ebis``T4{SHhuGt=Fq7F1DH=HF#hi(5|IBE=4NM?;P1ENAqT*!XMSlC{588% zSS_{s^OYj@1xrv00zD-DV1NMtAi@~vk!Pt=nFUtpE8$dG>6bRZ6kf%dkVqcl+f33! zO0Bsi5+7Rp;1|n7oHd0_L=9Z**z^(blPMLg&3478A4yphn6sF@MZj=9q?h$Qm@b}S z`v2_J@$Ts00`0)zv)#eBKmqYMJ$?4aUwwfM;IX}#KhB$tX}3!SKSr;%hwYs^xBls? zVT-|=;0Wy()9LBe9pVQjb7L8MxxL1a!j4GFTc($lfWTC z52-&z8R&9BP(kz^2K9(yB3KF*q4ozIc%wTG54+s3-w8(_wiZhzo+=KV<6?0*l%4&4 zx(L%RgQ_|Lhp;}PBV{0|wKQzMHB^}`fZQJ{{$eY|OKd}q$kpU1;@YGGjb^IVs9_CM z)yXNUT&WFebinSA%CL}Z)m)Gz$HE*$kC!4ePZ0%O9#s_)MVVH)ylR+gl>S%Z@huT$A;5e1fg7Qp)s!?q!)B)|bUL5pTD0>;Rf z8}M~Og%Sb+i_SuX0cOku_*vo%W=3W(e1p9~JGuaq>ph$O0eAqm_iVV4u?=|7@Sb(S zde1y#wp)=#jLu;J5rf1k$PP_`>poBbC9w-L5a`bonoxp6lghu11SF3;H^9l=e-{Zr zBmmO`fY<;|P92m#`SH%%m;g}qcP?O$2?u~o0Z0M@3215q0x{t8&tD$zui@5zehi^G z%dg9D0Nkb7*RLgs-Kta4%}ux4xfH46eg0EnO=9zD`dsEZou-)PJF)cp`Skg#T)N(o zYsjh4v#Q6t5u;R0KQ^-bC@r@NK=$gO_`D$vK$TsVrO|1>T-5RW`YuC_xg%V+yHDUqSxa?Im*cR(4}+fFQQc)x#i0}s+eB38&oW{7=mxV+ zN)Tba0VSjft3NF92>a(6W`KL39`A{y3Bb4jZGq(1A7KW7HxFRKpf$ipbYbpD1kkS& zoR|hO$O4U0yX$HomiRZnjEsS0Bcj7>3jtj~9YXpa2m~yw$OEtgwd>TewAv(gR;Zw$ z% zw0|%JXry>AdVoje6i@T$0;FWRQMZ6BEbz5*vKHa%po;L75kLYO31!$6#HdP5SfI)R z2R0vo(9)Z@5f51EBZBdxG4BgQyj3&pHiy#q8JIE7^VM^N2V0&#q1h!}keyjB@+ z!)laMxm2uW!JkzIiO;8D8=Jo)FWmmDAP92%7#PZhC_syFU^J=&)e;w^#w| zljh$r!2}&(11v1Q_l^x$u^rs}W#Y_@qJ)32*$c|Q>ppgY=t+&O zveQeiAFuOlwHnD|hI^iVoo4bJ=)tk&*RfBJ>sx~q)&g{zaaZ3u&#l$QR6axTspjC~@N9zFH>vN! zFD7?9lWjk^fggIxD@!}GiF$dd&}g90QSaF0pC*#^0_hnb5ne%J01N^=*cF~u%EJvz z0435El!!@C1?xhz2AC0ii9UouJ{%6Mxl&=yQ`y97zv-(*w|xpAJT+gaxp_Uq9I&ot)qdIC=KP+1b~C0>Aiv|LOjp zs$l!sXm!V}ip&S{>yX8RN14-6VXXL2;n^tazeYQa0#%EGdo9!`#?)qrt=}bl0opXx z&K`BO2u8+Gf74s+#Vu;Ro!0AUb(d*(qh32UuX@b$;v)2m&g*M1Wo!2A*gCXGLV%+&zl+;Poow7{jcFEpi`}45 zKGOwN2q|DAc7$@$HoeViSj-?1APDh|oWk8Y zA>)NI7@9x}5P>+R{B0MU|3W|#l;C<|%2&V?6eu8WaRu-_q7TqQSD*#SAIm_~eY*zD zIDr9>%7JvQ8Ax>R<7lub-@`f}q`=+}Vq^P#3|PcRc)?+_g!D5?$bj&hz;e-cPYtI4 z#Uu`J3IU)Q_PoK0RK4)db$UE}SFTwYvL+Y7-B@bhk$lc2>Ps4WQ%aseIsdJ`hI!7x#EM zmV2^}UN4n`OAiQ|;rF)$m;(l)eyU!Ti#(4AUG&3DdotAf-`_tTp}Zb;e`2>D2DgWj zkG8vi{p;N~zwjU5Io~b*)X%+e2E}pfyYH9?0OxTr6dBa3TLE?qZ7?HW%>CBGe(8hX zoSVh|&j-}AE8l4eJ|}SdC1U@+x=oSIXfW(!WjS`LC{|Ue4xTFemr{?X-&oj z_ynVb{8*WTryy(u zh_~o$0k+~JD?lWw4Ph>NQHlcv#(C}Up-yh zKRbD~|McXnx?e8?TP#BvaJK*T>8qzt@!>6{gBHus#WZ&FYg_BPqy2O)Ps7t&&xy$G zg7ini3Z4w&DI5SIC(E?XbV*WvoLy}GL^!JT;QM`qVC2k^)}gAFG3`2HkSxP)KzkLn zhE?z!5+J5_7{~|r;%d7};}$s3X1sp2N$a(_V{3XH;B{L7Jf>(|<+8(LNULULjULQy zS7;0QNccmPT~=0KZIg0E_1k4-~25j!z?VhvS>P7zNAca@dj6z=_>kfBKa0)j<%WV&T}@7T@8jy-Zk4 z)xqhiQjzSDUU2i>YxJVI^Oq++U~50g1!yMxCbie;^sfK<#fz(7`1PN^c|iTbFKj%# z@*j0zA8QmCuwYg8wc6f|5bbpE?b@xm?;if-@ZoPhnw$Ma4F9tUZ2wC>Ebfx?A1>{9 z2m)A0ptP~m1MG(aEkGf(G`F)v2tzxomDO)R43d01Nx+8*$OB*%T)`T^0}L>Muk;YV z08csTTN$Qi3&Rq=TV?eE6%JP62oNCH#FhTTx%ol?OrlM65s8z{0?rb%*?=n4s_1AG z)JOE*nV=@!HrcCC+QFj(3FhPCCYcmY%Y*W>&kpuaPY1c`;N+}X|Li0^04Xk2sWc8h z0}uGsx>=VR4c07~ZKlq%qo#Wc843CQ^(~&|sSYoQuoof1A4x`K)d>El55q|tFqjy2 zX7m}dAHd=Pa;nQck}=%&Z#(S9$f&DixBYq;tn?Pu9HPV`NbJ(HB)>ms6x6T~bE)h& zr$HOQ=x`KNs@$|ztsJg6a)!1~kiADgq#PAA@#TqRmM`Ir->GUN3y&djw zE{%C%&p{+Wd)i`!6IhiC&h@n%7WiJ+B1d&bG~RYX1O|iG66Q3Swu4zv%Az`CD%zAP zJrV#ND)4>a0gj$F-~>^FK&|Di`kQ&^`NNkc5qZKp28WI2M4I7OYB#-3}82GV{YS%(CFR zN794g6JWU=0B%hI?-l{%3FUvw0GPbqiU837pz<*T0REeRfOvodfafU-7%%`xKp+8Q z2QaOFtN@RH_V|zf_WWgY@bvieW_ACQ)}Zt0^L6$-pGFp#JKediUUpOK=hb|ARH4K9 zyaa224#;}Ee0|mG(rM#k_xrgjHqaCY%#IeE5q9gC%X4T^d8r|v^E9G_MSTuCzH3o7 ze);Jqs}CQ3@WBRxyq(vwzEd2(`So9T@!}WS-8bQ@U-;?oUi{K;{Q8TlXj(1QPoIAO zwk?kpfhb}dsF%9M{;&W1!@2&$NAvTKmV8+K&jbCNEmh_*51L&i~pDu?7aa?_*&4g$gh`k7xGmWHYgY&-%s#G=Q)NPQe*WCVWeTDOhXF z`YJd8PR|mXfa}5E9VIslWfB}%c;rKrUhXv6< zI!K2zCN_X`0Q3Qa+qUe6M-aY~j%I~gKphYSBj$B|O2FSU*Ka-SZj?K6$5~|c5250ro?iRI*J%2ybfcV1HOjapXg(f~ku7n95(l+T-PI{{Dyzz>1w&tl zU_h`3ARNTXFQdxtIneQ&p%{t-I50z6K!n5oMXUpX1QJF+h=i~irb7z!_Y3ImZSH#y zY^X~&@PFI#g9mCG3Kbu6Im{@2sE1|@PzGCqn)_BV4&VW70gzx2G2x0C7zDcD3ScK7 z;N<1@G2H^*Spm#|2}o9dlR#i{1ONpjC;-s`pytsA;1%_c9ROZY{4fFm5O|veGDQFp z1@ip3`nS)|KOgLN5peOe{&N3p?R+wL7T*yTuyU>GYFoZct0(uQNB`#5Yjz=sTB};r;aEe>y+* z{QSe8+(7dHn}DdZQGD~=i=Y4bpL$_rjK!b-sh|JuH-7%bi(mKK0y*Q~{!3(CN3)k% zME#_pD{2&9{QA#-xbeXXI)7aCB~1R{|3JXoPnLdhn)?anA07z%L2nLPPpeic%$J7q z9|8niLLAU9ViG{;2k1iwSPUj164z`akj zLT7|1RM^;V*-L4ueYHI$g1Jk#j4f%m=Ucr?0`u6pEfMhCSpo{gZzPb2;ewv28StQK&%0B z!ZWZ2wYqaUXB2&ozCjy-^9$MlbmFuIR6v9zY@~`;#qL##;x;03oL=i@;7GM;j38mcj<3R#nA!&n# z{HKD`L|JB$TQe?^U8k}iD!QB+(oPZ z@@1!rs#F2YDWz`R53eJ}zCiN(07Nix_n$X00N@aK-WbCh@UmP^wwM(?pdX2m@?Guo z8_wSSzc65k0B=LVd0gg!1mKJSz~4sYpI-meKQCtt;L|<;79jZlyB(nPc=0s3`-{K* z^jSONUfd|R+q>1Z=Vd%V<`Rq1x@I3Yo>o28Y$x0=xA*rGb2Oxg0tpGK#8df}r$R57 zUMbZX;sV8K&^|i~^Q=CL?k8JKRjPtGSebmoNfMMKy{W~}ekt4TSsET%zkQO=&xGyK zCO)1E3rn9EFcw=ndB|DtaPQ?>v6IcCQ4% zy&8o*y}4bTDAc3+7W{clpz-8hT{7*$=sPBqt84X_shoV(v$j~@z(^^#SSqJ#v*dlC zxouYX5ylrS9Dyle@O1`=Amb549fVIXH@n0{hD{m4D$@a_hls9l@qo`I_7Z^R&Dda2 zA!rqPI17OQ!+z9*;Uc*N{l4IU!AdfSZ`BH?SWWw%uo<+2gE4cM=#zj_A%f15MIH_l z9_VRC?d}xX-N;q_-gIQ^=x91%Kj}%}3(&wKEV3SHjE0q4HGCX;`;t|CakL`(%|xqV z?py8~LHP?OkZ^!XC8|!=s$_#e_G>7NGHr6~Y6_CTgj!Z;Y|#Tmh+a-i)Fx6oj*4nj z?`cFhc|Z_2JfsF-uO*k8I^P^7^xR8EWzbA=-)gI(5F8FoC}-BFp#UNVbafh6$q6Lq zTdTG~3mRBGC+pNItu8xC3Av1;Fy(X!GF#AP*4PFl|6r2~-SjCdXwV|#_gkrcDQQ3+ zF!$5?8yybF`%D992IMA~ye>Qd9X3orQ36^a0V*zDwpcEF=x0o~Ds*6kB>w?*=uUTg zUK>4$3xzPcV53XQ33XuvLRzr296VJ6zN>rQ|DO>EZ@lMm>I29F=MmmZB;a=|fKwwN zi2!g$0Olzj13-Xm0L~x)1_084Uwl{H{lyo}%8R<&6D`%FJ3D1Wf2Hsb(Q+guf+c-F zTUuO?`l{RUZ0h(ALMt1UdYuKepy#MAhvUNY=PT{yy>_--_F)xf4+&X(0Mjp0)nc-e zb$X)Hw-0CUw*oBjJp1X5#d_+&(4$AQdt1X>tbY;_JY|m#$FFzB6AMcVhbPw$4-YvI z0v1=s9#@O;6qy+ZE%YveXYW7$H=*OL-Fx-q>b-Xvz~x^gxcq z2v(=)_`Fsz6_`KD7aoXV+{oMMOcuM3SNAr>s79zn`L4hBU}9$*_t0jb<<`5uKY4R< z|9-1gZDX3=-rlV~*pd6ooc0|rcwL5B z^CcoduQF!WS{=k39r!U-G3pa~inbF)plQ{h>$K4d7#(h}ca^oD*W|MIYpe<@A&WK@ z-?|}7`;sYWqFq@SoNg3b=Ua;BJz!JjuyfYYm=x3ffG zX$1fa`2Qw=7xmP4?aE@H9R2H3B(+%0S5r^xIrs5#>nR32K&-Q+)b2(w+777W`ePwJ zmv1Lii>+ggXD<@Y&V_}IeJ+2>ofFr z0e8H4`0eMPA2KZr_rTQaJ-ydE#(O0&Z>G0r>dKW4YV%YbfWqgBGzx&#U7PBtA`=`P zq7N8jYM1~PfFZ;Y@Z|BGd)rmGcnF3!c|n%{r+qhW-r#^Jz#POQ=;}>)`uuTs9smNu z_Z=y)4wzu)fVAiie*~n!-m9ZeHlq#=l@aWWx`RA?K~Gp^Bek^rdRe`!5}M-uy_xlq zzIqKW5Wkgp4Rx^NpYPS%mNY5%O`OMo!sXg!a*|jTd@n@;;JsXI@3hlc z2MDnCk})79mO`pro4X%LT)p0gG|Lbcj*8yIDe!ln<{=Q-OSJges^m0!FwnmoUCa?k z>%`$p3?LB-y7!1!f)`*(3!acelXj+$olZqP!it=>#h?g<(pfvUf%osn)EJosx(xPJ z(uX=}6)Y-nzm(_!w2SJe6A@2LW3Nz!^|U>*cQigbtF`YX6ZyzgBiYb4<736Z`p}jb zXh`#q!A#7AH8zQ|Od6nW; zvesw_HEpHh_DFI8!RD)Q3P~v=bS2iKhTFmIFYEe#50X?w>$U0PMx9F=P08R*3m6*; z5yX3vg5MVCQs{Kj1fZXnl0ns7ewkg_Kd8_u)Df%Y`3DMd_d_K5UfcrwU0%irwhCrw zz{n;CnEp}z#8ffP53d!Cpbq5Ti--`HW1%gOR!2jI=@<#W+vT{ZOJiGtTCV*$(eDm}d|2Eo=l*`$_ zuD@{CziZd`%aZfnG4d0wCxtEmXOJ*%J_vRNBz|>wwUjCcvlv0_xx;}}Dt)&W+%HD* zQ6X9hsH64J@!bB(ex+Hj1~P#_xp?>{ps8!a^|{=>xQJSFZRgglhDjXno$wTArxVGo zg_Dnuw2k5GOD8A8<3~#i3zL%zOY4IM;6$RR1FieMM7(nFlMfGvA3pqK>Tq)E)2ki5 zfd9ReJQDzo07T%au>v2ID8DfLp!W1iHX-N(y4P>@bPUZ<{)`PE1APE}0Dd=b-n_H9 zc1LpL>U*#~^4U!efPdbktT|6680@b?9K6$a?+MW1<|%xChoQd%P5_6%jcZRvN3VT$ zZ(;}vP@$<-M%_StTCSb|`K9C+>;3&ef3IQTSd;Vf2X8N5E=0fhM;)3Ia9TY;LVr0)hNggEA)#iDh3jtWl9Y z7|q5x0V;hXka-%c{7>t@$kd1>7^J4ATjU7PDebdI>kVVPqpvD>3giT$FoZTB#5j1^ zf<_yh15v+ye7av|W)0XRq|Bms81<^GOf9z}n~v)4+jKb#UUk$zX1{}#uwYTiy(+oD zfbUhRyG)9pf!08&wpV2rRx+BXcv0gpn~4y^9Ei0a9^oc0{$DY(jym_N6gq5!*%Ab3 z4DbR&0VwAs7DMERs?V=5Uv${m6R1M(e~3G<<-BANXt`jyV6{njJ@A5`r2!>RE{KFH zuw_&_WEc8vAIiJH{^aD1$_x;WPU%5!y#RQ}`?EY?9#p>c{;d4pC4!vhfSic{FP-KA zoe2O}q?gkt@E@CiKYw@szsmyq=Ec*$dbVGgUCZu2L+Z~(BMq9cf6^XL_Bgd)E=2}s zJ%bguMwkX}a=UAs;!JSv2#IzHf}C@i-BzTCMgB@E=~jrQqBPDq zwmUw$kT?JF`J0E=4}Ws$aG~dCuZ>6q0Jyr7ykieAHP+MDQyA(fbkG+7(Bb3Jf!YTJ z57+e=YeK~TgoLo(lPrU7bkjB5d2;Q}y(f>m=@+2o+(SFK$&qmH0V_S&0DM`W^r$gO zNTy@x1)vL0NCW=}pT7yXa0mHtUt{!^GNzT3w?Xpp(D2$5ZdAWoe>?yefc>=E z@Ws75H9~-`s>q;CX-&t2wI|5j<3Y$k54PK|fAy70q&Yj|lkR2?5|5|;pe~&Nn=jYC z{P(pv$K1=E-CSvR5iUTz?3)LHC3hnp$385VD!0qZu6eY*WQVrmCWYD#S6Hd`4e|$J z`4eac)gF}#f2KISvW75076q^GV^X5kO5DD*=wo0Yl!yT!VYi0$79mJ61YiJROWe|S z8t@PZ3306y6qH`XhF!1ngu`iLdZ$oR=|K}EdPp=?UB?1oda43JbjIsz=!be5wK!p) zd;?VSO}2b=1z3F~>GI`HN4hL(MGtv1z%JFr-Kx7i zD-@C`ki%?Jsk&GJaBRp_o#zMu#7oo&^G}CdP$!SYfF2cC5%tSemncrTd2`^+(iOXDUrpzEAGpu;EBg#Dr)IIv%4_4}m(Kq~gP z^xsn}F!@_60O@%K1D;O*ywPl2ga&Zt1|;?Tmrf^tSb≪0levAG|vN{_-sZ@Xa^v z*0ZB%wEpZxe4|(fmD+~PSHc18{+xSXc%DmrRT|7<1QxM}@nZ1g1I>CHnov6$!3ZiU zKnfIcC_hr}iYHfVIzy$hz(81POPbDfSy5LfeATiCn?YPY%AsuNGpWHq?nR&Q$vaoQnaQphq!qJ{R+=5e22&NdFe*Nb4`qB8qhbNyO zoy>fCt-G&h>=8&{?|2WCzA>Ocq~z<5ItmQ$eDZ4F5X64?y6Yo=^?Y4VFJ;~_)luPQ z=R(It0Zv@f+cRp#j+}SyANJIve zN<}88B>@xAD!9zau({U$lSQL4-}{ngg_t(|MME0q2%j%3fAM0tMDoH z;lV{5fG_SQXn?RVDNTs$e-PA=P;$vaJbmbU=v@- z_l+f^lKNkT;-#MQW}_Z#f*KPe+uyyY*U&Q{1E@`)6DTIRsn`5`Bhyw`nYfi zCqK~a@Z$w&CNUiPkY8=H6LNwx5UGOwr~-LsmVpz>EM8JTxacpRUij}w@GJ|6 z7f8So5O|ga#@mOyAOk!tdssl=06e5P&{G899S4X8puV;^U7Jn4h&=r&zMrapSFE&4 zcc0?@ae(iq=lSzeZgF|_U^a>;O=UOBETY#as#K-AB9(}T3yoSzjL+_Q%DH+SOZUB1-`4bc?bh(j_+DgvYVt6+>|6L` zNooKd9^QVqzHt5e;nHkA;7JtseDk3)F1XN4w`P6?cEHKW$JfVc=^t_ZQ`npWy$s?I zgQbYyq4BA18ubybeCWXJ>q-6|!qJm<;6`81)YZ|UUci6g!<$#Y2&jIJ3n>k3^xC~w z{7PaFAR(5%97VwhmLgz-0E9R1K_#GpkN^h20O&v6cjyO3dtn>av~q1kt9LK^@cUjJ zY$e`)JtcE0&D+Sh}x=R1`Z!6Dac2b8F_asKPn7YXfWG1x`=hYBxrXR+@TyXWZ@7xShH8)YuXkjR*lelG2m;PU;7x?p7KqM~!C5wM%h;!#C z`>u0lqr=>VSO~_T-W5YMblUQtrGfHu-m~<7=K~-C0B29;f5e9p4M{veri6MF_&=I+W*2kt0z5Uq z@3PQrXLIh<#^T1^VsY77Y;CErk{#Zg9-mpB-5b8XaC_+x4#45$B6OmX3Z z*c13AeWY-;2SJaN0>S}+0FVm)v}f$oYgd^Gdj1q@Z-p&CYF{!zneMNQ(N4e-?q=-2 z*Z1Vk2vtt17^Wkf33o=XNzDIihyprr0)%RilG(`a4pR0l9x&^%@R?2EfJrXrAjZb4 z@{5>gxEqyd-L%)zW#{4o8aoxB6dZ6(Jeu6b44`e2OI2 z=%M*q13`3&hWLbA0qqm4w?E1tx9 zBS|9=$&b3@l>(~pYOUGb=Mm&W_js0omT;_jkok^`G5HOqZ3UD>-PXSSrs;$4Qv z=6lu^XDAy2Yk<7uhrxtRUC3;;IJm}Bx}wFbl?9;s%R4()S@g3dWnqBu7ZtEH?aOS4 z23YhFC-TCYxId5< z?-!&kpZv1Yeoo0rwAUEG{(If++yBzfe%h7tW8pr=PTzBUq(Po>CRk4konDzibXt&iXSd|`O# zaCm*@7IQzPKQuH2|F8Rzv=5jWo9LCwlm`NV5i-5Hc~^iKfS8ZNV7!OImQ4TH_oE=} z8JfD*J%YRs)PJH1#y5K79{RkI?W{%$laE9y|6uE&KL}yB1)5Kmk(!oKqYtTd>duJ5rchb+LP%85 z{wcf)C2Mp|^UkZ1T;XGq9k}o7WUc~4j3;N4hz08I-Y;H$@mpV9GzHL5HSO9s-E=#g zb!)_IE>&kupz6}GrqjhmQ5{z&NcfG1GTE$6t+0n(w*LEj{c<{CN+6tkegD2sleLO* zwMG$&fB!vsArwA|r0M}0mjRVZJuu&?f_0^|YZQr9e8Eu_%qFc~O*kHiV9Ze;tnyXbyYl+^jkg9(Ti|bO*zgYG0oy zmuZUavZ^jB19fLq)h-vKwMJiGGAwlqV0w|H1QH~h_=bd&$!at!ct+VG0+Y+kidy%U zHd(79FjbJM7aVO&6pC3+hnQIdRWUT@cP=_bl_KTjytqWkTT&UpnGr&Q!XlP_BdZf>sy*D#I2=1}YA2?sS33Ped`C47L4*c)?M3^q7{HY7hf zoY5|1;1Ha-f~YT;x$s>CiL-PxkqzkJ`Uka8nL*o})ZK?N_=GN-;gZcvnd=NzjDatp z_oJ=x`k@T!E}AaLbzYi*n3M$4FWCghE?E2|1Zqqd<+iur|2r1I83mBHQyt*DAP`Id zq@SFg0jQ47HUO6Z0p5B6eLypEwhQ1``t!5;=Xt6E`qkayU;TdRw|_hQV)3iWVtM*y zIrrSNpDPpKAs&0?a^*duC|-X*JzdKuhmy0EXk4s>?eISfDKfCz)!9Vxct30pg;GgD zpPj3x5{cZ)b|oKuKkX56kky4$>3M3SGTMGloIq}4albTYpP6n>%qYX5RAXj*E3r4e zu(0s?^`)gxfd0pqPU!Lv^Lu*-gZP5_0bM1g)=3KjVn6sA76vGHIUc%uu1rzGSESrP zxWiW=7pxq~}Ggp`0tl3)Mue#We`SA4NF_=JckIu$Eh=|5-_sm^(m<{R^$i<*~A-@%=5s5h6Gr~Y$i>^;l)c&~M-H1<& zGzs-pl~Y1inJQM<3JP;Vw4A(Ah!%@8fXke6Aba4UCvj@@GO)g*$<=1 ztXIR*gsd+&U=CD^6G?}u<^V6tx~!&9NTG_cT*C;0C!iOSU}oh5 zl~O8xwuD}t!~;;6yP6uAd_W7W4?w_n&JSwfy7az2^FB134^2d}n+=!%5fEmTD1z4W zHZKXE3<5gZ7s!BQf?n`KOR|}f0bv5b@gN5qP@x!|ts&@{3?x7HdwsWha8-XB?-mxnkpaFec>r4fbhtO3{1meT5&$$!53K)<8!pKgW)nbnskX{~eF zu57gc=ZO)3*iQ2Py{ZE{3L2`ux@&bn=P6d}sd_maQe-59UNM)#B=Er(oAr=Ghzd5y zsistQ&Y7MAl=EnHT#4~cwsvu?TCmUudx8TD`nU~tnKDj=hX0-45A^G^C994TL2b9G z2-7EbRHM}(tR+E^=ta8zpwgPHqxI3mb0q|jyo|u^_u>2$L7bvkAtyE9pIs#-x(!$#>J(YUOp$cPVX(LidPTf!wGfr4QYZwyO=r+6Rq>dhh{=Q8_dako!Rz-87&=#DIHGAZ zV24T)+N3BU1CTsN0CjA@JOuq7nO_(B843(CFF`pp1=s^v%wGC^a>yO(84Joim!2#! z!v*6x2mOP|kx^Kr@>}^p=P+6fI+Mi)Q^0S&sIb5s?7v_``)43S*yw`(Z@a(_!H8uj z5kYbngaACm%`k%;<})`C`Tya6Z(mM}|1AmV%nAevz-b;3&$H{FCm;aLKR=}Tf8|sL z^wtRY3_U80f=@ewZHQG_;{{PoJgtc zxw}}(&9&W$Tfg&Z$HGELQ*^JJg+|ZJ7U2KFikmigsu~VFc{&o+I5&0#V260;> z7v}vK3O+ml=m?%Xp}Ii;2od3HPyzNeVE1wDOC1CfU!sI*^g#gc^+78DEOPc zK<|%)_*xG~27ebmA~l%VQ(KXDu+>)n(AA z;#J5PA$w3~b?P;Iqbeb-?jkH+OsDEZ`YDaF^sL?j@)run<6#Z*9i>Vmimdvqmh^k+ zL~{?C5XPVmMM&k;Bz)hmCc^0r73%*^SV05C0W5bZF&`2YT|WLa}I$(0G-}Mu#vd(p;0xrq9PDQ zmCBg)x&g@hHmyPDql>U;!!Z)s28>#@l4v!PWCf@oN&I!pNO(X@$8LZwALP#j>$ktl z?$-Ea4y7stWS}A=T;31yCkDLO@3fv{Z)f#iAUNCsn0Nt$Ad6Xk{<22(fz@wQD2zJE zY~egMfEN{hR)J)IDbE`ex(iCV5fPwYi>)A9Kwv=sxfmIPbvvTN1$kIRJwQ zo-u*X%)q7BQx&k(1yKKzBQQjOv)-RB;H>$l2{?5E_+JEo`=7_gHyhu*c>34n_8OyD z>8X1S5BB}gJ}RwJ>h8+(+{%7^_Q!+iH+grqm9Hhs==!ToF&mgZ-1qoq8~UK27u6rF zIGy`Kwpz88=mkrh_w}rviLf3<#_pLT3S_6|`Ksh8FaGh}yMJ5`Ch_kcn;c564=;?x zluZtRrKRzOkLdztBna?iIf)YZ@EGrw+|;zPCgs& zB@%>`&fcDx#^exJ|IjGrfM9cdT-WG&;R6&9_YaNtg4NT}pVhyl=;@XkfRQ_rF-SKT zJDWj12iIpuG6iJWUmLkO3i!vRFNF!-7#*QgkbX&T0FQ@F!^G7Ql!p=pxW>G2eCO-0RWez|k)9~t*(_yu zH(oB1j~$OjW8qmWP?v3KyE6K^x`vE3@ohMe3%*D4K?07cDFV!zZ{s?D~!sJI;bOMO^64BIwhb#K!b`yAntC>&wsz_R@k&&tDqJG z%f6jImRZu-WyHT)WJ80vW#Jwc@TmI-S0&ppeC|GqQniMK zv?^_JW-OVsv-Gma)E-jA8~A|`4&00cX4kid){}bBK(N4SQ64T(d0B z<}%T22D(XBw5q}tBtB>X;rTb3GyzG@fp~EOaRm8ObwIzAs=NBbTD=gxBqOlI>#ML_ zypbz&REQ!SfZdE|p~eXJ$5zgyGdUOWrE||Si1vm02NzwI-1CeW_Dc|*8dF2RUZL&p zG-@1jkKN$tN~mG`DQ%rf8=*b`foevG(=0)+!OJcHLJ%bm>em$r$&mgjGw&(b1W>(Q zUWe_JD&UZl8=#Y&hal{=#&lZKd2_!GC4kJN@AsoCR_Y{o0Vxa2;IbkdG}~lmi~It( zq46B`eqL@?DoF&0(cXIva`1rjawG593`&}zcSc|o{r|23&zu0yP=GTUFe<=P8X#_f zRR4#UD1EfQ5(${MGe1yN0MY>P{~*8@)fcA6z4 zdQ%bzz`2_M!@v6A>J1qEKzLNG1PXTG=L1QgS7H!!vj@Dvr+TND9;SMF>GiiU0U4#+ zr$fL(glMrRn^$jA|A-6G6>+q1Fcf-5aT%aTxOt<%2MU$3j;&isS)v7XfBF9F`vWa% zzLmf-!-i<(d(wK?JR$5`9<=Ly?RK?#=f;!A7=PYP8*Q=oo{i!K4!dvh)wad#(x&3C zTL^V^bKCvzJ+B@R=*J`B>ta{!w)A@{53Dj>`TnU;@4gaC9-Tu>kf$Qno2hbza0``3 zEqiYo`i>lbTqSJ-{eps%fwPY0>Kzddrm4u{qtTrp02!&9pTaml+^1GKQO|@DtR;!>3?etFFQzS}U9<$T_ zJM~T(h9)Tij*->z{zo5(FI96Uvqooz#-pFhI5i4cRu9f*u=_edXoLoD&x{-pgi}Sz;Nw%mZc_0i!0;#Q0K)LgbQR zFXrXKH_6ZYnf*;DV^SSvQ$zxX26uI`p{_Wjhxi%O~#cXwJIx%t6sheItm>pzN!c4n0 z2pkp_2fcC30rWvl7t1BFF&pGMWe-xp0`wJ>z*ny>84Q0Wd3*RX(z$MIV9sh55NaDpC> zsP3FcyzGm6&5AgGM88@KA3!ezrC@+L1_l)%0?)Ll;;z+)+8(3F6VIxmA$i7`TLgH> zRZ`A&yCa{UEc#FcPfsWHjCM&MlK|O)evN1f`Bj;8DoU_QIL7!*s$_j<1ni)~ULS_2 zzJezLssPvm2%szMj&|Tem00=X1lDj`k}_Rr1`0%INY4+Q zSd#eCBR~OWJ@_^joQHG>@O%dVzT*J#!jXi2COM zc;^Ht)&H;l?e4P|zsS&Djs7*6z4e`E?c)Aii7h~BF`oVE_&B|oWCghAe*gISJ}rDU zGAA6TDibIGR>M1@hzoQo_kKDj)J12?r&iYQX4_G+n?m($z9~jw6^I|@cAC|-#YKH^ zzf=M`uE$F|jj2byGYdWAy~Edr))#(#@(|+R_1g;^081y_`&+|H3oru4j~M$;-W>8g ze8Y15_U*%kz3Y3wvN!o=X<_E!cp;A~z|7>rWRgwaXa~=s@eb~L$`h*&^um*mPKW=~ zNAHbI%zg?`eAwP|s}m1`0pNHK2L{`|kzNjl;!Lk(4MGob?X%BDyKml+OoBK&PKm|P zh0Ft+dwiVL=#fKe zD~8a{;vc61av|Zw@&k`g!n?ZsC<=8m(HVf#9IG z3K|J>ylW+pXinG>@D37@APC@qD(f64LTG<(4iqSxv6ObMCW&lo&E|)bGY!pFV{5eH zORhJQGjW@r@!71fsy-X3!H%F@)oB;I2zIs$dF`!XZ+{XS0R{D2M*0d>&9$$5NNl_}lL zm>kHLG-Ny6##gdr&>i5&fi_@?7rpxK$IS~SS^m%yh__#h(oh9ZaT1?0B zV5`r4wBgB$D^AGpA+glh&z8!yOJ&b`pbl|8m3z5Zhr+&?6QZTk%Rpzs9j?qqhI+0J zgU${A#rUtY_**)q{w^$V>0|aWvwrd`*AF=XPEObjJR~9H@Xedod$0E%q8B{*cJkAc z-k$aG;h~Q8{A6K*W?|@Pc&2x{hotVIt^63+AXLDi1@uB=y=?!$_rV1D5Ka6jqJXOq z1*8u>DfC_$>cQ~)CILbt(26^@#-?tK^j$*>lyoMOs^qnw`>d}UQDHCr!>O+*;{g<6 zQb-^b(`ZyjMqvg#8FAcS#$jjq;57!EzF?%t4&S3uD)lhS@u|MQ%I!M{y&ZP@&Yj0! ze@XJk7k`rVhh)b34V*x0BlX=y^nHR`0kzL(OuGZ>4BxS7F*GM?G2-B%?oW8Kp+u)c zv}5S!p<@kTNr@~cl(p+Jf*O*kCnW?Dt7hx;d{;4O(5l3E!mSP3!R=wC?<3?(=%aCk zJ|$@Ve#>!c`aVs7ubNiGp(G|q4#w}px7-?3x-#T5kPN6s|EJ~b@CD6|)2sizW9&e& z%`Qj7sjyoRve}U3z2i&H+#2(N1Q)1D=XJoqt(v=0*s9fl=@QAtx+iQU z?=Cf$twc#&2kq0>!op}39dM0tzDDogs3ckHfdxdQcb?tvUhY2IeNm@Hz)obeX;Mt! zE){J|l*} zaQV^*_;#5wKomnRhsx&gVsGluLi_XgYe@7p4-6VjPLWg}S-@-G=7CaaC40KiFE3=I%-faECXl5)Z^4T_zq0V#f7&htDI0l*7< zy9c<;^Rx~4@SPHfED*{c@b4@LObP=!eR2TM0!TW5w-Df8{_1zTySq>8zxb@ZSoS=P z6zlD1`FSdL*B4AZ%?hcz$NFQ>>w|Lnqm|rT*qtDZ;kjLy?#)N;2gFn}9S{RRu$h$_ zfqnbo)wJl$;U$_bwxNr*o0Ycb`7T@ATzPGOUyLv2HtNFkj|UK551Ln}dLK;u(Z!%m7P!OK%R3mi7*BOLD=X zzL~9|k>uF=)yT>C*5P>15efb~qgRRbox0LJG(OWeKFRujv~TkU5CEwiFo%EwrmzD# zUHKQbB))GqZ2qxs+J%lOzG|v>U77}V;?D4BO0F(WeJV{9^b){}qY{ch)K~!!f$qO> z^TtjeLj_o}FaQoGym)%0xs6nvX%K;vQQOLSa>Pl6ex)*z=A)dVNGS5Y9iYW43d#5HU=vn7$16{@sK3Z83M0#uu^MAk? z3kia6VA&U_ytHBu;LLjB0fQK}7!oZYg}}jlm)dO~2q=jWRt5tu7c@Gad}St@GKbP| zK2sT)U&UZ)NjtKk%!f29PFdz|da;Z_&vY{}IR9t}K+oeIYRpjCQS>@VyZ}VLlXGp! z+HZ6M1CZuupNRh#PM{4E)3E_cV*W+uN0G@tz)qoxE&9u~wIU5c8*-q-@3G?SP{k#I z`4nntkx{G2PEmBo2j*w}p{!WC_+H2i-dScp!LVZSq%LT10t;zu%waYg&L`~p;ueRD zL0`%I?=(iWPy`(+U=v+Go7#S5%&)SBJXV|6q;{y{wEqsWKCM`TBKju?1j9e2)p(K9 zfJ@#$%Wfnh;5=78XaTW7M(W=tVFiv$8KqI~!gO%iWU?s`^}5&|VDrg|Avu3s(7F6p zqifZ8{sQ|xctdc4V#_WmAH;Cpz(I*j7#xw>_wyouYY55^@Ky-;ZWC}u13I+<<7d=A z4^9AS2;eD20RaF&1(x_f(h?9Wa40agCfm`CZz|>J{_j660nn6Zb6?%f zafR&*rQFJ0r8<8!Z}(JE%w){Z=j8>N-Bm@4rP8YUJnJd60hdunW9a{SSFZ|~v9lJ0+anA?9z zS_6)cFIN>n!0XaHFxfHvaB^~Mf#P1r_A`GtLvr8NoAJ*k zMxHzvlGr|tEf#x2JtON&<9(xjtn;SW5^#ru7DC~J5`2SI0sP-W0Vse~AcVr+Q@#Lj zAhe>Z#ME|!Cf+JHWz&CpqYqI5pX?sJ1~@1w2k84pAPP*N4ji58V=U;i8kc!+w4uhI zMapjvCe#j%V*lmQDG_Kua_5aSCTd^2di=!~Uw(-l=$$V)1Rj_!kqiEEBe(q^t%`FE zPivJnPd-_;G9HKuzmCLzt*&2VV3`-D&(?|v^t||W>Jmzu#iCIF&^at-t3RU>5`zY1 zDycR(1s`6Y_qovx{2qHqPejAA?Be^GtRTecgFQZ<9{i0qMNM;%^Xc@W^zg92E9rDv zwL*^rK$nTJL+#cp;{sC*7=j|iOhPD}CIu2W7bWt^YP53xq>n9@=PMTf%JK8}F3rtV z!(wGLnNRMi14GG1q0kf?`B7=H*QgaMl75#MU179Mk_^Ev$oUF2L;$GyNL6PLh)R+m z-~gBar#^@D0QCJY>&ZxMw7px5Ffp5QS6LEN#b~64QCOwy2}_w|f-y#(u}UGRG9Nm# zMtNpsjy0$)Sk%O`a_NlJ>p3=+eziiOHT0u@qWvaxqi%EXnwLPuHL4<}~7J$fr7v=c2RuD<^wC_P8ETYU6J~aUZ0hrdEor zRvkR?O(cBu`p1)=TSGmQkH*J;b{%~`vY(#;{^#O{48SD7bLnVddI5Iu;m<7GhWkTY z&=H2gzx3wwHw%-8lUqlJlRcyBV?CpL3x_kk+l8S50bx%-@zx91p7ag%PF^3!akK*@ zfO_v8rVSW*(mggbhCKi+JQRSfg@4|Z96iT|KOGw4<{uv#ABS0lwZN^YHrm%cMkDZZ zHyyRlkQeokKyr&9Ac-C%C5|BifHy$;Sb>h>8n}SQyWC;`XeH(u1?C5lH4PfLa`Z3r z5)o(_Y0q>dGMy~E`ukUZ`o)*`?m#D&BEVi{blDi>M2 zfL0lHM?6}K*kn^+Qx6uy9@HBk=J6Cqfrr((1nW0-8j_5Zf?%B^APC1B$!i$dOF*q) zd!g2P+*-S!aH`VjyQD^zIR_T4*>*_GSmaEU{+0J!cQdJwY31U38Rz0+c20~YYlVEk zKC{)#H-;vn5z4=@wT{^r;-2C}#G(h;ug&nmT3%xP7r6gxji%HdG+_1ZB%{rEvaibi zA6a10m)xrCZdCYeb8R;xb15a;(TSRX>VQ9u@!yU9Jn9$xvimx#BePig@a~!mR85i2 zh4kSfj^}oz+)B2Qo>hrbrBx^mf(BWzOsjRUypPdSs0;?RTH^xbkD0a9vrg`?n{iH3 z_!a&^U%#t!IW{0O`nzmKwHjkk*udx55hO?;G<%^4kr+Y`-`~Z~u+zrYfbu3T#4C5{ zGb)EHc;9T+#Lf?RO(y<$6MKNpe*c9_a01U?@cX&)NgI=$^FtrNk{pP^1n#f4u@+SK z%kT-1ltFU2+Qk6ymIn0J3{ZN$l>qa0+5u4i1cG5WbeakX2~dLm7y)>kMS+~Q0PnIu z{#OU|BK+*Ho<4oij+9<(NDQ-jV!yoqL-zOgfB0)psS4_6*QV3C{rB%W=jviW%+93) z?uqF{Aei>(!{KEiS8D~*X?*IU`|OH^`g;*EPq@Q6@xgT%-nkcl_OiXP^B|+iHqgX; z|KsZ)f1Hkc-u!s-@Z;g(-l4x(;JE|?@bIUvbMY@o$lv72ZRmg~0vP}f(FI-y9H0mI z@!`qg=ih$*m!^2XtNZ+V?;?rv#L<6$| zlvjSE&;h8BoVq%8tI!Q^kk$Z2aG__UK<0Q~@90&~$Esl-yP_7Hzj-nb;r$}X(dluR zM8|-cI{rRyh{{dYwm1L#?;m43@Wm$ob4UUF!f#r;@!$dfPp!?lwA#pQt5X^6N^E|) z_9tI|olYAKP=S2Pq>xg%JOFsad~3w87!T{?uyk;?)d?v1HQ9JLK?%bP(u5-NP}1Y; zoJY&6B|8zLZJ0xvpc+cDIz;NK^dT_?h3_sm`g@to-M!8(7x%kf!;iuF&hN==LJ{n1 zp%R28q}DTpz*NqJ-gDgD|Ks#xIwY1uVos4N$>kretjzr}`}X(lW>g!wxsc^9#GZ1h zx)Y7)W~cI0J_69>%ml?eG{JO_DM$f3U`-6J@K%yf0Sl1+iw8ih!aPvL;~R+|1zjvO zi_|*$v4$lGD-~~{{RiXR;@NDWujUsci1Souv6pJnH6@$0DMcSiv23B*TsC$ z$-qxY$N(kX*&m}1FtJSKgGw()M1@-0WRd*;YWTP$g8knfy&4%y8G=VOuQm)zUz&rQVK5zdp%N4nO($liQ2|Lk}eo5H-*zydDDoeGK^b`Spi12LEvz9N^pE z`;Uij9v<$!Im{0=_8v}=%h3aNckM}eX<>M*@6IbEe?wdApaDz0Qqb=xOToSZ#GZ~J z$N_BrJ05I83%GV~v-=iepfT8klS{wZJM=rllS{)hOV`JT$2pX0dVLR31J^#22*EvO z&8IkxU_g%b_{|Y1M1&!LmpdX2mk(OYC>MN#Zg;|G@6^R-`3?&2 z9e@EG;PA8Ds%>v?|J#2{2f&lf?KTdhcQ#F$n`_v8fC0$$8sF^veYGax)vVw9!!I6x zz0Z;;lE9xMZ-JAg!qVHLRdDsieF$;0<(Ijz$_&d*6B4L$1z;V1p9=IbSvT!}pf#AN zMdPXPoXX=?tG)QCD$?03Um6xg_&aGoGaUshcn-RZB)VvQgSL6-1z3o>yjpGD*B`Lk z-Mn?)?+mJpnHBgyVmT+^&`M~(Cfsg2j55cHB{TP-^#e>wYt=uN5O)Bi38;O=c>Y$k zF-oQfQXncB*Izn(rhDF3i$vphI}3y@33@=x2@iKfS8GJ9RX_6Fj>FbBdOunXydU&-Q+$<4-CfNwC#;W&H; zyW|eF!hh-*>h;mhBOXxp(>qY6{tquZ{El<}PP3f!j}KY+$xO%t3?_6)Mw#e>^W$d$ zcmbG@5#aRkb_2k>cV?icZ@~Y6fTt=zguthPpr<2%^df12&ujo`0?w?!BpJY&3YZnZ z#xMTWi(mc4dh01-i;YtK$Ai=l&!7K(YJWewpPGKI{{6W*SYJ_bZmv?r?l-;BF4tOa z(Owqe^cUkodU_UdNIDz!)mBpWKv>L+?&v5Bf{435SE_IPnz;SlT2rf#qAcG0NGdRo z`Ad}_Ur&CDfM@vE|6-CY-w9?wA5YGVPg4I&OSb_4q^^UBB76;3{URCHga<<_1^0J6usaz(#D0@ zEy-?t=+UjKPi}Nvc_R4%ureGQNp2;fAtMeRZ9-JQ0Dul4;G+=;_G=WE zasKu9%co~RAh1^3>3c;701mi`glFf$y-l;F@Zj-wJ-kyMfI3SWFp6ToTwA$wkMOYF z^R8Ktg-FdyGLpR+6)XHx*nJ*osOkFlPPv@1DEt<^773n;`VV32gWXq5^sU|>@Xa1X zc0}0JDe0euR$dub3fZuoE-mGmvmQT}uN>cZg&_nZ7&=((3UX+yE^A$~P`0&f zQzqh?aJ6+nOt8UkNvBo&Yddf~gtDL%t!3N@vsM+?f5_wtIZE|EmdoCMww5lB)Cx*V zDh@lU(ctoLP{75l1{%k_wChU({e{Q03daATMs29pjCu+k(0Rm((PnK_j9#5^*P1{B zoC|1yfCOtS2zJ7i360!r&2{4hig7Z>MI~7#N31HnwX0!GR8`pa8xO7_|5FGlzup+! zaTG;a(=51RR!>;&1;Y@P3P??m{2>5i2_BD|Tmix4C)wMj4f!qf2mO*DO4r{x7_)WG zQ|VF+kP-$z#-eJ6O!E8H=)3yq`i*jSgI@Oi$a=Wz4Q7*89y2i<;0i>XxKZhMoF_++ znDYb9Ggg^Ev}HUL*$`P@WMNZ2gKtn4U`Avf9eH5>wlUD3=QZQ8PK~p zu(KWDvuDX){OY$ip6nN0pl*q?p>BE-;bO#|D*95D?AqMF{QT2(domPa_>AVc z{VD&~Z&p>wA5W$}>+M+hi}9t&qphv_PBlM1{AhAXIsrZb@uvY`E;#(;=-Y*#Ib1rS z{xJag_8&k0_Hb|V5WMf`i$G_7teL}wh`$(x~*E2gTv6CKsI`-L> z0`S4QzsLbVuW@tp3giEs8{Lv10Qvz5nCmbI@c8%9`g3x$FybBbdBTZSFhI!<_G{hu z6_<{i2eb2(f5g|B*hx-2{;kJfeEG%K_a3x)^xfDj$ztWrm#X++S0v%iD?9DeSl)q8 zC7Rn$9)J1Vn^?Zzn%)#izCs-z5y<0bF{jF)#H&Sj>X>jCvAUMJ04#nqPK{Ftx^?30 z>i5`yMxt>*i1?|uPgvU-oY$!XjPoJV8=N1k9A9*zAcWHgw+|=70ak*1-e*UQ}sR>U2#T+WGY%zp&!OChUTD|y=67H8G(a$01US8_WOWmTmO zpHJ8u>S)>t14ZIb;s=0#k~Ux>SzzZ^L87p=6-Z8Ojn%^L=ICeU0?-WxtzcEy?R=1{hRmVX$tx6@+{(w^HGHHFT^8uy7WQ=R9R-MI)Te`x_wcpvVBrHe= zK}xa??`In$33;La$d!^HK!cpeh0Cwk-~#8v6Kp`~#sAM{HM^+o!2yGr%wUICWhEgJ zX<%^PX1;LADzlQh?9zZ5xcZ?7%5@f_0n)CN7N%klIM?a7TtET6v92%NC?;(@9DWUN(>2*qbBARUCsnR!1y>e!?*r)WF4xa(xk_9 zc)jD=od-9h%t0sw2#E2v+nS!<>bTjHkGSs>vIPxjmJqIiM2b#;@;Ugv%R)U~djGhc zXw1Jpn0B`kiF|G3@vF!89)E#!CzslJxp{BRLcHNy;ywEcD~$XDbzJM zTTTFRCwvFf6CQ0uvcuPgD`es*=xtHY?!++#DhqN6pqGY!vb|BmlLYEjRV6R*NZ9Q* zc(ur1!h%!OBn}1)>UcUG4p~)*_tJAtXU?g?_>)B;2*I+?H@~bl`#NzIfws`uIX^!; ztCTrXYMsxgc3Igdr8BBrG>6k$J*OzI+})7Be|IBR_Bh|4tGClu)1{R*Z2hoIDC76r zoXL+hDuG6EB0o{BHAX4%LO#mH&XixXhNIQKWX#>bc|Ba%A>MytCJ}B_+ysB31KMh! zH{m3px@iE4Py`EAnOHz7Ncz~$*@}?R5k?98W&i@Tp0G?`FQ%-1dDqqhlhfwc2jdpD z9oU}&mxT2OuSR9DE7f2oYCSi*dcc$l=;YoYOAI1_6iS5&W{?(@ANe2#5(0<_=p^a` zf-flmHaLO&CVu6O#kw3WyusQ1^*eM}1*q8rvaxqqKohVDA#zlQm{5VKf=Ol9$#g#Q zzij8_3Y{emN#FwHA$~tZKbKc=;etX%vrb^Z2Qszeyj2DgpfyWo;Fd0#so!04u0gux-0P3F!;4KH>AKtM5 zpS8c)|Ltclu;?s{#~am_8@cZDVyY~Z+ZcF0PyO-p_+3Q3K)@gEb1Lk%T5ODhjE0eF z3PXVUY;tk8PFk%!s|>LN4D9V`Fc6QHE0L)4Zg%eO)4O*|&TP{cn9Z~Pd;J=iOdW6T ze}2kVo+Gr4ed{rWJ+0ImPRAd^Sc*o@7_3^1kq>Ij=FPc4^_j&z>kTS3!Eg^M-1|Chk>eaIWKBBUqs!Y5{-m_?YBMWG6AsTD zrd4aYL0kY~%*B)njfN(m)Mw_hqOY2bqOPz211kCM6>%dZsY8g{177RwG9UoI9#nS5 zSrbH6I#HkczB2}cKkL?qGRXP_9;q}nLW0|%CS-@WKxj+pi05@2}?0xh7 zy*;`7gG-$F_<)78^||yuvhS6RlI+rV?^{bBymy=?0m9?Ru9eFnh1;Pi!v0RKZ(+sQ z%qO?<6VmiAUG5X8d?M9CrQxtPYu!WfAXdKJ_=LsX1-1s^iAp>f!Tdw(P8Rx->;;;5 z0u(_JcvI#2Cdd#(FbpB6D*1uCqebAtA}?`$*{SkGLlzS_{{5qrkPc=)mq(1NLiRRClTk0|8lC<${!G2|48oD>`AT&7uG=;eBdBkh8S z+k*|lzd>n2E(|tk(pk>?wIl`-8K}B&UagYZWF(?1400JxV9Il_e=V^OQ7S_TBryCU zG=aAi;Pdi#)Ga96~IhOYop zYHroxWa#alY%uj6=fnzwXLz<8&F#9A`j5)9+OE!!$N9tb^z{7f$&Uww{;h@y&70n9 zjQ$2b-s2s^<9vtYk9>p6jqa(TnWc}HmX@wBNSdINhq!}73jF-x!ka^MLW}`Nllha5 zvFV}0wHJHJ``y_NXvMo;BV7-ZXKi)V;eneK5$00Q1KN-W2S#khyhm zKTkeZD<7HPo0$0e$(=i2-1*bTUw?6LFG_12~n|D8G$SG^r(juo?IceJ|Q)-#CN0&$H}jAPLqjHjKUa4=~1>JRV+ z4QTlcB27UdhRuOcx@_-KI)xmHev|?6ut$u8Bu4CZMZ7rJvMJobfnX3lATgq9dqjgU zARHiRe{XjF$M0>j_nxM~A3OxYM$_55#t+{A{ZhSb$^31|>G#jI%dU*OQi;dcz$B94 zIPpZq>4`#qt1*<%gY{GY(EFL|*Bi9-e8lDOL;(l$7#=m@3N#C&$w-l*A2-QnF=8<} zYZxzcTvRapKD7!Iti7Yuy}O!hMr(8gl3NII$Q%F|QRqE_$`kQW$X+klUC=3t^-#v) zSEUsYfVIA008hhUP}ylPAdj>unE|?p^??l_v!$&rrQfSl>HI4E{1PnkO`SFg22l6I z{m1VQ31>fXz!-K8nq(x2G8Et!%2iKjxJ^cdS`2<8e=l<4;01ZVQrF*wgeb-`fm1`) zKVXtwP$^7aD9<*q1}{*cOK0&LEe@SU5wb`Jfa5Z5;fk0M4bnNC%5dJKGks|G%B==9 zA*AQ1ewoWEr30UB|2PBAew>kjd2;{512}s?0+xt?XI@|&0&nAh&f-9+f6~A{;{Jap z1Ku5ScwQy;ml7x}Oxf+5T-;C&v%>KHc8F(}BG28st7H zHIR;-m0eF^o85cI$jr>6q49-Jex>(hX>xe);NxHUxyJhXkl7kPnj8mv_^E|A(#m0J z{MTpTAKrdAb+d2bQC|CV>+6rMeJ06!=kYl?LEP6C^vIq`R4(<6NicKSQ0#%g1YS?A-G+(#j#(Evd*iL%`S*44YuLDo!V_T&;{uLC>;Yl(YvWp5JnU;xbJN@ zlvbuhC=OH9rRr*cAPUd!n6#XTWtF%7#{Hkj} zM>CHg$YsPAP&r_`-$e?5(jZCs!39YNWZkdyn>#s0%vMB(Ug_+ykp-aRvy$(Zp&umA zXj9-$U~>2~8eGXUVTX+b0767uHjCGM!J@XPK2UK2fCOOdr&5|@@QI{3fKvibBDg~r zjYt8Ew)5w33gjVwX9V<)26Q_2pM?US`2h0+J5btxmVlfB>;Rxkes?aFXt`@^x#J%uK=<}@*{{-fbH};#eho)H zR>IXt-j`Sqijt;3)E z{NYkzbbae^3^0y*9X~|>#}jO>W2D+gK6me(tA&x;Vb7JuR`0~<6l}gnw>p^iXJ)YQ z9hvO?*&7eOBmnT{lgf-lGPpjBr^us8m%{yb<=LQi&r%ti96tOQ9pcGDT8-PkGd{I- zc=XA`TQ?`xZx0U%KfCql)0;PXhB|KHGXU&ZjTDple7@P7J^+sh+OYwp>5haE-nkkrzSoDJLCy8~1A z)>PGd_tv)S<(qA@FA(f;;`T+V3V&{QcYE7fsixAJ2aEcwRp!9xDuk{!8;UN& z;C1_q=CWI>6zLQg?v3qa2RjgJ;0heU(|aF46q%{S7@$)D79rz+?jHXhe3S%L zD62381icW}hdlwaHp8f@=+xReHEIgl>Ii9Fq9d#&kjG)plsjFq*gz*nC|;M*5_b37 z2mrNtSCvW~NumAft}dOLiJt(V&iicdlpp}XL7g`!i&;!IxmRuWn=QJS#elBI06_p9 zKcJD#WW&-!;{L0&CYxFZQ`zQ#0tr-j-m40_940TWgqIbW%i#8wi;l~dPSgoTBV|rR zu+`|4_&{RY&%)60`@h z!RXQJ*&kmIXak7=jIcaL-o3q7davv_HLz8v|JnTE$=1;1)c7zAKAHgNzn}o0%s>G8 z$$$LG!H(mZ?w{}gB8FS-(q8ruJ)Z=AKNJu`X;Y0 ztq+Zj4^7-a7Bn>SZ?87#-ADUAy}30ra{|tY>&WD!*PM5e3E`Ojm7nbyUx0MLhab{z z06~tA=4k*9hbI|69!)8V`Ja0NgRlc{kkM`o#oQiW@Lg%<|zkT_#SlGc6&#z`f_&-!# z>vNjvTW0BY`w}Rn%Ro32c;AFGZ%E#72qb|}4yiyPAg>?5=)MQ2$ zO)^P!b&{zw*0s~0wK_>tqvQNW9oKfo4|ck`IyyR|UsnGCzj-}L*8@pOn&!|w+|P60 z*L@v&{jFaA)uH!oJpffes=r{Ht@_)8sCTdh+Pbasehl1Qv&d?e1?fbiS$K8sBOZBt z0is$br%Q8CXonI@QSJJ_YEMHa)R8mn!w>zTuQC1b>f;GSgbWkI~*4A4UR+ROPh`MG7|pM6_TV-w|ubwT%|Q zptZvLi^Ks7U`FuC9gvGuLi0_thR_DU03v*#tNIsSa{$Qok160o6wn7JFq|dm0LTPD zbU?R02mw(1S85=#1|SgN|91lVi&n7kGP9p8be{mXUEP?6MkC{f7mi$jHcP8#X7>R-H_h(BW8~*H92pwGVdV{d>IB?KE^6S4!e%@DT z{h>5~Ao;5woyh<2CkPJ_IKlnZ5g5eGrxx}W=a#o>AQi-73%=`tz}SP-pfDKh9R1f$GnCukhdPkuc*U^K z$#0$a$r#WSSOXQ?hOOFeyXEx(gOriex2O5@mVy!6t@@zfc|Pd0`Yh#xu@=u`^bR=n z3Yp&Na{%G-k{s(lvjUrqgVFi0<@QT{0iAyB=+UGRTY=yp|vt*>er&sv54 zB@hF|e3l>tM7wKv`GzzNLHvs%LqJCJd8}LlD;N_1+J_+eE%YjJ&6AbT0&U5R}^#zN>OqRMdykRAX}*Gk~;y}FwrCEdxF3GDP+Qn~>Kr_k(l%Sel0lHf*2Lr_GQRGiwtw~krg``S zKNy_{2k5KO%^ES@o0zX*kT>}^K^|OKfV;OTRrP~d@@y_OMf|z)Y3u_h=YRUa?)FBkUqbPdQ|SI;dXf?fWex@Ra~C*&MknyIf(8dL{dyInEKfe=vh(4aG_gMyVI8Nm`6=K*~> z=_xV}m)&mnxu(aIV^Rh~0J-}mFhEke0}`#*sTV;EAf^xipm#={3b|CN01gKiB(aPh z!)JNn2Mj5{ep;;!%vwPf+CUBn)&Lj|Gds--XwPp$Iy}G1Ne{yMoxhbB*5!Dz^gP+ zyoDFYheUu|qy+#902<)c@PBOvaJ>Q~I)D!>z#spEzx~B8_rHGGd>jhB&l2Z;STJPi zS+=ZaLy%cTyX2e?DY~JilW{rOt|!k1kO&-w1}NLlzI>j;$`{5z7SK7W1}zL&5>c7W zr)MLL99TouIs;mqLxEH;N3niTfA6RSol)lCyZ7~f|8BR+|NWPieoI0JSUr=WZ3oL2 zBSeUY=Kdb(10H+1Hh1HSt=7R@77&q_z?9$}O@{WD-&whPz z_uijgu`35svBGUcvWLuI3DNo=zMTHjJ3qwb8G) zVO7ErOGraF(SX2n+pf*31?~*)X#QMmU;On{6KXAq6fD3LL5PLQpGkZq1bnSQlVJTy_g|vd@tmOy1{+L0A#= zpfa7uzHnH_Wbn`AD+fI`SfMI788Cn`rN^mLD5!A>KnxMgRJ~9G`m7o-nY(Vihc#NL zVXI3dSHZ1Q%$Y?Vt3j^ZuzFpv0F!J;1fswrB^n8`{YPH_pFs7P&^0deB4SA>AE&5k zDf~g?-~&RkZ@ylQxV6PvgZ&N^!D@x|ru`OZ1=s>ovfD6;h0Q5)L#@~x!U*Jp71#$9 z07Jkv2yhhzOay^=d*@0E2nc|bKkj~%|MzBqcxV0hfdP{8|9${K9^jAv)qdw?lRL~l z=D{@SBw-2igOD@d2{%s~4Vca!a%_UNG`6{h#ev}<$71jc$=F0Rao&I43DF5BHo&Zt zKFmEr6M4g(2;=C88Y77TAe}mDKxTUHj@6z+J z@YKZXCLVuKoXeY@xlHWAlfi^C)6i{1NQK=NCITfBAM8cbZL!eCSi%m2F)P#*dI`6b zHCP`W#QQwYundJ|0Ec9(VU1Jjb0}b&+cTOwoY+ZN@J6RiqQpYmLi}4fH!{j`T`L&W z3S|iBeHI78{(M}X^1)-_bj*AcET5L0A`>HwX8O(wML;>tOm!cVo)hD zDX6_Sm3sWK<}TUQNr2XAZ{N`>!Q%&cz)EO;FbF|#xCM}*(hH-3kA_hELg0fR#(&}e zxB*{r0RJZtgxvhsQGoc)H}K{|Ao#Tm7|(bR3=j>#brt|#er^BDzuGS3x3iG_J%$f> zhTr^>KV%YI=QUqEgzg7R9$)lC_B=XpeuI!8q+KCA4CevcjPm{c7UKxHji`Sc4De_h z46HtJhM7Q)J;cpX$YWA-tU1Ciz}QH?jTYXXR@jU)^Y+_t)>*IE>VrZq$psi1FfLQC z*Gm%%E1ypx--qOUFD{Ob!YpKz%me5OAe);6W$?kh$=x4*c5l@bdoVval3GIIR~$%x z8qTvzfCx)JT-r>2OB_yH4K6QepCMER9JI@oeDY+cVaXLTPUr=%%)D{y>){9pRxCI_#9PS6=Oh`U z(249!gabQ`Y*_4O>AFK=2hAYLCh%-D-n?PN4WCPHe77+yluBHIs>>YkksL^l7u&Uht+wiHb5rO5-5BH0tRAaUx3Ua zjgR5TX6(f5S{M!SZf<0vi1N-AG4!|g0TM0+J)jOVNu-1Ff?OzJA4!pYV1WPv87`0F zN38*<7V_4*)nB}r*tx_AWr1*;Iue}l?P(hh|P4OiXt3hj_72nYiBAry$zKb}5FfpPVJ z&;Sq<5WKMh84r3-C!*cKilM^@n8zwQ z5Q6XLr;Tm~rZ5=?jl0GM?>O*I@&}cGlcDPeT!X3O-YoYA$%@0nc+dpE9xOSs99v$Q zTb+wdk;fu=T_nlh^NXWjn2utb4?Y8BZ?gKkp#O|6+#g+;D&aErmk{ugDt-0YLOQch zoZmY}woiQW!4Kz;$-2F7N{>RSI|64=6QTpA?mvM1XcRp>On*x1dtYF;zxl&o`Y8~P zA1wXS@BP?)A`DtwC@mI~zluR);o_(k{MDt;<~D&VOo0%f76*TBY9zfnl~4K0qClWt zCpLc&rXvNxhUGu_z`*>f?`I#bRRUMw9{V!1os!j0&{gC zPf-}cavUJ?LUdSCJ+SF32T7!h=8U1dQ%Yg{h1?Gi093hbkYe000fmn*47tV5sFWNB zq;^SIrWPDRg&Jo!4D^YAPUKc2tVN1N<1>hu^->WF|M$K@>ttoFP$7pXutYQ;SDl;~ z8`~%A#_)P$`&IL--wYei2Supz3~HkV=n*Qkpzy>C1pCXBAo>9h0_H$vWCMB-69R|u z61YDF1_r$xl`k@>iBh@*Q4cQmLII@?Ibbp!&ktGvJM4y-Pnb$$^@l?wiO--3HNeS$ z2F$338Lhp*QH+OEP-Zc>s1dn*3^qr4g&aEog+w+sE7=f%1EAMil@jEoDU?!?Y#dxC z$op=StU(n_7_92?4UtwKkdUf3&)UG?hq_-b8ix-HDF2W`nk|xX*rJU;L=7Xz`?~lk zEDq#ekp6V)X|)PQgOE&lp%erM*x**fEkO)FAXxoJaJ-<2r*udpM$o?y#y>C)HQc_V zL6MSl$lIEmTEktKjw1|6D1ZbQF`_y|2ta-yFypAgSb+oJy7o!$|9$-rU5@~Gx#j|r z72qvQ0K^LPiVBDa@_=ytUvq)JM*xTx_<9HM<@5T%_RGf{S9#vdIu1KUP(5=Tx~xGl z?LKLQyU%k)*o$BmL-U19G=b%DmT_*s+78VG7-J)fMz~Ww2kwhMk#z9`Fp3RI;P0aSjKqZ41#E+)Sp^3-h%S7x~q8i3@3y%nhImrcZc zcX4rI1kJz-1bcX$T&%sQ-CsiW?oNKTGM8GK$9%A8>LcBE0lojoX7Jf(BdJn6wmXRs z5O{(-RUm z`q^KY*!}F@-rU5>Nd0&J!4zNIJ1Xve1)st6DExxv=0E^0dwy%YM zP`CIBjJ`t9zBLT}58=iM6x|jG%wou?dl(ni05qLpB{t6>NcU?cU%9a5EOlgxfjd|b zp)uezT&H(_zSDk!Gs{$K_jmecx>EkF%gc~};h#Kx0so+#Ov*a@3`Tf-$0(qxMdWvd zxeW621`OPODzvSf- z3yU?Z24}X#;MQo|H*TZ+Ar9(_?RU^SGT&{8ELz|U(dd8?$S~`g$XrSn4gdfEc;ifD$l%tNKpIHtT1H5gjn&pr9kB;M6R^j=(kpO0bpKf~Zjb zUigCo2tcft9)uzU{mMZZhHMvxVK)^9jT@jId<9i@gHR)rIdlRT1xs~$6}J3d5e^sp zJ;Yk<>S3mS2Q_;0ezt|LX$) zP5{IVFnZP6z2l#;m_%GL1K!5RzU$&AjJ(W-{5NhTQAM?@I zC(pB)Z1S1&K{X5_e`9;M8fG{?@w$=7M6(@004$;cPr8ls^M(%_n6QyYUU2oR3Z002 z5|(^w3?rJ6b-0Zz9SYe1JVk9D+U8R5<%72#_yhzqjsk~q!>M4(02FKtL)!v~!8}Fo z|J23Y92~Ze=T;|H!QO`}F9i5=bCB{*#@e`qNAJ%+xVO0Z#qJkBdkSJDK?;#6KlJ5%5hcADeK3kYumri4_x06bO5<5P*;0=+c7+t8>fqpIH@O++Vr} z?ctC9@MnJJ9tb@?vKw%fn;^u^_QKV8lv9?3D^Z+7xh zuS%cF!8^bP$2^f_?cssm$<0CV6;fT^f4B2gB2kAfbT0jDFi4DmSGYYG`U!8 z{xKeeElx`#jDs34;iypragi1*`|iF zwA{w*iP4A!;#o7%NSrqW>+4NBl%lB&XGZ{Of&-riV!arMTOjjdNdpLQGY=lm(&lbD z9dtdL%*+=-`~#)WHxc9D>;>5{mZO*vxFCQMKRkru$Z7}kv5X!Gkc8r$6|MmtVJ9kDInE zO|#o;FSkRlyU(*B51({pztU%;Q1WzmXTr%IzCLMS8^H0t0Ma13hu{1l+hU>t3oKyQ zcW29?{6hKA`N^l2NJGzj0<#$I(*zquZA&b!5c=e`4aS3P3gn7#gm!w{O`*!I!mZ6Sqx53{XF%h3Wg!ruBUCi-Qp!`8LG*4ddZ+>wPi?|rF zf0jmLsr(WYIk+{!Hs87I`4R1h@<-1fg9E4u^*=d>Z?T-7gR{pJ`ilh!1m@C<50(U* zi%Sp%F8v6cKwv%kGl(VorQci(uB0|67k+m#Hg|DcLp~^yL6-XyQ*%%hPHm1l4X)CZ zDK%f>0wkUui#V|3^Z9z6NSx69zn|4tKmu5^;f02i7Y+v7Y^53qcycgPg-doRPcp9h z2mxv2*aFEr$Z+`35lU{9PfE%%v5R6x! zK~&Fa1g;=0g5hr}G+bw`kD^Sy+R63&Irsyb!SF*&lh5Z9OIgydsXj?`U>WXUq>48;+qZ`9eA+!z4JG)9Fx=_)gyBTj z=;DeA%4pn1tW!7KIBVb`c7K^|SX$bq5#%eWowXag5ler-2C=P=mligQD07VbK>p{` z9(?S9XLH_>+zpPB6<-X6Y)WM^=|T|~K77E_dAbDn0ET~TC}sjHwNM}qG=oq99cc!P zhbt9eCeJe+IEx`}7V%utJT=T)GoH!2}VG$s)1H zHzw3!t2g6~^5bogVqDT1y;eRAiPMsw?~Mk~KPr1jcws>~k^%aI$53*TTh-*gF- z9}!dFk5$$Wsi4;{WCHl00KR&&gTyvMtuoIH^YS4#!2IK*4+qoct}r>Q^96WvMp%V z9nl0_DGn;he7u-FG)COiYg;biK8&JsV4d5xKwae2bKe3b_Z1#8A-I7!I0V2lCtv^D z4HoKP#>e{<;q23cioR0+uKqT*0?(eUBNI~hNA@NrSLdb{_l}9{-ozwigY(en{wgfI zkOhp;{}NdS`cvt64r69;`AHux|AeF)uhodz{|E*Lp#CE!q=e2v!-oK%7`gy_2y?~i z$kM_+TKt=o1_V>58dMavK z_vqR2V4xL9i>#F82TZv%Pz=k;e5VD55f#aqHAK0ITFtYwM#tjG z_kj+spJkijvok|i95#e;95l~B4{YUn>9W=5G`bs+Fv5z9L?TgI+6``&&<3P%?Qc$& z5EnSFc((Km{Xj4uAEXQEoQcnq(l5pHWx#|3ODW}v=Ml`(Omr}W_6u$`(kEDoq={zu zOea_-_+P$|E96O1pj2o_d#B_+%PaM=HhL3{9_I?-ToAuS_9Rg;G-JhLCou z9~;(=Nl>vf1SrG;!YC4YrUi0(cE;;?vi!h)<`oB|v02Ufdu0+@gq%sMO#Rc0dfS5iuyTWS-SEjl@3%YD%C5URF-(jpFJ z8Y!S8BuFKg)_go9mA=bXnA}c%Fe6A7EJhdwU%uNee0U+MvJ`_G*0FRr4 zA?HLK5uP#d|Bj34G_b&$p9FQmXA@?B%ceBBx#yOTYcTP;SjLB7;Y;R=(Fs5RQ>&x% z_q3v4z4saMBbZzQ^KfNl9*iG=fJDA?5E z78liq3H_IH8%3_!*=MKl{#V=GZu^a!YV9Xt3-6a1RX%LyEYX%TlWQgna1CI?Aw{{W z3GX)&Ct7$2g%jCG11SK=csx5h%MRaawswLAHAPwr_zfhA>G=i7fl8_5ZgMkO+FaVy zm*z15Z0?q;<=utyZm|%@lF!ulJ2O|Uf0isBRpIs z3J&r-41p2;DB&MM1pGCHj43(=;=w;KC5@@Cb zp=dH9^~W#KIAB5h@ODI^wpP@Jj0^Ulvm%%`NdWSD9lmMEb=B0kUM`%ms~EA>=LH>@ z2z)4=9Rfjk_qya791~+g!$T|&y0YKcx|9+>rfg;Ug@OTPY}g z$E^~pz&mE}YPCZET@)m^2SBV?d>aP<%GjWm!)*k_0s;7d%3+;Q02bho6@g(_oP&@5 zw*mN&2t=NS$m1>^Bn99K0JvfRUvq%IcLTYxe#HWU8Q>qG{K*!Oi~wIB{K5HtlW%g{ zkGB&)cs$5t6WMOF`fX9xne@0{vyU6yZ689tpIgF+Z_oDhPE3@}P~q|6{(iH;LLe0F z(xroZjz`>I&M56Bs)6X*LB*v(cs*ceel46}s|UCo9oRKxDvm&qsUJ9gaPqpz<+7bb z*b<8Rg7{+__`?iSIas)O*FHYJ>@DNoUz{`Tt?n%^1C%oX2E2cN5~T}#ZEO2Vo z?fC*`Bya(~xAXv%0T7QSasN*~`0UT-SNHOBbIY~mRZ|ULvb(s4q)|Ua2&3o$Hf^HM zVK7=O0^R7Ga~aRt8tNZ)4%{EHfOzx%lpF*!!V(<>1_)FD1qK~;8YTgKUO}-PcF^m# z_FJt&f!tluPOF$E?SIM-kI*NVPhPaoHKIy+XU9^Ak%4Ap2ad!1;HbyLBx`e2tJ>ID zD;4q9TSh{ez)jldX{4u9UVK%zMEPx4%g#j4jgis$2LgOy4(jJO zmmX|x<`)WakAKj~m$4Cv5BU6`fJ|^tsSMyh2**G)4s?ANt^Q`(l3$Wd&h7MD1x_ks zQY9>w^L&n|fJj&%LWEtE72ukl0U0e5mD&LElLaUxo`$q%Lk}5`Xxbr@I1mB|nZJyP zc*hZ?tR#M;U^{_ zN)iMBw2$)2;reeKQw;~kLDEs8G0;(}X&{3eGvih#r2J}yRtxGcx&*04s`k>b6$ps1 zEW8RCv)a{4nei&O8)z&Z=>EkYD@95x&KI@nBl)lZ?xhd_VC6e3)ae8|2!enU` zg&K)w&JsQwtc7#+(*Qz3i1nl|xRJ=!VE|OAFf0#~%%CSt*KLt+y4mg4fX^|`DCFkw z(XvS`uQL!CRF_7U!O#JP7j;ZZcW?RP`105fg774)I+tM3m!8CSkemxJ=QxVBM`9^c zZ~;d|yBGIQ_>t{v!u)@+2M*xG${cC@k?*mG@6(>!!-4^?qnHmC7FMQqWncW+gD>Xi z@7>1$fd4@s{3ceU^<^xQ0L$TK>$j7SHF*KNY3T&xA>N?AC9`6 znl4=S`VavPOaq97`?+`jxzih-PR1|)ZJpXdhja?YDAF!Me;j6wwY*SyW2Qf?{=TR z4vV1)z#PzY=XovqiM}S>D#ARB<2XaYIEd5rd=e>No7K@ghJZkFeyQY9Zqn5UB{&4c zgUrYvUn182gLn?BJ&=FsV0ky_$jZ9uC;u6_FV}!Q*C~!)(A;j?K)NRbsmuBp%8M^wc6$=>^0x+zRj>gZi{Y zg?&GK1LsV4j)xg7gNG$9%_#6<`V#*2aI1PK+GV1U;Oz^mmS zc@G-SuMVROTtXU1t%P-GsKX@=5epV= zC4HgOx-HZX+;|LDm@PTmta_@a0U9}A8Terp4&siAtdT45i4I@QhY~D=G{6WM?XA~r z2u_2YTKj1Excv@AY?_-x7mm^;C5#gQ&OvDTYqd$>^Y8ZNE^5b@?YU$0=<(DjK*Mx; zVxqruyth1oiQ)JHEx^Ryg$bYlj*7iGf4jYRe0YO9hPqcUHX$ z_V2ei;cOn1I}JOahm}+G0jRKm zDF5ob)$48bcl^igR)V>B(lL%7%w4?O(mG=k@nUV~xE%@&E{@uOBujoqFb3CNNRBZw zDP&&V>B8ReR;au;5(k81|_Icb_Hz2yiSxaatfCN z$Rz%*W7AUf@vKC1=SC#-&?VJlzUB*r{-rT;p|f1m*vN5QLlbf48ansM_F1$0di|^m z?hJpG+YW45Q0OZxf(iVDJ-Z9F{g6Vs7muf4Ef1Jb5zS!}2s;6q zL4Fus^SBgsT1Ai&*x8U-<3vV{RqB#CToPRM)1>4H;b(0_X~qG77n}k(62SAvVqI?# zM&uaQC9@c^H8~%OSdY~{rN?lfQ$h{tRj4J?S}kgprF|4S0Jw>$g*tpOK2=1b0p(sD z0Db%z1b~awunjk0DFEX@p+>CLMhtiE7&SLV0!nO@Fj4{NL@HQ)NgVPGf%2wdNOM;c zfg!saS9AoHKw6jKrc`Ob41jM63!t$zwQ3lu!6Af%+sz-3!4?#Xzz=@F!2h8Iybc7% z2JrhxK=c5_3lR7pVF7(e1ie9C@%Ru3K`!mAoKVaU-H{r^YD2n@i?*1 zH;bKS=d+Vm)bY)UB^%;Hhuy?CIW}tiL9_wqZy&eajJ5`DbpIB2)Z$PaCYlg|9X{vj zXrotlW)9XIEzd*vRnZ55gPCHLe*5MxVbULV-gsd1XwpUS(}(!j-vsX#HlccsD2dpUM!vh?gxk- zX`8+F@$zzvYz^M+Ap~&b7e?mdm+#uk%l*#Q+}`o!Q4I`(DH!~YKDaN}{_6cnn1MkR zw6XwbVR3@Y7myx|PW(n{Y9VG?oLre)_~YfY3H`sxG&;4CM)5B!LSZ~PvS>P<>uZai zD!Kiu)e7`YD0mow!%n|Kv=1&llz?O=s1uz)pdNVi$V2BG9(`tjyQ^2&dc`|izyJ$8 zpKCqo_iO%ithckptBbX5wQTn7{KfHBQH=b7<3hTy)5`5^)dnL)|K8TXu7k(GS_LAb zfTxf_=)@+@JV|OV&&BM-ynL9s4qz@Cb+P!ixPcQwArsr&%O0I`BJljhkRJVQAMg9&f2AVcad zL}uz?_@HS-v@hh-P&O!lMY0(=#Jo`W6JfwKJUp%%0=zpb(dnt#M>?-l1!7=W2CeVR z@UW88SY4C^zndO&s4)%bbyA;1A(8vsVuR2tsQ@Rmid=3aG3eRMthf4~7r7yu4|D>^2|0OTN3*M<2mo3!cK zyeJw9hpKgK!h@KUEtN+_e*9qVk&Sie6OXs|`NDP%AYe8K`d_*F))kOC;B*^IRszrF z=PueuN2K$|^^X%^Wp8zAc@JFPdw({9PJj@478fQ___*3@7!gqHB(OJijM{&9v^4^p zZv?3w7keh;0HfJYqoKeS;P`0oV)>E`7WnAOD8`5q^}Amzf)}^|^Wgi7QxoVh5GArO zh0_5JgQndFE0de>1oiuu=N9}USR2L{kQp{VI*$VYF@gSej4E*tQQrq14=(sIJ>v)v zUND$}nEUmZ2*3=$@Qa#D zzr3Ep#-b+9FJmt7Z#DUy%i~zEv@qB2?<7<+Ysl_>59hH^r#G+&D9`RiNdI}U2*YCq z7ZeNJkZC!?&h1Pk01(iajRqd-M07bIVOWpNfvW`h0Kzc@V^CVBn9Vrg3}CPW%*UUO zD=EdeEx<(+iEfyTepnbrea);t^Tn-hAf1LaRs)qz6-2FO{C8C#LhNow};NrRMA_ctu9*X4ZahF+&&U^-S zEt28j@H&(dhg2bV$p9*-1v;@rEYpZ3D0P&+5VJoibT9;w<2gEhuCPOyP1%Qq;}lfGlthW2jLv>Ysf|R?pc~f#)Z-iwNA7BfQ;^Q< z)u}(iNwV>=3d{j~^031!7?*<&>?La8YdtXW0{b8Z#DnC4led@yt}?+0_(x8bJKz9b zD}cxf084K;0Q)e$6h{Tbl}m`1-Co!rFDkAB|N20mz7nV8tC4f+=sTg#Wn$8$In@QWM@xa=qP zj%&wc7qEPRZ+5&#>K~z?Bk=Y3JYV`42wF@lbls;hC|uV zwOZ^2u)pJr_|j6$bUerzEz*sLXfhnGTq0NS$6^Md!ZwG)f${q%d~P-)He==ObZaby z*+&7XYi68VJNQ{MEz9y&YQZZtsoz0-JnmW|vDH(M<}Auao8V+VM>f|2T?2GyOL z>u0ct7A#qql1CbQqGKQMc~3$@~87`u-`lHBr>207s`1O6f(ejK`nOWQC1~07${Jd^**01 zKq+Yj49#pR*!jcBi;7BAR-6zLgHK}y85+K6I*m5s zmXAXQNd8UeMbm+qHEiwFQoE9oSf%nA_4JI6gaH6Y@M7FoeI&z1L4sV687rJd%~%pl z!zS3SrX>c6RpPmc`i1L<0$3roiPOXA>3t9;;m1%)jrPv%2(D<eiQ zkINqiz||V?+6e^Zf6W8@^I!Y%-p_x@{dQ)1yV+!51(FPPK?Y6+4_gOoHj50ZlE=C5 z$u}~vfjNe4Ff7ghR|hR+3%;exCjGvPr_31WSZOiAcY38Af$bs`uj>Xm4k10NB)>Vf0ylx z3*7H3SlQzM03jIbe1L+n#l_8CKX!y<(qG1ju)2z2Azd)N`{N^^1{cew3jlo=rU`ro z=l~a6@x8shIWjR!Ok4mSST;?ptW3l(%8OTjcLCObPzHe`JoyWzNwOG#p*S2rFh-0n zkAOokf+X=cCWH7CIVmt9j4mN0U?f(H)$j;e)k+&ob7^#%(;^vHv7yJoBH(KMzlMkp z)IWv*5Ag?2B}9Rl3ao*j74xwgNhs@`XN~>dpom+&=&zZMcKTaKe|XeY&s1Z}dpo(w zh4{Nm)$VWn;ftsHorMRx3&)p85@IkOz|ad`V2MtM9pDs733blFANyIqDcv&rY_#6x z#L;Y&z!Q8HDTDYc)yr(o<*@~-j*8J~ciZ%3Xs@u@0~sL1NW_K^d^l+e;29_am{n{Q zmSebWgh@8p>xw zlcJYO^p${8>Tp33uz~B}I))7Z@W1J4=?p?!XAsdoJ}m@Y#~BuDvD@3wz_Lf>bAapeme!)2~-Q)fZ7BMmXAkMZ;E*XFc6RH9JbGDU zH^C$`o(6z_%j@k>!|BT<(9L5w4Ds7=0QX?vb=WW{0+=Z@xUZ6dT-3;&+A2N=ou985 z-SoFW!5x;$nykUZa&XPZ%c*0w0$2>mnHhd?etupIzQqTDkp-$YrXT^jixkb{|Gap2 z`R)ivJ`n~jem;j$V1$k(F_=X)P>tFU9{bZ2Htc%3iU7`O*zBN%bvYi@ z*Jd|pPbGl62=#;m0MP&dT6-etQV6-ypI+Kmr#pbivT{lQLe5RVE}miSh@ z)(;uoHrkK;QilOGp zIu!}GSZedR(D+6}p6uBnY(KJzGw6QLw1%j~7jDER7ZlFie;)l{`TgL}C-5PTLkZ22mf$IW7%TKeS1Ek2*cl~|7h`h8{{Z=Ql{PgL5sCD_K)p~JmS0g6`heMVx_~4!}=yOG!ngf^% zB2kbZ7P_P0`EoKzfOgR!J4+lydM=TO!2EaMPf+|DvpAw5@|kr371)57$Dp<-3`T>l zEJe|()nS=XqoJ^lC*&w3oD4Yt$9vT_m&z*Ds_y1g0;zV`s?p<|-_YqqUY!-AKiT*p z$RDsGBE`hO599{B#5*>N05FMFV^APP*s4>TWh$%nBT9%Ycex$<9+gUl%il_AG=Kq( zB(f)r>PM|AsG-|-rTXSgwM3>J_G$#6Gh#Fm$RQmP2_;I^O~jI2gMbA5`+oi>4FCZE z2@#lZfQT6|U_j7-@Je`q*CK$cK)`iE0{SolT;~D(eLDO2@#8GQ+?&B{?pZd$XB?Z) zV6xifW#PkeCs|;>IkZF%zwE4Ix0}UEiGKV%%lBAcma$>YZNZ6v{eP&N%|EkMu>@dn zhuCOer^H#@+}l8hbq3a0MKD%&y~U&Qc8$y6{yI1~&G4lTsPFKOe2Xa=E&~}Ga%vGE zP_7)j`{U(z7Z-bbm$=GT7FSnCaEIdnz%?Gj9v(jbAnPNv8|^(-eJgW6_Z!P5v;&v# z(BxkLV?dn0T;L=?{i8!zT{fBa_C^reiH~3IE%*KZu@@V;S_MvwtWI&Gs|yQ1hns%x z=k|84kijWvkZUn0{@Q*VG`RjjL#mC8qVvEE0AK(eLV96z!bBj3SdFYB;>FCt+k-kh zj!yg8J!zk}F#ezS2fcpk z^0@Xylc@!9Jbnx1b166xTTa_yG5WTi?&ayp#kskD0WN~PLNNRO;lp&Bi-Nb+YVYjy zGVv60Eqt|$j73V+aV&fR@dF`700U_avj7`{AW#u8BKim0c{htdUpMT*qu?)#DFqt@ znNh3O=D<#5r4mN$lENlfQV#(<#HbcNgVA5Yf=yr}fie$k_Rmhx0IZ+2!b6R-ljd-^ zZ{!$NB--mun9B282Dj>D+vyH!gm<^MxFiI=yZP!=E)&lO7m^DwjDf-r7D03{uryI{ zDhB>|J2wao3arGF>J3`0T*%LxYaKh^>qd)5Zz9aazqK@i-OZA}-yC%M*o7t%g&Z)% zcmY;Opb`%_ufU5P9fHnfV{0;(2;^voY!)IQ8*o59`u&+1(F|ZPG66RFf}}Wyu>3Amf(m9iNFX%YtY`bixD2^keHQfIds4u$r;Kp ztOTe4A>a)x0Y3#^XN=si+`enq*l&u(G8z7n%+L%Y9$YCm>(m$mAWBy2yh4@WIvD8M z5BypXc$EqE!3toA5CL%alOy1I3cwg}4gcXm+&~Er_m@IbB zJ3lxq2I$QsZ^SPnTo6UeZyK?F*?%tf<> zgM(_7rb8?XuX+|vQH;e1Z}1Mz12BlTGGnLTy#?FvJ#*IaR1cm#s|OJT)NBm~sU-9P zAPFXrBN%pv6WjI5x4-c2^5PgbdVB7={xJotPMJvU`jP&NX?y|ppD6S82*`q~ix+!4 zaa8QP3v>cV@N&+!aud_-)qO_Oh5*47EDgzErx&mbg%=CA=j%xbKF5<4-B*FY_(e<{spePUcXptw=env zvQAKVt20YKcX>PjX<7a7;py9l!C0^kTEmDRs6e{l6QGA33+@mzu+X5^!(2T#@Rt(tdZDH zh%LkBX0YwgFZZ-gb#wbfkaOPs=Bt#qn}eFo<4A$0v6+YCg#K@EBIZCZl}hz;xfF07KKu{_9yLtVDN0N{ zxiGs+W%iGBq(89A%}7^Mh3!T?SsEx6cB@nY_Ykenp#7LK%0T`DtA_%IP>G%YG;jhh zd;)QrI6yA+K2)kO2Oy^zc|uA+_-3IIcf8x60F_7sYY>TE_Yv5D0f`|0Kou02m$f(kJd%;Q^Za?)mdhHPQ4m zkj!xCI$@&QgF++lJi$DF9_l2MQ2nrn7H7l5Jn!(F3l<^z9MeanWaIEK$K{atPakCH zdX%YPhvdj*KtEeASID*J0F{Iv^WpNZdn!zm!IIoleO66&ik&g5gB)qyB*~ z?$t&nC&(6IB)$qE!sjt42Z23In6QHw#i=uicQL=mYFjbW1b!sPf!kBjQy~kT^i(kD z&p5~i0AMuDvIz6>fFy8$wP4+Y^N!L83o1?%xIj4f{Z($Wa68B^G zuXZcka>fOfuG2aD5SHGjr)y3N>kIf?HcTPkev60_Yp#O>L5%e*o6qzll#}yRC8DxV zWJ^|Ma<8R{uEMIcG#QA52{4$?zmV^n?4Me)HPmG^JB9J|4x{ZxnXRieI zM&hhs#DBn@wm$}Q4QEsMso>V9+i>|U!ZvZTumPvi%^1>Pfex34{V>m0e7<5+wZgr zz4KN!f$d|lQ|nuV0t=S;3OUCr+)iJac8O#z8I1qk(BoPt(ban@#DlpY^O4C#V#4*; zx$I^!-h)_J2p-WmhZuoV}X$Cc9?na6X|6P+gAzAFhAg z{MTUsSMWbxuOS_HtSVi z){;o>g6x}xsZ244_WmS0h_XpfK9N04gu;oCr_;j6oy?)b6)N))_X(6k$Z7TDats7< z(WuYkirNaD%or?$PwQ`87U)s9Uf?aqRc#JWy%qcp{c)5p75DcCXyCr?HF*~EX@a5I zPRUl!xB8$3V4A;NKE6P&y)uUa|9oY2t_D;Pb!mzL{##vHMmj%OzHO-e3Fc4eLhW`7 zo%OrD<(dg|0}3Cvzdto%!aCs++W|rk0H8m&w=%M4ni}zqu1*jn01`ut7N+zUUtlvp zAchgZfY>W8%>UdR(1KBt0}3wT+!QQHaQ{PqM2JOj7{dcv#S9~PRE1?Ow?@c}1mKr> zFaaTn*T)198RP-~=X9)6O`rG5JGB-^2LGMoV`PH9y4*Q$?Z3jr3;F-MqsuLW?tR6- z)sHjN^YM{*dbu4{z1=M+-=Dv@_6Sutiwy6}eBn8<;)!{-V?1h%9A*B9wj8uS z3gVp}*N*~1jJ3C)K7Fx$7_zjVJbjZz+DNLnW0c58C(7~u&Qnm<-!z*$`%SL5)oK~V z-~@GYJO1dfz>Po}qeVf%?Lcqy1@taTVRZXMS}kx+jaUIfKJ>y88KQnglvpn{>t#BN zfl??GL6=vB38E+$f*5GmO7vpF6~dvRQm`tOSaVoV534O2tCEs~J?xdE3($Jy(uW&k zYP1p>l?%&%xkQOC0tLVUo6v`2aM0E+RI6`OHfV&v@TYVZg>qV@ zqO?k20Jq)7Pom*Zj0CU4O7L!k=n=fbX7v3?4p#y5Gf&IyA1gr%z&BQ501*72KmZa4 zbZrGnq<|lmfY-Ue*KVNXWFh5$Wd?Lr|JPn1-@5_-`Ct5fZ$J0)7k|6e`kH?nDpwPS z*>B3bOw>nb4?FvacsmSuV7C?uCUVbn-#C#CP%joaj|C+gc&OiJ!j=N;7Nqw|WI?A!R$LFdR*vay)-pu_zj$VBzaGeYich;2^`ng^0}r z0FUKzx#srZ?ZE*ifXltTi@gz4K01KUR{{RxA_p-L@LaxD`+Tnrk}k^rqK!iV008Or zk9&oF&2;g{aZFdgwJUfdFKB z>JG*Op-+a+JFxA*N0As(RX!~B`f>l|<&K31+Yd=U!26L3g2zs)-G=rL>YqeZl#C5Z z^9zbu(;h&9E(Rf(e}y>jOr=-MmCO8KfE~)hC`dF4LzpF} zwZcMgh1|{)`74; z4)9B^k+bnR%ya8qC-F1#@KWyVESxw4$2TmtFwsc!adx|V*5Hj<;satAoITfmoo#dz z7&Tf*_p2_9^vz6wzUQ3 zq}bi!@;uu=N(cl}aMCp_?j;8(&d4#5`6w87BLo!Xk7yK~*%0A1@aUIIDS!kPv&~BR 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

  • -rH_Ux@$r$~e>fdePR$Z7o)tHW-@&XHt;76Oryl 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 literal 0 HcmV?d00001 diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/script.dat b/fle/cluster/factorio-server/scenarios/default_lab_scenario/script.dat new file mode 100644 index 0000000000000000000000000000000000000000..6ed02ae17325594e0b2647a9296391e92b33cc71 GIT binary patch literal 1441 zcmb_c!EVz)5Vh0YHcCiSL6CYtqDWkQpf@hmH$G#vu_x}T>s@PiorLlO`~v&|7fxLG z9R7emfalmr8=6E#!cro8X6MbDH!~YYabzMqF@9Xrob>Ncy!q_xe8@(bBN-?cDp2Y9 zF&ijnO;TwXkDAyhx=0yYSetjBzpmJErF^jZ&TuwUfN^C~%0k{MvkrZovS-uU8FERf zj50-%JEiXhj(}q*ncL29VPx(p5_0<9OO3GDTvcX)sRyg0)3iLgie%-CX=$F}>=?5N%gwNZRRbm}jNEFK+SPzKFINdc zSp!E)9|T77Aq0S5>^k`uU{k*`!C3*4$h0Co-nLw#j$vV?MjD4JkH>d3M~yvM-LTeC F{0E) Date: Mon, 28 Jul 2025 19:36:14 +0530 Subject: [PATCH 15/53] added hot reloading control.lua --- fle/cluster/factorio-server/run-envs.py | 94 +++++++-- .../default_lab_scenario/control.lua | 185 +++++++++--------- 2 files changed, 172 insertions(+), 107 deletions(-) diff --git a/fle/cluster/factorio-server/run-envs.py b/fle/cluster/factorio-server/run-envs.py index 375ee82d3..65e622548 100644 --- a/fle/cluster/factorio-server/run-envs.py +++ b/fle/cluster/factorio-server/run-envs.py @@ -11,12 +11,22 @@ ROOT_DIR = Path(__file__).resolve().parent.parent.parent.parent +import logging + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + class Scenario(Enum): OPEN_WORLD = "open_world" DEFAULT_LAB_SCENARIO = "default_lab_scenario" +class Mode(Enum): + SAVE_BASED = "save-based" + SCENARIO = "scenario" + + class PlatformConfig: def __init__(self): self.arch = platform.machine() @@ -30,6 +40,8 @@ def __init__(self): self.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value self.scenario_dir = Path(__file__).parent.resolve() / "scenarios" self.server_config_dir = Path(__file__).parent.resolve() / "config" + self.mode = Mode.SAVE_BASED.value + self.temp_playing_dir = "/opt/factorio/temp/currently-playing" def _detect_mods_path(self) -> str: if any(x in self.os_name for x in ("MINGW", "MSYS", "CYGWIN")): @@ -98,8 +110,8 @@ def _get_volumes(self, instance_index: int, for_server: bool = True) -> list: "bind": "/factorio/saves", "mode": "rw", }, - str((self.config.scenario_dir / self.config.scenario_name).resolve()): { - "bind": f"/factorio/scenarios/{self.config.scenario_name}", + str(self.config.scenario_dir.resolve()): { + "bind": f"/factorio/scenarios/", "mode": "rw", }, str(self.config.screenshots_dir.resolve()): { @@ -117,12 +129,16 @@ def _get_volumes(self, instance_index: int, for_server: bool = True) -> list: def _get_environment(self) -> list: env = { - "LOAD_LATEST_SAVE": "true", + "LOAD_LATEST_SAVE": ( + "true" if self.config.mode == Mode.SAVE_BASED.value else "false" + ), "PORT": str(self.config.udp_port), "RCON_PORT": str(self.config.rcon_port), "SERVER_SCENARIO": self.config.scenario_name, "DLC_SPACE_AGE": "false", } + if self.config.mode == Mode.SCENARIO.value: + env["PRESET"] = "default" # Docker API wants ["KEY=VALUE", ...] return [f"{k}={v}" for k, v in env.items()] @@ -137,7 +153,9 @@ async def get_scenario2map_ctr(self, instance_index: int): config = { "Image": self.config.image_name, "Env": self._get_environment(), - "HostConfig": {"Binds": self._get_volumes(instance_index, for_server=False)}, + "HostConfig": { + "Binds": self._get_volumes(instance_index, for_server=False) + }, "Entrypoint": ["/scenario2map.sh"], "Cmd": [self.config.scenario_name], "Platform": self.docker_platform, @@ -153,11 +171,15 @@ async def get_server_ctr(self, instance_index: int): "HostConfig": { "PortBindings": self._get_ports(instance_index), "Binds": self._get_volumes(instance_index), - "RestartPolicy": {"Name": "unless-stopped"}, + # "RestartPolicy": {"Name": "unless-stopped"}, "Memory": 1024 * 1024 * 1024, }, "Platform": self.docker_platform, } + if self.config.mode == Mode.SCENARIO.value: + config["Entrypoint"] = ["/scenario.sh"] + config["Cmd"] = [self.config.scenario_name] + print(config) return await self.docker.containers.create_or_replace( config=config, name=f"factorio_{instance_index}" ) @@ -192,12 +214,20 @@ async def start(self): return # Launch all instances concurrently - containers = await self.get_containers_to_run() - scenario2map_ctrs = containers[0] - server_ctrs = containers[1] + if self.config.mode == Mode.SAVE_BASED.value: + scenario2map_ctrs = await asyncio.gather( + *[ + self.get_scenario2map_ctr(i) + for i in range(self.num) + if not self.check_save_exists(i) + ] + ) + await asyncio.gather(*[item.wait() for item in scenario2map_ctrs]) + await asyncio.gather(*[item.delete() for item in scenario2map_ctrs]) - await asyncio.gather(*[item.wait() for item in scenario2map_ctrs]) - await asyncio.gather(*[item.delete() for item in scenario2map_ctrs]) + server_ctrs = await asyncio.gather( + *[self.get_server_ctr(i) for i in range(self.num)] + ) await asyncio.gather(*[item.start() for item in server_ctrs]) async def stop(self): @@ -213,15 +243,49 @@ async def restart(self): tasks = [ctr.restart() for ctr in ctrs] await asyncio.gather(*tasks) + async def hot_reload_scenario(self): + # Sync scenario files into the server's temp directory for hot-reload + containers = await self.docker.containers.list(filters={"name": ["/factorio_"]}) + + async def sync_container(ctr): + cmd = ( + f"docker exec -u root {ctr.id} sh -c " + f"'cp -a /factorio/scenarios/{self.config.scenario_name}/. {self.config.temp_playing_dir} && " + f"chown -R factorio:factorio {self.config.temp_playing_dir}'" + ) + + proc = await asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + stdout, stderr = await proc.communicate() + + if stdout: + print(f"stdout: {stdout.decode()}") + if stderr: + print(f"stderr: {stderr.decode()}") + print(f"Container {ctr.id} sync complete (exit code: {proc.returncode})") + + # Run all container syncs concurrently + await asyncio.gather(*[sync_container(ctr) for ctr in containers]) + print("Hot-reload sync complete.") + def parse_args(): p = argparse.ArgumentParser(description="Manage a local Factorio cluster") p.add_argument( "command", - choices=["start", "stop", "restart", "clean"], + choices=["start", "stop", "restart", "hot-reload-scenario"], nargs="?", default="start", ) + p.add_argument( + "--mode", + choices=["scenario", "save-based"], + default="save-based", + help="Mode to run the server in", + ) p.add_argument("-n", type=int, default=1, help="Number of instances (1-33)") p.add_argument( "-s", @@ -260,6 +324,10 @@ async def main(): config.scenario_name = Scenario.OPEN_WORLD.value elif args.s == Scenario.DEFAULT_LAB_SCENARIO.value: config.scenario_name = Scenario.DEFAULT_LAB_SCENARIO.value + if args.mode == Mode.SCENARIO.value: + config.mode = Mode.SCENARIO.value + elif args.mode == Mode.SAVE_BASED.value: + config.mode = Mode.SAVE_BASED.value mgr = FactorioClusterManager(docker_platform, config, args.n, args.dry_run) if args.command == "start": @@ -268,6 +336,10 @@ async def main(): await mgr.stop() elif args.command == "restart": await mgr.restart() + elif args.command == "hot-reload-scenario": + if config.mode == Mode.SAVE_BASED.value: + raise ValueError("Hot-reload is not supported in save-based mode") + await mgr.hot_reload_scenario() await mgr.docker.close() diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua b/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua index 8e30a5fed..f19f52154 100644 --- a/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua +++ b/fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua @@ -5,21 +5,20 @@ require "story" global.paths = global.paths or {} local think = function(thought) - game.players[1].print({"","[img=entity/character][color=orange]",{"engineer-title"},": [/color]",{"think-"..thought}}) + game.players[1].print({ "", "[img=entity/character][color=orange]", { "engineer-title" }, ": [/color]", { "think-" .. thought } }) end function check_for_player_death(event) if event.name == defines.events.on_player_died then - game.set_game_state({game_finished=true, player_won=false, can_continue=false}) + game.set_game_state({ game_finished = true, player_won = false, can_continue = false }) end end local function on_configuration_changed() - game.reload_script() -end + game.reload_script() +end local on_player_created = function(event) - local player = game.players[1] player.force.enable_research() @@ -42,46 +41,46 @@ local on_player_created = function(event) entity.destructible = false end global.intro_entities = entities - + global.car1 = game.get_entity_by_tag("car1") local entities2 = { - global.car1 + global.car1 } for index, entity in pairs(entities2) do entity.minable = false end global.intro_entities2 = entities2 - + local recipe_list = player.force.recipes -- game.players[1].clear_recipe_notifications() recipe_list["chemical-plant"].enabled = false - local technology_list = game.forces.player.technologies + local technology_list = game.forces.player.technologies technology_list["automation"].researched = true technology_list["stone-wall"].researched = true - + technology_list["optics"].enabled = true technology_list["gun-turret"].researched = true technology_list["logistics"].enabled = true technology_list["military"].enabled = true technology_list["logistic-science-pack"].researched = true technology_list["steel-processing"].enabled = true - technology_list["electronics"].researched = true - technology_list["fast-inserter"].enabled = true + technology_list["electronics"].researched = true + technology_list["fast-inserter"].enabled = true technology_list["automation-2"].enabled = true - technology_list["electric-energy-distribution-1"].enabled = true - technology_list["engine"].enabled = true - technology_list["fluid-handling"].enabled = true + technology_list["electric-energy-distribution-1"].enabled = true + technology_list["engine"].enabled = true + technology_list["fluid-handling"].enabled = true technology_list["oil-processing"].researched = true technology_list["logistics"].enabled = true technology_list["heavy-armor"].enabled = true - technology_list["steel-axe"].enabled = true + technology_list["steel-axe"].enabled = true technology_list["military-2"].enabled = true technology_list["chemical-science-pack"].enabled = true - technology_list["military-science-pack"].enabled = true + technology_list["military-science-pack"].enabled = true technology_list["logistics-2"].enabled = true technology_list["advanced-material-processing"].enabled = true technology_list["solar-energy"].enabled = true @@ -156,7 +155,7 @@ local on_player_created = function(event) technology_list["rocket-fuel"].enabled = false - + --blocked tech upgrades technology_list["braking-force-1"].enabled = true technology_list["energy-weapons-damage-1"].enabled = false @@ -197,40 +196,36 @@ local on_player_created = function(event) technology_list["artillery-shell-speed-1"].enabled = false technology_list["follower-robot-count-1"].enabled = false technology_list["follower-robot-count-2"].enabled = false - - - end script.on_event(defines.events.on_script_path_request_finished, function(event) - local request_data = global.path_requests[event.id] - if not request_data then - log("No request data found for ID: " .. event.id) - return - end - - local player = game.get_player(request_data) - if not player then - log("Player not found for request ID: " .. event.id) - return - end - - if event.path then - -- Path found successfully - player.print("Path found with " .. #event.path .. " waypoints") - global.paths[event.id] = event.path - log("Path found for request ID: " .. event.id) - elseif event.try_again_later then - player.print("Pathfinder is busy, try again later") - global.paths[event.id] = "busy" - log("Pathfinder busy for request ID: " .. event.id) - else - player.print("Path not found" .. serpent.block(event)) - global.paths[event.id] = "not_found" - log("Path not found for request ID: " .. event.id) - - end + local request_data = global.path_requests[event.id] + if not request_data then + log("No request data found for ID: " .. event.id) + return + end + + local player = game.get_player(request_data) + if not player then + log("Player not found for request ID: " .. event.id) + return + end + + if event.path then + -- Path found successfully + player.print("Path found with " .. #event.path .. " waypoints") + global.paths[event.id] = event.path + log("Path found for request ID: " .. event.id) + elseif event.try_again_later then + player.print("Pathfinder is busy, try again later") + global.paths[event.id] = "busy" + log("Pathfinder busy for request ID: " .. event.id) + else + player.print("Path not found" .. serpent.block(event)) + global.paths[event.id] = "not_found" + log("Path not found for request ID: " .. event.id) + end end) @@ -239,142 +234,143 @@ local story_table = { { init = function(event, story) - game.players[1].teleport({0, 0}) + game.players[1].teleport({ 0, 0 }) game.print("Welcome players") - set_goal({"Enjoy the scenery for 5 seconds"}) + set_goal({ "Enjoy the scenery for 5 seconds" }) end, condition = story_elapsed_check(5) }, { action = function() local character = game.players[1] - character.insert{name = "iron-gear-wheel", count = 47} - character.insert{name = "pistol", count = 1} - character.insert{name = "firearm-magazine", count = 1} - character.insert{name = "electronic-circuit", count = 90} - character.insert{name = "copper-cable", count = 38} - character.insert{name = "iron-plate", count = 10} - character.insert{name = "steam-engine", count = 1} + character.insert { name = "iron-gear-wheel", count = 47 } + character.insert { name = "pistol", count = 1 } + character.insert { name = "firearm-magazine", count = 1 } + character.insert { name = "electronic-circuit", count = 90 } + character.insert { name = "copper-cable", count = 38 } + character.insert { name = "iron-plate", count = 10 } + character.insert { name = "steam-engine", count = 1 } set_info() end }, { init = function(event, story) game.print("Collect 10 iron ore") - set_goal({"goal-iron-ore"}) + set_goal({ "goal-iron-ore" }) end, condition = function(event, story) return game.players[1].get_item_count("iron-ore") >= 10 end, action = function(event, story) game.print("Well done") - set_goal({"completed-iron-ore"}) + set_goal({ "completed-iron-ore" }) end }, { init = function(event, story) game.print("Build a stone furnace") - set_goal({"goal-build-furnace"}) + set_goal({ "goal-build-furnace" }) end, condition = function(event, story) local has_furnace = game.players[1].get_item_count("stone-furnace") >= 1 - local furnace_on_ground = game.players[1].surface.count_entities_filtered{position = game.players[1].position, radius=100, name = "stone-furnace"} >= 1 + local furnace_on_ground = game.players[1].surface.count_entities_filtered { position = game.players[1].position, radius = 100, name = "stone-furnace" } >= + 1 return has_furnace or furnace_on_ground end, action = function(event, story) game.print("Great! Now place the furnace on the ground.") - set_goal({"goal-place-furnace"}) + set_goal({ "goal-place-furnace" }) end }, { init = function(event, story) game.print("Smelt the iron ore in the furnace and collect 10 iron plates") - set_goal({"goal-smelt-and-collect-10-iron-plates"}) + set_goal({ "goal-smelt-and-collect-10-iron-plates" }) end, condition = function(event, story) - return game.players[1].get_item_count("iron-plate") >= 10 + return game.players[1].get_item_count("iron-plate") >= 10 end, action = function(event, story) - game.print("Well done") - set_goal({"completed-iron-plate"}) + game.print("Well done") + set_goal({ "completed-iron-plate" }) end }, { init = function(event, story) game.print("Collect 10 copper ore") - set_goal({"goal-copper-ore"}) + set_goal({ "goal-copper-ore" }) end, condition = function(event, story) return game.players[1].get_item_count("copper-ore") >= 10 end, action = function(event, story) game.print("Well done") - set_goal({"completed-copper-ore"}) + set_goal({ "completed-copper-ore" }) end }, { init = function(event, story) game.print("Smelt the copper ore in the furnace and collect 10 copper plates") - set_goal({"goal-smelt-and-collect-10-copper-plates"}) + set_goal({ "goal-smelt-and-collect-10-copper-plates" }) end, condition = function(event, story) - return game.players[1].get_item_count("copper-plate") >= 10 + return game.players[1].get_item_count("copper-plate") >= 10 end, - action = function(event, story) - game.print("Well done") - set_goal({"completed-smelt-and-collect-10-copper-plates"}) - end + action = function(event, story) + game.print("Well done") + set_goal({ "completed-smelt-and-collect-10-copper-plates" }) + end }, { init = function(event, story) game.print("Craft a burner mining drill") - set_goal({"goal-craft-burner-mining-drill"}) + set_goal({ "goal-craft-burner-mining-drill" }) end, condition = function(event, story) - return game.players[1].get_item_count("burner-mining-drill") >= 1 + return game.players[1].get_item_count("burner-mining-drill") >= 1 end, action = function(event, story) - game.print("Great! Now place the burner mining drill on the iron ore.") - set_goal({"completed-craft-burner-mining-drill"}) + game.print("Great! Now place the burner mining drill on the iron ore.") + set_goal({ "completed-craft-burner-mining-drill" }) end }, { init = function(event, story) game.print("Place the burner mining drill on the iron ore") - set_goal({"goal-place-burner-mining-drill"}) + set_goal({ "goal-place-burner-mining-drill" }) end, condition = function(event, story) return game.players[1].get_item_count("burner-mining-drill") == 0 end, action = function(event, story) - game.print("Well done") - set_goal({"completed-place-burner-mining-drill"}) + game.print("Well done") + set_goal({ "completed-place-burner-mining-drill" }) end }, { init = function(event, story) game.print("Build an assembling machine") - set_goal({"goal-build-assembling-machine"}) + set_goal({ "goal-build-assembling-machine" }) end, condition = function(event, story) return game.players[1].get_item_count("assembling-machine-1") >= 1 end, action = function(event, story) game.print("Great! Now place the assembling machine on the ground.") - set_goal({"completed-place-assembling-machine"}) + set_goal({ "completed-place-assembling-machine" }) end }, { init = function(event, story) game.print("Automate the production of iron gears") - set_goal({"goal-automate-iron-gears"}) + set_goal({ "goal-automate-iron-gears" }) end, condition = function(event, story) return game.players[1].get_item_count("iron-gear-wheel") >= 100 end, action = function(event, story) game.print("Well done") - set_goal({"completed-iron-gears"}) + set_goal({ "completed-iron-gears" }) end }, { @@ -417,18 +413,18 @@ local story_events = defines.events.on_entity_died, defines.events.on_entity_damaged, defines.events.on_player_died -} +} local function on_configuration_changed() - --game.reload_script() - init() -end + --game.reload_script() + init() +end script.on_event(story_events, function(event) - if #game.players > 0 and game.players[1].character then - check_for_player_death(event) - story_update(global.story, event) - end + if #game.players > 0 and game.players[1].character then + check_for_player_death(event) + story_update(global.story, event) + end end) script.on_event(defines.events.on_player_created, function(event) @@ -441,6 +437,3 @@ script.on_event(defines.events.on_unit_group_finished_gathering, on_unit_group_f --script.on_event(defines.events.on_gui_click, intro_screen.on_gui_click) script.on_init(on_configuration_changed) script.on_configuration_changed(on_configuration_changed) - - - From c8ce3097922efc5b47e3d5374d5522f5a82803e7 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 13:13:24 +0530 Subject: [PATCH 16/53] FactorioInstance refactor --- fle/env/a2a_instance.py | 2 +- fle/env/gym_env/registry.py | 2 +- fle/env/instance.py | 1051 +++++++---------- fle/env/lua_manager.py | 57 - fle/env/protocols/mcp/state.py | 2 +- .../admin/get_factory_centroid/client.py | 4 +- .../tools/admin/production_stats/client.py | 4 +- .../tools/agent/connect_entities/client.py | 18 +- .../tools/agent/harvest_resource/client.py | 12 +- fle/env/tools/agent/move_to/client.py | 8 +- .../tools/agent/nearest_buildable/client.py | 4 +- fle/env/tools/agent/place_entity/client.py | 2 +- .../agent/place_entity_next_to/client.py | 2 +- fle/env/tools/agent/score/client.py | 2 +- fle/env/tools/controller.py | 14 +- fle/env/tools/init.py | 9 +- fle/env/tools/tool.py | 13 +- .../system_prompt_generator.py | 15 +- fle/eval/algorithms/beam/run.py | 2 +- fle/eval/algorithms/beam/run_milestones.py | 2 +- fle/eval/algorithms/beam/run_parallel.py | 4 +- .../independent/trajectory_runner.py | 2 +- fle/eval/algorithms/mcts/__main__chunked.py | 2 +- .../algorithms/mcts/__main__objective_mcts.py | 2 +- .../algorithms/mcts/__main__planning_agent.py | 2 +- fle/eval/algorithms/mcts/main.py | 2 +- fle/eval/tasks/task_abc.py | 2 +- 27 files changed, 515 insertions(+), 726 deletions(-) diff --git a/fle/env/a2a_instance.py b/fle/env/a2a_instance.py index f64aa9bda..a07ecebc4 100644 --- a/fle/env/a2a_instance.py +++ b/fle/env/a2a_instance.py @@ -55,7 +55,7 @@ async def __aenter__(self): async def __aexit__(self, exc_type, exc_val, exc_tb): """Async context manager exit""" - self.cleanup() + self.server.cleanup() @classmethod def _ensure_server_running(cls): diff --git a/fle/env/gym_env/registry.py b/fle/env/gym_env/registry.py index af021cefb..5a4d142c8 100644 --- a/fle/env/gym_env/registry.py +++ b/fle/env/gym_env/registry.py @@ -146,7 +146,7 @@ def make_factorio_env(env_spec: GymEnvironmentSpec) -> FactorioGymEnv: container_id=0, # Use first container num_agents=env_spec.num_agents, ) - instance.speed(10) + instance.set_speed(10) # Setup the task task.setup(instance) diff --git a/fle/env/instance.py b/fle/env/instance.py index 6e7e8feb8..d306708d9 100644 --- a/fle/env/instance.py +++ b/fle/env/instance.py @@ -11,6 +11,7 @@ import time import traceback import types +from contextlib import contextmanager from concurrent.futures import TimeoutError from pathlib import Path from timeit import default_timer as timer @@ -32,6 +33,7 @@ from fle.env.utils.controller_loader.system_prompt_generator import ( SystemPromptGenerator, ) +from fle.env.utils.rcon import _load_lib CHUNK_SIZE = 32 MAX_SAMPLES = 5000 @@ -72,10 +74,9 @@ def from_factorio_direction(cls, direction): class FactorioTransaction: - def __init__(self): - self.commands: List[ - Tuple[str, List[Any], bool] - ] = [] # (command, parameters, is_raw) + def __init__(self, server): + self.server = server + self.commands: List[Tuple[str, List[Any], bool]] = [] # (command, parameters, is_raw) def add_command(self, command: str, *parameters, raw=False): self.commands.append((command, list(parameters), raw)) @@ -86,233 +87,252 @@ def clear(self): def get_commands(self): return self.commands + def execute(self) -> Dict[str, Any]: + """Execute all commands in this transaction""" + start = timer() + rcon_commands = {} + + for idx, (command, parameters, is_raw) in enumerate(self.commands): + if is_raw: + rcon_commands[f"{idx}_{command}"] = command + else: + script = self.server._get_command(command, parameters=parameters, measured=False) + rcon_commands[f"{idx}_{command}"] = script -class FactorioInstance: - namespace_class = FactorioNamespace - _cleanup_registered = False # Only register cleanup once per process + lua_responses = self.server.rcon_client.send_commands(rcon_commands) - def __init__( - self, - address=None, - fast=False, - tcp_port=27000, - inventory=None, - cache_scripts=True, - all_technologies_researched=True, - peaceful=True, - num_agents=1, - **kwargs, - ): - self.id = str(uuid.uuid4())[:8] - self.num_agents = num_agents - self.persistent_vars = {} - self.tcp_port = tcp_port - self.rcon_client, self.address = self.connect_to_server(address, tcp_port) - self.all_technologies_researched = all_technologies_researched - self.fast = fast - self._speed = 1 - self._ticks_elapsed = 0 - self._is_initialised = False + results = {} + for command, response in lua_responses.items(): + results[command] = _lua2python(command, response, start=start) - self.peaceful = peaceful - self.namespaces = [self.namespace_class(self, i) for i in range(num_agents)] + self.clear() + return results + + @contextmanager + def __enter__(self): + """Context manager entry - clear any existing commands""" + self.clear() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Context manager exit - execute the transaction""" + if exc_type is None: # Only execute if no exception occurred + self.execute() +class FactorioServer: + def __init__(self, address, tcp_port, cache_scripts=True): + self.rcon_client, self.address = self.connect_to_server(address, tcp_port) + self.cache_scripts = cache_scripts + # Initialize Lua script manager and script dictionary self.lua_script_manager = LuaScriptManager(self.rcon_client, cache_scripts) self.script_dict = { **self.lua_script_manager.lib_scripts, **self.lua_script_manager.tool_scripts, } + + def run_rcon_print(self, command: str): + return self.rcon_client.send_command(f"/sc rcon.print({command})") + + def send_command(self, command: str): + return self.rcon_client.send_command(command) + + def load_init_into_game(self, init_scripts): + for script_name in init_scripts: + self.lua_script_manager.load_init_into_game(script_name) - # Initialize hooks as dictionaries to organize callbacks by tool name - self.pre_tool_hooks = {} - self.post_tool_hooks = {} - - # Load the python controllers that correspond to the Lua scripts - self.setup_tools(self.lua_script_manager) + def update_game_checksum(self, script_name: str, checksum: str): + self.send_command( + f"/sc global.set_lua_script_checksum('{script_name}', '{checksum}')" + ) - if inventory is None: - inventory = {} - self.initial_inventory = inventory - self.initialise(fast) - self.initial_score = 0 + def _clear_game_checksums(self): + self.send_command("/sc global.clear_lua_script_checksums()") + + def _get_game_checksums(self): + response = self.run_rcon_print("global.get_lua_script_checksums()") + return json.loads(response) + + def init_action_checksums(self): + checksum_init_script = _load_lib("checksum") + response = self.send_command("/sc " + checksum_init_script) + return response + + def connect_to_server(self, address, tcp_port): try: - self.first_namespace.score() - except Exception: - # Invalidate cache if there is an error - self.lua_script_manager = LuaScriptManager(self.rcon_client, False) - self.script_dict = { - **self.lua_script_manager.lib_scripts, - **self.lua_script_manager.tool_scripts, - } - self.setup_tools(self.lua_script_manager) - self.initialise(fast) + rcon_client = RCONClient(address, tcp_port, "factorio") + address = address + except ConnectionError as e: + print(e) + rcon_client = RCONClient("localhost", tcp_port, "factorio") + address = "localhost" - self.initial_score, goal = self.first_namespace.score() - # Register the cleanup method to be called on exit (only once per process) - if not FactorioInstance._cleanup_registered: - atexit.register(self.cleanup) - FactorioInstance._cleanup_registered = True + print(f"Connected to {address} client at tcp/{tcp_port}.") + return rcon_client, address - @property - def namespace(self): - if len(self.namespaces) == 1: - return self.namespaces[0] + def _get_command(self, command, parameters=[], measured=True): + prefix = "/sc " if not measured else "/command " + if command in self.script_dict: + script = prefix + self.script_dict[command] + for index in range(len(parameters)): + script = script.replace( + f"arg{index + 1}", lua.encode(parameters[index]) + ) else: - raise ValueError("Can only use .namespace for single-agent instances") + script = command + return script - @property - def first_namespace( - self, - ) -> Optional[FactorioNamespace]: # Add this property if used - return self.namespaces[0] if self.namespaces else None + @contextmanager + def transaction(self): + """Context manager for backward compatibility""" + transaction = FactorioTransaction(self) + with transaction as t: + yield t - @property - def is_multiagent(self): - return self.num_agents > 1 + def register_post_tool_hook(self, tool_name, callback=None): + """ + Register a hook to be called after a specific tool is executed. + Can be used as a regular function or as a decorator. - def reset(self, game_state: Optional[GameState] = None): - # Reset the namespace (clear variables, functions etc) - assert not game_state or len(game_state.inventories) == self.num_agents, ( - "Game state must have the same number of inventories as num_agents" - ) + Args: + tool_name (str): Name of the tool to hook into + callback (callable, optional): Function to call after the tool is executed. + Will receive the tool instance and the result as arguments. - for namespace in self.namespaces: - namespace.reset() + Returns: + If used as a regular function (with callback provided), returns the callback. + If used as a decorator (without callback), returns a decorator function. + """ + # When used as a decorator without parentheses: @register_post_tool_hook + if tool_name is not None and callback is None and callable(tool_name): + callback = tool_name + tool_name = callback.__name__ + if not hasattr(self, "post_tool_hooks"): + self.post_tool_hooks = {} + if tool_name not in self.post_tool_hooks: + self.post_tool_hooks[tool_name] = [] + self.post_tool_hooks[tool_name].append(callback) + return callback - if not game_state: - # Reset the game instance - inventories = [self.initial_inventory] * self.num_agents - self._reset(inventories) - # Reset the technologies - if not self.all_technologies_researched: - self.first_namespace._load_research_state( - ResearchState( - technologies={}, - research_progress=0, - current_research=None, - research_queue=[], - progress={}, - ) - ) - else: - # Reset the game instance with the correct player's inventory and messages if multiagent - self._reset(game_state.inventories) + # When used as a decorator with arguments: @register_post_tool_hook("tool_name") + if callback is None: - # Load entities into the game - self.first_namespace._load_entity_state( - game_state.entities, decompress=True - ) + def decorator(func): + if not hasattr(self, "post_tool_hooks"): + self.post_tool_hooks = {} + if tool_name not in self.post_tool_hooks: + self.post_tool_hooks[tool_name] = [] + self.post_tool_hooks[tool_name].append(func) + return func - # Load research state into the game - self.first_namespace._load_research_state(game_state.research) + return decorator - # Load messages for each agent - if game_state.agent_messages: - for i in range(self.num_agents): - if i < len(game_state.agent_messages): - self.namespaces[i].load_messages(game_state.agent_messages[i]) + # When used as a regular function: register_post_tool_hook("tool_name", callback_func) + if not callable(callback): + raise TypeError("Callback must be callable") - # Reset elapsed ticks - self._reset_elapsed_ticks() + if not hasattr(self, "post_tool_hooks"): + self.post_tool_hooks = {} + if tool_name not in self.post_tool_hooks: + self.post_tool_hooks[tool_name] = [] - # Load variables / functions from game state - for i in range(self.num_agents): - self.namespaces[i].load(game_state.namespaces[i]) + self.post_tool_hooks[tool_name].append(callback) + return callback - try: - self.initial_score, _ = self.first_namespace.score() - except Exception: - self.initial_score = 0 + def register_pre_tool_hook(self, tool_name, callback=None): + """ + Register a hook to be called before a specific tool is executed. + Can be used as a regular function or as a decorator. - # Clear renderings - self.begin_transaction() - self.add_command("/sc rendering.clear()", raw=True) - self.execute_transaction() + Args: + tool_name (str): Name of the tool to hook into + callback (callable, optional): Function to call before the tool is executed. + Will receive the tool instance and the arguments as parameters. - def set_inventory(self, inventory: Dict[str, Any], agent_idx: int = 0): - self.begin_transaction() - self.add_command("clear_inventory", agent_idx + 1) - self.execute_transaction() - # print("RCON output:", result) - self.begin_transaction() - # kwargs dict to json - inventory_items = {k: v for k, v in inventory.items()} - inventory_items_json = json.dumps(inventory_items) - player_idx = agent_idx + 1 - self.add_command( - f"/sc global.actions.initialise_inventory({player_idx}, '{inventory_items_json}')", - raw=True, - ) + Returns: + If used as a regular function (with callback provided), returns the callback. + If used as a decorator (without callback), returns a decorator function. + """ + # When used as a decorator without parentheses: @register_pre_tool_hook + if tool_name is not None and callback is None and callable(tool_name): + callback = tool_name + tool_name = callback.__name__ + if not hasattr(self, "pre_tool_hooks"): + self.pre_tool_hooks = {} + if tool_name not in self.pre_tool_hooks: + self.pre_tool_hooks[tool_name] = [] + self.pre_tool_hooks[tool_name].append(callback) + return callback - self.execute_transaction() + # When used as a decorator with arguments: @register_pre_tool_hook("tool_name") + if callback is None: - def speed(self, speed): - self.rcon_client.send_command(f"/sc game.speed = {speed}") - self._speed = speed + def decorator(func): + if not hasattr(self, "pre_tool_hooks"): + self.pre_tool_hooks = {} + if tool_name not in self.pre_tool_hooks: + self.pre_tool_hooks[tool_name] = [] + self.pre_tool_hooks[tool_name].append(func) + return func - def get_speed(self): - return self._speed + return decorator - def get_elapsed_ticks(self): - response = self.rcon_client.send_command( - "/sc rcon.print(global.elapsed_ticks or 0)" - ) - if not response: - return 0 - return int(response) + # When used as a regular function: register_pre_tool_hook("tool_name", callback_func) + if not callable(callback): + raise TypeError("Callback must be callable") - def get_system_prompt(self, agent_idx: int = 0) -> str: - """ - Get the system prompt for the Factorio environment. - This includes all the available actions, objects, and entities that the agent can interact with. - We get the system prompt by loading the schema, definitions, and entity definitions from their source files. - These are converted to their signatures - leaving out the implementations. - :return: + if not hasattr(self, "pre_tool_hooks"): + self.pre_tool_hooks = {} + if tool_name not in self.pre_tool_hooks: + self.pre_tool_hooks[tool_name] = [] + + self.pre_tool_hooks[tool_name].append(callback) + return callback + + def execute_post_tool_hooks(self, tool_name, tool_instance, result): """ - execution_path = Path(os.path.dirname(os.path.realpath(__file__))) - generator = SystemPromptGenerator(str(execution_path)) - multiagent_str = "" - if self.num_agents > 1: - player_idx = agent_idx + 1 - multiagent_str = ( - f"## MULTIAGENT INSTRUCTIONS\n" - f"You are Agent {player_idx} out of {self.num_agents} agent(s) in the game. " - f"Follow your specific instructions given to you by the task." - f"Use the send_message() tool regularly to communicate with other agents about your current activities and any challenges you encounter. " - f"Start each program with a send_message() call to explain what you are doing. " - f"End each program with a send_message() call to confirm your actions. If your program errors out prior to send_message() being called, the message will not be sent. " - ) - return generator.generate(multiagent_str) + Execute all hooks registered for a tool after it has been executed. - def connect_to_server(self, address, tcp_port): - try: - rcon_client = RCONClient(address, tcp_port, "factorio") #'quai2eeha3Lae7v') - address = address - except ConnectionError as e: - print(e) - rcon_client = RCONClient("localhost", tcp_port, "factorio") - address = "localhost" + Args: + tool_name (str): Name of the tool + tool_instance: The tool instance that was executed + result: The result of the tool execution + """ + if tool_name in self.post_tool_hooks: + for callback in self.post_tool_hooks[tool_name]: + try: + callback(tool_instance, result) + except Exception as e: + print(f"Error in post-tool hook for {tool_name}: {e}") - try: - rcon_client.connect() - player_count = rcon_client.send_command("/sc rcon.print(#game.players)") - if int(player_count) == 0: - print( - "WARNING: LuaPlayer hasn't been initialised into the game. Entity placement behavior _may_ be incorrect for boilers and pumps." - ) + def execute_pre_tool_hooks(self, tool_name, tool_instance, *args, **kwargs): + """ + Execute all hooks registered for a tool before it is executed. - except Exception as e: - raise ConnectionError( - f"Could not connect to {address} at tcp/{tcp_port}: \n{e.args[0]}" - ) + Args: + tool_name (str): Name of the tool + tool_instance: The tool instance to be executed + *args, **kwargs: The arguments passed to the tool + """ + if tool_name in self.pre_tool_hooks: + for callback in self.pre_tool_hooks[tool_name]: + try: + callback(tool_instance, *args, **kwargs) + except Exception as e: + print(f"Error in pre-tool hook for {tool_name}: {e}") - print(f"Connected to {address} client at tcp/{tcp_port}.") - return rcon_client, address - def setup_tools(self, lua_script_manager): + def register_controllers(self, namespaces, invalidate_cache=False): """ Load Python controllers from valid tool directories (those containing both client.py and server.lua) """ + + if invalidate_cache: + self.lua_script_manager = LuaScriptManager(self.rcon_client, False) + self.script_dict = { + **self.lua_script_manager.lib_scripts, + **self.lua_script_manager.tool_scripts, + } + tool_dir = _get_dir("tools") self.controllers = {} @@ -365,7 +385,6 @@ def wrapper(*args, **kwargs): module_spec = importlib.util.spec_from_file_location( tool_name, client_file, - # str(Path(client_file)) ) module = importlib.util.module_from_spec(module_spec) module_spec.loader.exec_module(module) @@ -379,11 +398,11 @@ def wrapper(*args, **kwargs): class_name = "Reward" try: - for i in range(self.num_agents): + for i in range(len(namespaces)): # Get and instantiate the controller class callable_class = getattr(module, class_name) callable_instance = callable_class( - lua_script_manager, self.namespaces[i] + self, namespaces[i] ) # Create a wrapper that will execute hooks @@ -397,13 +416,13 @@ def wrapper(*args, **kwargs): if directory_name == "admin": # If this is an admin method, we hide it in the namespace by adding a shebang setattr( - self.namespaces[i], + namespaces[i], f"_{tool_name.lower()}", wrapped_instance, ) else: setattr( - self.namespaces[i], tool_name.lower(), wrapped_instance + namespaces[i], tool_name.lower(), wrapped_instance ) except Exception as e: @@ -411,305 +430,277 @@ def wrapper(*args, **kwargs): f"Could not instantiate {class_name} from {client_file}. {e}" ) - def eval_with_error(self, expr, agent_idx=0, timeout=60): - """Evaluate an expression with a timeout, and return the result without error handling""" - - def handler(signum, frame): - raise TimeoutError() + def cleanup(self): + # Close the RCON connection + if hasattr(self, "rcon_client") and self.rcon_client: + self.rcon_client.close() - signal.signal(signal.SIGALRM, handler) - signal.alarm(timeout) + self.post_tool_hooks = {} + self.pre_tool_hooks = {} - try: - return self.namespaces[agent_idx].eval_with_timeout(expr) - finally: - signal.alarm(0) + # Join all non-daemon threads + for thread in threading.enumerate(): + if ( + thread != threading.current_thread() + and thread.is_alive() + and not thread.daemon + ): + try: + thread.join(timeout=5) # Wait up to 5 seconds for each thread + except Exception as e: + print(f"Error joining thread {thread.name}: {e}") - def eval(self, expr, agent_idx=0, timeout=60): - "Evaluate several lines of input, returning the result of the last line with a timeout" - try: - return self.eval_with_error(expr, agent_idx, timeout) - except TimeoutError: - return -1, "", "Error: Evaluation timed out" - except Exception as e: - message = e.args[0].replace("\\n", "") - return -1, "", f"{message}".strip() - def _get_command(self, command, parameters=[], measured=True): - prefix = "/sc " if not measured else "/command " - if command in self.script_dict: - script = prefix + self.script_dict[command] - for index in range(len(parameters)): - script = script.replace( - f"arg{index + 1}", lua.encode(parameters[index]) - ) - else: - script = command - return script +class FactorioInstance: + namespace_class = FactorioNamespace + _cleanup_registered = False # Only register cleanup once per process + namespaces = List[FactorioNamespace] - def calculate_optimal_zoom(self, bounds: BoundingBox, resolution="1920x1080"): - """ - Calculate the optimal zoom level to fit the factory in the screenshot. + def __init__( + self, + address=None, + fast=False, + tcp_port=27000, + inventory=None, + cache_scripts=True, + all_technologies_researched=True, + peaceful=True, + num_agents=1, + **kwargs, + ): + self.id = str(uuid.uuid4())[:8] + self.num_agents = num_agents + self.persistent_vars = {} + self.tcp_port = tcp_port + self.server = FactorioServer(address, tcp_port, cache_scripts) + self.all_technologies_researched = all_technologies_researched + self.fast = fast + self._speed = 1 + self._ticks_elapsed = 0 + self._is_initialised = False - Args: - bounds (BoundingBox): Factory bounds containing width and height - resolution (str): Screenshot resolution in format "WIDTHxHEIGHT" + self.peaceful = peaceful + self.namespaces = [self.namespace_class(self, i) for i in range(num_agents)] - Returns: - float: Optimal zoom level - """ - if not bounds: - return 1 - # Parse resolution - width, height = map(int, resolution.split("x")) - aspect_ratio = width / height + # Register controllers with the server + self.server.register_controllers(self.namespaces) - # Get factory dimensions - factory_width = bounds.width() - factory_height = bounds.height() + if inventory is None: + inventory = {} + self.initial_inventory = inventory + self.initialise(fast) + self.initial_score = 0 + try: + self.first_namespace.score() + except Exception: + # Invalidate cache if there is an error + self.server.register_controllers(self.namespaces, invalidate_cache=True) + self.initialise(fast) - # Base tiles visible at zoom level 1 - # These values are approximate for Factorio's zoom levels - BASE_VISIBLE_HEIGHT = 25 # tiles visible vertically at zoom 1 - BASE_VISIBLE_WIDTH = BASE_VISIBLE_HEIGHT * aspect_ratio + self.initial_score, goal = self.first_namespace.score() + # Register the cleanup method to be called on exit (only once per process) + if not FactorioInstance._cleanup_registered: + atexit.register(self.cleanup) + FactorioInstance._cleanup_registered = True - # Calculate required zoom based on both dimensions - zoom_by_width = BASE_VISIBLE_WIDTH / factory_width - zoom_by_height = BASE_VISIBLE_HEIGHT / factory_height + @property + def namespace(self): + if len(self.namespaces) == 1: + return self.namespaces[0] + else: + raise ValueError("Can only use .namespace for single-agent instances") - # Use the smaller zoom to ensure entire factory is visible - optimal_zoom = min(zoom_by_width, zoom_by_height) + @property + def first_namespace( + self, + ) -> Optional[FactorioNamespace]: # Add this property if used + return self.namespaces[0] if self.namespaces else None - # Add padding (20% margin) - optimal_zoom *= 0.8 + @property + def is_multiagent(self): + return self.num_agents > 1 - # Clamp zoom to reasonable values - # Factorio's min and max zoom levels - MIN_ZOOM = 0.1 - MAX_ZOOM = 4.0 + def set_speed(self, speed): + self.rcon_client.send_command(f"/sc game.speed = {speed}") + self._speed = speed - optimal_zoom = max(MIN_ZOOM, min(MAX_ZOOM, optimal_zoom)) + def get_speed(self): + return self._speed - return round(optimal_zoom, 2) + def get_elapsed_ticks(self): + response = self.server.run_rcon_print("global.elapsed_ticks or 0") + if not response: + return 0 + return int(response) - def screenshot( - self, - script_output_path, - resolution="1920x1080", - save_path=None, - zoom=None, - center_on_factory=False, - ): + def get_system_prompt(self, agent_idx: int = 0) -> str: """ - Take a screenshot in game and optionally save it to a specific location. - - This does nothing in headless mode. - - Args: - resolution (str, optional): Screenshot resolution (e.g., "1920x1080") - save_path (str, optional): Path where to save the screenshot copy - zoom (float, optional): Zoom level for the screenshot (e.g., 0.5 for zoomed out, 2.0 for zoomed in) - - Returns: - str: Path to the saved screenshot, or None if failed + Get the system prompt for the Factorio environment. + This includes all the available actions, objects, and entities that the agent can interact with. + We get the system prompt by loading the schema, definitions, and entity definitions from their source files. + These are converted to their signatures - leaving out the implementations. + :return: """ - # Clear rendering - camera: Camera = self.first_namespace._get_factory_centroid() - POS_STRING = "" - if camera: - centroid = camera.position - POS_STRING = ( - ", position={x=" + str(centroid.x) + ", y=" + str(centroid.y) + "}" - ) - - self.rcon_client.send_command("/sc rendering.clear()") + execution_path = Path(os.path.dirname(os.path.realpath(__file__))) + generator = SystemPromptGenerator(str(execution_path)) + return generator.generate(self.num_agents, agent_idx) - # # Calculate optimal zoom if not specified - # if zoom is None: - # zoom = self.calculate_optimal_zoom(bounds, resolution) + def eval_with_error(self, expr, agent_idx=0, timeout=60): + """Evaluate an expression with a timeout, and return the result without error handling""" - command = ( - "/sc game.take_screenshot({player=1, zoom=" - + str(camera.zoom) - + ", show_entity_info=true, hide_clouds=true, hide_fog=true " - + POS_STRING - + "})" - ) - self.rcon_client.send_command(command) - time.sleep(1) - # if not response: - # return None - - # Wait for the screenshot file to appear and get its path - screenshot_path = self._get_latest_screenshot( - script_output_path=script_output_path - ) - if not screenshot_path: - print("Screenshot file not found") - return None - - # If save_path is provided, copy the screenshot there - if save_path: - try: - # Create directory if it doesn't exist - os.makedirs(os.path.dirname(os.path.abspath(save_path)), exist_ok=True) - - # Copy the file - shutil.copy2(screenshot_path, save_path) - return save_path - except Exception as e: - print(f"Failed to copy screenshot: {e}") - return screenshot_path - - return screenshot_path - - def _get_latest_screenshot(self, script_output_path, max_wait=2): - """ - Get the path to the latest screenshot in the script-output directory. - Waits up to max_wait seconds for the file to appear. - """ - start_time = time.time() - while time.time() - start_time < max_wait: - try: - # Get list of screenshot files - screenshots = [ - f - for f in os.listdir(script_output_path) - if f.endswith(".png") and f.startswith("screenshot") - ] - - if screenshots: - # Sort by modification time to get the latest - latest = max( - screenshots, - key=lambda x: os.path.getmtime( - os.path.join(script_output_path, x) - ), - ) - return os.path.join(script_output_path, latest) - except Exception as e: - print(f"Error checking for screenshots: {e}") + def handler(signum, frame): + raise TimeoutError() - time.sleep(0.5) # Wait before checking again + signal.signal(signal.SIGALRM, handler) + signal.alarm(timeout) - return None + try: + return self.namespaces[agent_idx].eval_with_timeout(expr) + finally: + signal.alarm(0) - def _send(self, command, *parameters, trace=False) -> List[str]: - """ - Send a Lua command to the underlying Factorio instance - """ - start = timer() - script = self._get_command(command, parameters=list(parameters), measured=False) - lua_response = self.rcon_client.send_command(script) - # self.add_command(command, *parameters) - # response = self._execute_transaction() - # print(lua_response) - return _lua2python(command, lua_response, start=start) + def eval(self, expr, agent_idx=0, timeout=60): + "Evaluate several lines of input, returning the result of the last line with a timeout" + try: + return self.eval_with_error(expr, agent_idx, timeout) + except TimeoutError: + return -1, "", "Error: Evaluation timed out" + except Exception as e: + message = e.args[0].replace("\\n", "") + return -1, "", f"{message}".strip() def _reset_static_achievement_counters(self): """ This resets the cached production flows that we track for achievements and diversity sampling. """ - self.add_command( + self.server.add_command( "/sc global.crafted_items = {}; global.harvested_items = {}", raw=True ) - self.execute_transaction() + self.server.execute_transaction() def _reset_elapsed_ticks(self): """ This resets the cached production flows that we track for achievements and diversity sampling. """ - self.add_command("/sc global.elapsed_ticks = 0", raw=True) - self.execute_transaction() + self.server.add_command("/sc global.elapsed_ticks = 0", raw=True) + self.server.execute_transaction() def _reset(self, inventories: List[Dict[str, Any]]): - self.begin_transaction() - self.add_command( - "/sc global.alerts = {}; game.reset_game_state(); global.actions.reset_production_stats(); global.actions.regenerate_resources(1)", - raw=True, - ) - # self.add_command('/sc script.on_nth_tick(nil)', raw=True) # Remove all dangling event handlers - for i in range(self.num_agents): - player_index = i + 1 - self.add_command( - f"/sc global.actions.regenerate_resources({player_index})", raw=True - ) - # self.add_command('clear_inventory', player_index) - - self.execute_transaction() - - self.begin_transaction() - self.add_command("/sc global.actions.clear_walking_queue()", raw=True) - for i in range(self.num_agents): - player_index = i + 1 - self.add_command( - f"/sc global.actions.clear_entities({player_index})", raw=True - ) - inventory_items = {k: v for k, v in inventories[i].items()} - inventory_items_json = json.dumps(inventory_items) - self.add_command( - f"/sc global.actions.initialise_inventory({player_index}, '{inventory_items_json}')", + with self.server.transaction() as t: + t.add_command( + "/sc global.alerts = {}; game.reset_game_state(); global.actions.reset_production_stats(); global.actions.regenerate_resources(1)", raw=True, ) + # self.server.add_command('/sc script.on_nth_tick(nil)', raw=True) # Remove all dangling event handlers + for i in range(self.num_agents): + player_index = i + 1 + t.add_command( + f"/sc global.actions.regenerate_resources({player_index})", raw=True + ) + # self.server.add_command('clear_inventory', player_index) - if self.all_technologies_researched: - self.add_command( - "/sc global.agent_characters[1].force.research_all_technologies()", - raw=True, - ) - self.execute_transaction() + with self.server.transaction() as t: + t.add_command("/sc global.actions.clear_walking_queue()", raw=True) + for i in range(self.num_agents): + player_index = i + 1 + t.add_command( + f"/sc global.actions.clear_entities({player_index})", raw=True + ) + inventory_items = {k: v for k, v in inventories[i].items()} + inventory_items_json = json.dumps(inventory_items) + t.add_command( + f"/sc global.actions.initialise_inventory({player_index}, '{inventory_items_json}')", + raw=True, + ) + + if self.all_technologies_researched: + t.add_command( + "/sc global.agent_characters[1].force.research_all_technologies()", + raw=True, + ) # self.clear_entities() self._reset_static_achievement_counters() self._reset_elapsed_ticks() - def _execute_transaction(self) -> Dict[str, Any]: - start = timer() - rcon_commands = {} - for idx, (command, parameters, is_raw) in enumerate( - self.current_transaction.get_commands() - ): - if is_raw: - rcon_commands[f"{idx}_{command}"] = command - else: - script = self._get_command( - command, parameters=parameters, measured=False + def reset(self, game_state: Optional[GameState] = None): + # Reset the namespace (clear variables, functions etc) + assert not game_state or len(game_state.inventories) == self.num_agents, ( + "Game state must have the same number of inventories as num_agents" + ) + + for namespace in self.namespaces: + namespace.reset() + + if not game_state: + # Reset the game instance + inventories = [self.initial_inventory] * self.num_agents + self._reset(inventories) + # Reset the technologies + if not self.all_technologies_researched: + self.first_namespace._load_research_state( + ResearchState( + technologies={}, + research_progress=0, + current_research=None, + research_queue=[], + progress={}, + ) ) - rcon_commands[f"{idx}_{command}"] = script + else: + # Reset the game instance with the correct player's inventory and messages if multiagent + self._reset(game_state.inventories) + + # Load entities into the game + self.first_namespace._load_entity_state( + game_state.entities, decompress=True + ) - lua_responses = self.rcon_client.send_commands(rcon_commands) + # Load research state into the game + self.first_namespace._load_research_state(game_state.research) - results = {} - for command, response in lua_responses.items(): - results[command] = _lua2python(command, response, start=start) + # Load messages for each agent + if game_state.agent_messages: + for i in range(self.num_agents): + if i < len(game_state.agent_messages): + self.namespaces[i].load_messages(game_state.agent_messages[i]) - self.current_transaction.clear() - return results + # Reset elapsed ticks + self._reset_elapsed_ticks() - def begin_transaction(self): - if not hasattr(self, "current_transaction"): - self.current_transaction = FactorioTransaction() - elif self.current_transaction: - self.current_transaction.clear() - else: - self.current_transaction = FactorioTransaction() + # Load variables / functions from game state + for i in range(self.num_agents): + self.namespaces[i].load(game_state.namespaces[i]) - def add_command(self, command: str, *parameters, raw=False): - if not hasattr(self, "current_transaction"): - self.begin_transaction() - self.current_transaction.add_command(command, *parameters, raw=raw) + try: + self.initial_score, _ = self.first_namespace.score() + except Exception: + self.initial_score = 0 + + # Clear renderings + with self.server.transaction() as t: + t.add_command("/sc rendering.clear()", raw=True) + + def set_inventory(self, inventory: Dict[str, Any], agent_idx: int = 0): + with self.server.transaction() as t: + t.add_command("clear_inventory", agent_idx + 1) - def execute_transaction(self) -> Dict[str, Any]: - return self._execute_transaction() + with self.server.transaction() as t: + inventory_items = {k: v for k, v in inventory.items()} + inventory_items_json = json.dumps(inventory_items) + player_idx = agent_idx + 1 + t.add_command( + f"/sc global.actions.initialise_inventory({player_idx}, '{inventory_items_json}')", + raw=True, + ) def initialise(self, fast=True): - self.begin_transaction() - self.add_command("/sc global.alerts = {}", raw=True) - self.add_command("/sc global.elapsed_ticks = 0", raw=True) - self.add_command( - "/sc global.fast = {}".format("true" if fast else "false"), raw=True - ) - self.execute_transaction() + with self.server.transaction() as t: + t.add_command("/sc global.alerts = {}", raw=True) + t.add_command("/sc global.elapsed_ticks = 0", raw=True) + t.add_command( + "/sc global.fast = {}".format("true" if fast else "false"), raw=True + ) # Create characters for all agents self._create_agent_game_characters() @@ -728,8 +719,8 @@ def initialise(self, fast=True): ] if self.peaceful: init_scripts.append("enemies") - for script_name in init_scripts: - self.lua_script_manager.load_init_into_game(script_name) + + self.server.load_init_into_game(init_scripts) inventories = [self.initial_inventory] * self.num_agents self._reset(inventories) @@ -738,17 +729,16 @@ def initialise(self, fast=True): def _create_agent_game_characters(self): """Create Factorio characters for all agents in the game.""" # Create characters in Factorio - self.begin_transaction() - color_logic = "" - if self.num_agents > 1: - color_logic = "if i==1 then char.color={r=0,g=1,b=0,a=1} elseif i==2 then char.color={r=0,g=0,b=1,a=1} end;" - - self.add_command( - f'/sc global.agent_characters = {{}}; for _,c in pairs(game.surfaces[1].find_entities_filtered{{type="character"}}) do if c then c.destroy() end end; for i=1,{self.num_agents} do local char = game.surfaces[1].create_entity{{name="character",position={{x=0,y=(i-1)*2}},force=game.forces.player}}; {color_logic} global.agent_characters[i]=char end', - raw=True, - ) - self.add_command("/sc player = global.agent_characters[1]", raw=True) - self.execute_transaction() + with self.server.transaction() as t: + color_logic = "" + if self.num_agents > 1: + color_logic = "if i==1 then char.color={r=0,g=1,b=0,a=1} elseif i==2 then char.color={r=0,g=0,b=1,a=1} end;" + + t.add_command( + f'/sc global.agent_characters = {{}}; for _,c in pairs(game.surfaces[1].find_entities_filtered{{type="character"}}) do if c then c.destroy() end end; for i=1,{self.num_agents} do local char = game.surfaces[1].create_entity{{name="character",position={{x=0,y=(i-1)*2}},force=game.forces.player}}; {color_logic} global.agent_characters[i]=char end', + raw=True, + ) + t.add_command("/sc player = global.agent_characters[1]", raw=True) def get_warnings(self, seconds=10): """ @@ -757,8 +747,7 @@ def get_warnings(self, seconds=10): :return: """ start = timer() - command = f"/silent-command rcon.print(dump(global.get_alerts({seconds})))" - lua_response = self.rcon_client.send_command(command) + lua_response = self.server.run_rcon_print(f"dump(global.get_alerts({seconds}))") # print(lua_response) alert_dict, duration = _lua2python("alerts", lua_response, start=start) if isinstance(alert_dict, dict): @@ -889,154 +878,8 @@ def run_snippet_file_in_factorio_env(self, file_path, clean=True): if clean: self.cleanup() - def register_post_tool_hook(self, tool_name, callback=None): - """ - Register a hook to be called after a specific tool is executed. - Can be used as a regular function or as a decorator. - - Args: - tool_name (str): Name of the tool to hook into - callback (callable, optional): Function to call after the tool is executed. - Will receive the tool instance and the result as arguments. - - Returns: - If used as a regular function (with callback provided), returns the callback. - If used as a decorator (without callback), returns a decorator function. - """ - # When used as a decorator without parentheses: @register_post_tool_hook - if tool_name is not None and callback is None and callable(tool_name): - callback = tool_name - tool_name = callback.__name__ - if not hasattr(self, "post_tool_hooks"): - self.post_tool_hooks = {} - if tool_name not in self.post_tool_hooks: - self.post_tool_hooks[tool_name] = [] - self.post_tool_hooks[tool_name].append(callback) - return callback - - # When used as a decorator with arguments: @register_post_tool_hook("tool_name") - if callback is None: - - def decorator(func): - if not hasattr(self, "post_tool_hooks"): - self.post_tool_hooks = {} - if tool_name not in self.post_tool_hooks: - self.post_tool_hooks[tool_name] = [] - self.post_tool_hooks[tool_name].append(func) - return func - - return decorator - - # When used as a regular function: register_post_tool_hook("tool_name", callback_func) - if not callable(callback): - raise TypeError("Callback must be callable") - - if not hasattr(self, "post_tool_hooks"): - self.post_tool_hooks = {} - if tool_name not in self.post_tool_hooks: - self.post_tool_hooks[tool_name] = [] - - self.post_tool_hooks[tool_name].append(callback) - return callback - - def register_pre_tool_hook(self, tool_name, callback=None): - """ - Register a hook to be called before a specific tool is executed. - Can be used as a regular function or as a decorator. - - Args: - tool_name (str): Name of the tool to hook into - callback (callable, optional): Function to call before the tool is executed. - Will receive the tool instance and the arguments as parameters. - - Returns: - If used as a regular function (with callback provided), returns the callback. - If used as a decorator (without callback), returns a decorator function. - """ - # When used as a decorator without parentheses: @register_pre_tool_hook - if tool_name is not None and callback is None and callable(tool_name): - callback = tool_name - tool_name = callback.__name__ - if not hasattr(self, "pre_tool_hooks"): - self.pre_tool_hooks = {} - if tool_name not in self.pre_tool_hooks: - self.pre_tool_hooks[tool_name] = [] - self.pre_tool_hooks[tool_name].append(callback) - return callback - - # When used as a decorator with arguments: @register_pre_tool_hook("tool_name") - if callback is None: - - def decorator(func): - if not hasattr(self, "pre_tool_hooks"): - self.pre_tool_hooks = {} - if tool_name not in self.pre_tool_hooks: - self.pre_tool_hooks[tool_name] = [] - self.pre_tool_hooks[tool_name].append(func) - return func - - return decorator - - # When used as a regular function: register_pre_tool_hook("tool_name", callback_func) - if not callable(callback): - raise TypeError("Callback must be callable") - - if not hasattr(self, "pre_tool_hooks"): - self.pre_tool_hooks = {} - if tool_name not in self.pre_tool_hooks: - self.pre_tool_hooks[tool_name] = [] - - self.pre_tool_hooks[tool_name].append(callback) - return callback - - def execute_post_tool_hooks(self, tool_name, tool_instance, result): - """ - Execute all hooks registered for a tool after it has been executed. - - Args: - tool_name (str): Name of the tool - tool_instance: The tool instance that was executed - result: The result of the tool execution - """ - if tool_name in self.post_tool_hooks: - for callback in self.post_tool_hooks[tool_name]: - try: - callback(tool_instance, result) - except Exception as e: - print(f"Error in post-tool hook for {tool_name}: {e}") - - def execute_pre_tool_hooks(self, tool_name, tool_instance, *args, **kwargs): - """ - Execute all hooks registered for a tool before it is executed. - - Args: - tool_name (str): Name of the tool - tool_instance: The tool instance to be executed - *args, **kwargs: The arguments passed to the tool - """ - if tool_name in self.pre_tool_hooks: - for callback in self.pre_tool_hooks[tool_name]: - try: - callback(tool_instance, *args, **kwargs) - except Exception as e: - print(f"Error in pre-tool hook for {tool_name}: {e}") - def cleanup(self): - # Close the RCON connection - if hasattr(self, "rcon_client") and self.rcon_client: - self.rcon_client.close() - - self.post_tool_hooks = {} - self.pre_tool_hooks = {} - - # Join all non-daemon threads - for thread in threading.enumerate(): - if ( - thread != threading.current_thread() - and thread.is_alive() - and not thread.daemon - ): - try: - thread.join(timeout=5) # Wait up to 5 seconds for each thread - except Exception as e: - print(f"Error joining thread {thread.name}: {e}") + self.server.cleanup() + + def reset(self, game_state: Optional[GameState] = None): + self.server.reset(game_state) \ No newline at end of file diff --git a/fle/env/lua_manager.py b/fle/env/lua_manager.py index 6304751fa..0cf5b7cda 100644 --- a/fle/env/lua_manager.py +++ b/fle/env/lua_manager.py @@ -30,14 +30,9 @@ def __init__(self, rcon_client: RCONClient, cache_scripts: bool = False): self.game_checksums = self._get_game_checksums(rcon_client) self.tool_scripts = self.get_tools_to_load() - self.lib_scripts = self.get_libs_to_load() self.lua = LuaRuntime(unpack_returned_tuples=True) - def init_action_checksums(self): - checksum_init_script = _load_lib("checksum") - response = self.rcon_client.send_command("/sc " + checksum_init_script) - return response def check_lua_syntax(self, script): try: @@ -49,28 +44,6 @@ def check_lua_syntax(self, script): return True, None return False, e.args[0] - # @deprecated("Using tools") - # def load_action_into_game(self, name): - # - # if name not in self.action_scripts: - # # attempt to load the script from the filesystem - # script = _load_action(name) - # self.action_scripts[name] = script - # - # script = self.action_scripts[name] - # if self.cache_scripts: - # checksum = self.calculate_checksum(script) - # if name in self.game_checksums and self.game_checksums[name] == checksum: - # return - # self.update_game_checksum(self.rcon_client, name, checksum) - # - # correct, error = self.check_lua_syntax(script) - # if not correct: - # raise Exception(f"Syntax error in: {name}: {error}") - # print(f"{self.rcon_client.port}: Loading action {name} into game") - # - # result = self.rcon_client.send_command(f'/sc ' + script) - def load_tool_into_game(self, name): # Find all scripts for this action by checking prefixes tool_scripts = [ @@ -130,22 +103,6 @@ def load_init_into_game(self, name): def calculate_checksum(self, content: str) -> str: return hashlib.md5(content.encode()).hexdigest() - # @deprecated("Moving to tools") - # def get_actions_to_load(self): - # scripts_to_load = {} - # script_names = _get_action_names() - # for script_file in script_names: - # name, content = _load_script(script_file) - # - # if self.cache_scripts: - # checksum = self.calculate_checksum(content) - # if (name not in self.game_checksums or - # self.game_checksums[name] != checksum): - # scripts_to_load[name] = content - # else: - # scripts_to_load[name] = content - # return scripts_to_load - def get_tools_to_load(self): scripts_to_load = {} lua_files = ( @@ -192,17 +149,3 @@ def get_libs_to_load(self): scripts_to_load[name] = content return scripts_to_load - - def update_game_checksum(self, rcon_client, script_name: str, checksum: str): - rcon_client.send_command( - f"/sc global.set_lua_script_checksum('{script_name}', '{checksum}')" - ) - - def _clear_game_checksums(self, rcon_client): - rcon_client.send_command("/sc global.clear_lua_script_checksums()") - - def _get_game_checksums(self, rcon_client): - response = rcon_client.send_command( - "/sc rcon.print(global.get_lua_script_checksums())" - ) - return json.loads(response) diff --git a/fle/env/protocols/mcp/state.py b/fle/env/protocols/mcp/state.py index 9c1ff88ab..da331d96a 100644 --- a/fle/env/protocols/mcp/state.py +++ b/fle/env/protocols/mcp/state.py @@ -48,7 +48,7 @@ def create_factorio_instance(self, instance_id: int) -> FactorioInstance: inventory={}, all_technologies_researched=True, ) - instance.speed(10) + instance.set_speed(10) return instance except Exception as e: print(f"Error creating Factorio instance: {e}") diff --git a/fle/env/tools/admin/get_factory_centroid/client.py b/fle/env/tools/admin/get_factory_centroid/client.py index a9948d6e1..53b03723c 100644 --- a/fle/env/tools/admin/get_factory_centroid/client.py +++ b/fle/env/tools/admin/get_factory_centroid/client.py @@ -4,9 +4,9 @@ class GetFactoryCentroid(Tool): - def __init__(self, lua_script_manager, game_state): + def __init__(self, factorio_server, game_state): self.state = {"input": {}, "output": {}} - super().__init__(lua_script_manager, game_state) + super().__init__(factorio_server, game_state) def __call__(self) -> Camera: """ diff --git a/fle/env/tools/admin/production_stats/client.py b/fle/env/tools/admin/production_stats/client.py index bec7e77d2..117d33b7b 100644 --- a/fle/env/tools/admin/production_stats/client.py +++ b/fle/env/tools/admin/production_stats/client.py @@ -4,9 +4,9 @@ class ProductionStats(Tool): - def __init__(self, lua_script_manager, game_state): + def __init__(self, factorio_server, game_state): self.state = {"input": {}, "output": {}} - super().__init__(lua_script_manager, game_state) + super().__init__(factorio_server, game_state) def __call__(self) -> Dict[str, Dict[str, int]]: """ diff --git a/fle/env/tools/agent/connect_entities/client.py b/fle/env/tools/agent/connect_entities/client.py index a462ee2f7..7401f191a 100644 --- a/fle/env/tools/agent/connect_entities/client.py +++ b/fle/env/tools/agent/connect_entities/client.py @@ -57,18 +57,18 @@ def __init__(self, connection, game_state): self._setup_resolvers() def _setup_actions(self): - self.request_path = RequestPath(self.connection, self.game_state) - self.get_path = GetPath(self.connection, self.game_state) - self.rotate_entity = RotateEntity(self.connection, self.game_state) - self.pickup_entity = PickupEntity(self.connection, self.game_state) - self.inspect_inventory = InspectInventory(self.connection, self.game_state) - self.get_entities = GetEntities(self.connection, self.game_state) - self.get_entity = GetEntity(self.connection, self.game_state) + self.request_path = RequestPath(self.factorio_server, self.game_state) + self.get_path = GetPath(self.factorio_server, self.game_state) + self.rotate_entity = RotateEntity(self.factorio_server, self.game_state) + self.pickup_entity = PickupEntity(self.factorio_server, self.game_state) + self.inspect_inventory = InspectInventory(self.factorio_server, self.game_state) + self.get_entities = GetEntities(self.factorio_server, self.game_state) + self.get_entity = GetEntity(self.factorio_server, self.game_state) self._extend_collision_boxes = ExtendCollisionBoxes( - self.connection, self.game_state + self.factorio_server, self.game_state ) self._clear_collision_boxes = ClearCollisionBoxes( - self.connection, self.game_state + self.factorio_server, self.game_state ) def _setup_resolvers(self): diff --git a/fle/env/tools/agent/harvest_resource/client.py b/fle/env/tools/agent/harvest_resource/client.py index 959d50124..04f24cb46 100644 --- a/fle/env/tools/agent/harvest_resource/client.py +++ b/fle/env/tools/agent/harvest_resource/client.py @@ -46,15 +46,15 @@ def __call__(self, position: Position, quantity=1, radius=10) -> int: # If `fast` is turned off - we need to long poll the game state to ensure the player has moved if not self.game_state.instance.fast: - remaining_steps = self.connection.rcon_client.send_command( - f"/silent-command rcon.print(global.actions.get_harvest_queue_length({self.player_index}))" + remaining_steps = self.factorio_server.run_rcon_print( + f"global.actions.get_harvest_queue_length({self.player_index})" ) attempt = 0 max_attempts = 10 while remaining_steps != "0" and attempt < max_attempts: sleep(0.5) - remaining_steps = self.connection.rcon_client.send_command( - f"/silent-command rcon.print(global.actions.get_harvest_queue_length({self.player_index}))" + remaining_steps = self.factorio_server.run_rcon_print( + f"global.actions.get_harvest_queue_length({self.player_index})" ) max_attempts = 50 @@ -80,8 +80,8 @@ def __call__(self, position: Position, quantity=1, radius=10) -> int: def get_resource_type_at_position(self, position: Position): x, y = self.get_position(position) - entity_at_position = self.connection.rcon_client.send_command( - f"/silent-command rcon.print(global.actions.get_resource_name_at_position({self.player_index}, {x}, {y}))" + entity_at_position = self.factorio_server.run_rcon_print( + f"global.actions.get_resource_name_at_position({self.player_index}, {x}, {y})" ) if entity_at_position.startswith("tree"): return Resource.Wood diff --git a/fle/env/tools/agent/move_to/client.py b/fle/env/tools/agent/move_to/client.py index 257726793..0dee2665d 100644 --- a/fle/env/tools/agent/move_to/client.py +++ b/fle/env/tools/agent/move_to/client.py @@ -72,13 +72,13 @@ def __call__( # If `fast` is turned off - we need to long poll the game state to ensure the player has moved if not self.game_state.instance.fast: - remaining_steps = self.connection.rcon_client.send_command( - f"/silent-command rcon.print(global.actions.get_walking_queue_length({self.player_index}))" + remaining_steps = self.factorio_server.run_rcon_print( + f"global.actions.get_walking_queue_length({self.player_index})" ) while remaining_steps != "0": sleep(0.5) - remaining_steps = self.connection.rcon_client.send_command( - f"/silent-command rcon.print(global.actions.get_walking_queue_length({self.player_index}))" + remaining_steps = self.factorio_server.run_rcon_print( + f"global.actions.get_walking_queue_length({self.player_index})" ) self.game_state.player_location = Position(x=position.x, y=position.y) diff --git a/fle/env/tools/agent/nearest_buildable/client.py b/fle/env/tools/agent/nearest_buildable/client.py index 5aa9dfe23..53b3846fb 100644 --- a/fle/env/tools/agent/nearest_buildable/client.py +++ b/fle/env/tools/agent/nearest_buildable/client.py @@ -4,8 +4,8 @@ class NearestBuildable(Tool): - def __init__(self, lua_script_manager, game_state): - super().__init__(lua_script_manager, game_state) + def __init__(self, factorio_server, game_state): + super().__init__(factorio_server, game_state) # self.connection = connection self.game_state = game_state diff --git a/fle/env/tools/agent/place_entity/client.py b/fle/env/tools/agent/place_entity/client.py index cc539dc9e..663034a99 100644 --- a/fle/env/tools/agent/place_entity/client.py +++ b/fle/env/tools/agent/place_entity/client.py @@ -90,7 +90,7 @@ def __call__( try: object = metaclass( - prototype=entity.name, game=self.connection, **cleaned_response + prototype=entity.name, game=self.factorio_server, **cleaned_response ) except Exception as e: raise Exception( diff --git a/fle/env/tools/agent/place_entity_next_to/client.py b/fle/env/tools/agent/place_entity_next_to/client.py index 23f42017c..7690a7564 100644 --- a/fle/env/tools/agent/place_entity_next_to/client.py +++ b/fle/env/tools/agent/place_entity_next_to/client.py @@ -54,7 +54,7 @@ def __call__( try: object = metaclass( - prototype=name, game=self.connection, **cleaned_response + prototype=name, game=self.factorio_server, **cleaned_response ) except Exception as e: raise Exception( diff --git a/fle/env/tools/agent/score/client.py b/fle/env/tools/agent/score/client.py index 9e2f23c61..f474d53df 100644 --- a/fle/env/tools/agent/score/client.py +++ b/fle/env/tools/agent/score/client.py @@ -21,7 +21,7 @@ def __call__(self, *args, **kwargs): if isinstance(response, str): raise Exception("Could not get player score", response) - return response["player"], goal + return response, goal # if __name__ == "__main__": diff --git a/fle/env/tools/controller.py b/fle/env/tools/controller.py index 5f5f6cd75..ab3de6f18 100644 --- a/fle/env/tools/controller.py +++ b/fle/env/tools/controller.py @@ -5,8 +5,8 @@ from slpp import slpp as lua, ParseError from fle.env.entities import Direction -from fle.env.lua_manager import LuaScriptManager from fle.env.namespace import FactorioNamespace +from fle.env.instance import FactorioServer from fle.env.utils.rcon import _lua2python COMMAND = "/silent-command" @@ -15,16 +15,14 @@ class Controller: def __init__( self, - lua_script_manager: "LuaScriptManager", + factorio_server: "FactorioServer", game_state: "FactorioNamespace", *args, **kwargs, ): - # assert isinstance(lua_script_manager, LuaScriptManager), f"Not correct: {type(lua_script_manager)}" - self.connection = lua_script_manager + self.factorio_server = factorio_server self.game_state = game_state self.name = self.camel_to_snake(self.__class__.__name__) - self.lua_script_manager = lua_script_manager self.player_index = ( game_state.agent_index + 1 ) # +1 because Factorio is 1-indexed @@ -137,7 +135,7 @@ def execute(self, *args) -> Tuple[Dict, Any]: parameters = [lua.encode(arg) for arg in args] invocation = f"pcall(global.actions.{self.name}{(', ' if parameters else '') + ','.join(parameters)})" wrapped = f"{COMMAND} a, b = {invocation}; rcon.print(dump({{a=a, b=b}}))" - lua_response = self.connection.rcon_client.send_command(wrapped) + lua_response = self.factorio_server.send_command(wrapped) parsed, elapsed = _lua2python(invocation, lua_response, start=start) if parsed is None: @@ -166,7 +164,7 @@ def execute2(self, *args) -> Tuple[Dict, Any]: parameters = [lua.encode(arg) for arg in args] invocation = f"pcall(global.actions.{self.name}{(', ' if parameters else '') + ','.join(parameters)})" wrapped = f"{COMMAND} a, b = {invocation}; rcon.print(dump({{a=a, b=b}}))" - lua_response = self.connection.rcon_client.send_command(wrapped) + lua_response = self.factorio_server.rcon_client.send_command(wrapped) parsed, elapsed = _lua2python(invocation, lua_response, start=start) if not parsed["a"] and "b" in parsed and isinstance(parsed["b"], str): parts = lua_response.split('["b"] = ') @@ -192,6 +190,6 @@ def execute2(self, *args) -> Tuple[Dict, Any]: def send(self, command, *parameters, trace=False) -> List[str]: start = timer() script = self._get_command(command, parameters=list(parameters), measured=False) - lua_response = self.connection.send_command(script) + lua_response = self.factorio_server.send_command(script) # print(lua_response) return _lua2python(command, lua_response, start=start) diff --git a/fle/env/tools/init.py b/fle/env/tools/init.py index 1098c56bb..b05594fc1 100644 --- a/fle/env/tools/init.py +++ b/fle/env/tools/init.py @@ -1,18 +1,17 @@ from fle.env.tools.controller import Controller -from fle.env.instance import FactorioInstance -from fle.env.lua_manager import LuaScriptManager +from fle.env.instance import FactorioInstance, FactorioServer class Init(Controller): def __init__( self, - lua_script_manager: "LuaScriptManager", + factorio_server: "FactorioServer", game_state: "FactorioInstance", *args, **kwargs, ): - super().__init__(lua_script_manager, game_state) + super().__init__(factorio_server, game_state) self.load() def load(self): - self.lua_script_manager.load_init_into_game(self.name) + self.factorio_server.load_init_into_game(self.name) diff --git a/fle/env/tools/tool.py b/fle/env/tools/tool.py index ab1df71de..0075ba078 100644 --- a/fle/env/tools/tool.py +++ b/fle/env/tools/tool.py @@ -1,19 +1,19 @@ from typing import Tuple, Union from fle.env.entities import Position, Entity -from fle.env.namespace import FactorioNamespace +from fle.env.namespace import FactorioNamespace, FactorioServer from fle.env.tools.controller import Controller class Tool(Controller): def __init__( self, - lua_script_manager: "FactorioLuaScriptManager", # noqa + factorio_server: "FactorioServer", game_state: "FactorioNamespace", *args, **kwargs, ): - super().__init__(lua_script_manager, game_state) + super().__init__(factorio_server, game_state) self.load() def get_position(self, position_or_entity: Union[Tuple, Position, Entity]): @@ -42,9 +42,4 @@ def get_error_message(self, response): return response def load(self): - # self.lua_script_manager.load_action_into_game(self.name) - self.lua_script_manager.load_tool_into_game(self.name) - # script = _load_action(self.name) - # if not script: - # raise Exception(f"Could not load {self.name}") - # self.connection.send_command(f'{COMMAND} '+script) + self.factorio_server.load_tool_into_game(self.name) diff --git a/fle/env/utils/controller_loader/system_prompt_generator.py b/fle/env/utils/controller_loader/system_prompt_generator.py index a030ed764..33973f931 100644 --- a/fle/env/utils/controller_loader/system_prompt_generator.py +++ b/fle/env/utils/controller_loader/system_prompt_generator.py @@ -15,7 +15,7 @@ def __init__(self, base_path: str): self.base_path = Path(base_path) self.tool_path = self.base_path / "tools" / "agent" - def generate(self, multiagent_str: str = "") -> str: + def generate(self, num_agents: int = 1, agent_idx: int = 0) -> str: # Generate schema schema_generator = SchemaGenerator(str(self.tool_path)) schema = schema_generator.generate_schema(with_docstring=True).replace( @@ -33,9 +33,20 @@ def generate(self, multiagent_str: str = "") -> str: # Load and process the manuals (agent.md files) manual_defs = ManualGenerator.generate_manual(str(self.base_path / "tools")) + + if num_agents > 1: + player_idx = agent_idx + 1 + multiagent_str = ( + f"## MULTIAGENT INSTRUCTIONS\n" + f"You are Agent {player_idx} out of {num_agents} agent(s) in the game. " + f"Follow your specific instructions given to you by the task." + f"Use the send_message() tool regularly to communicate with other agents about your current activities and any challenges you encounter. " + f"Start each program with a send_message() call to explain what you are doing. " + f"End each program with a send_message() call to confirm your actions. If your program errors out prior to send_message() being called, the message will not be sent. " + ) + if multiagent_str: manual_defs += f"\n\n{multiagent_str}" - # Combine all parts into final prompt return ( f"```types\n{type_defs}\n{entity_defs}\n```\n" diff --git a/fle/eval/algorithms/beam/run.py b/fle/eval/algorithms/beam/run.py index 467c09c8e..0aea8a9d5 100644 --- a/fle/eval/algorithms/beam/run.py +++ b/fle/eval/algorithms/beam/run.py @@ -137,7 +137,7 @@ async def main(): try: instances = create_factorio_instances() for instance in instances: - instance.speed(10) # Speed up the game for faster evaluation + instance.set_speed(10) # Speed up the game for faster evaluation except Exception: print( "\033[91mError initialising Factorio instances. Are the docker containers running, and have they been activated?\033[91m" diff --git a/fle/eval/algorithms/beam/run_milestones.py b/fle/eval/algorithms/beam/run_milestones.py index 8354b6aca..f4e7c7017 100644 --- a/fle/eval/algorithms/beam/run_milestones.py +++ b/fle/eval/algorithms/beam/run_milestones.py @@ -154,7 +154,7 @@ def create_factorio_instance(instance_id: int) -> FactorioInstance: inventory={}, all_technologies_researched=True, ) - instance.speed(10) + instance.set_speed(10) return instance diff --git a/fle/eval/algorithms/beam/run_parallel.py b/fle/eval/algorithms/beam/run_parallel.py index e8a39d07a..901f18067 100644 --- a/fle/eval/algorithms/beam/run_parallel.py +++ b/fle/eval/algorithms/beam/run_parallel.py @@ -46,7 +46,7 @@ def create_factorio_instances(start_index: int, count: int) -> List[FactorioInst inventory={}, all_technologies_researched=False, ) - instance.speed(10) + instance.set_speed(10) instances.append(instance) except Exception as e: errors.append(f"Failed to create instance at {ip}:{tcp_port} - {str(e)}") @@ -89,7 +89,7 @@ async def run_model_search( # Create 4 instances for each model's beam search instances = create_factorio_instances(instance_start, 4) for instance in instances: - instance.speed(10) + instance.set_speed(10) except Exception as e: print( f"\033[91mError initialising Factorio instances for model {model}: {e}\033[91m" diff --git a/fle/eval/algorithms/independent/trajectory_runner.py b/fle/eval/algorithms/independent/trajectory_runner.py index efba0976b..e8f6f0503 100644 --- a/fle/eval/algorithms/independent/trajectory_runner.py +++ b/fle/eval/algorithms/independent/trajectory_runner.py @@ -466,7 +466,7 @@ async def create_factorio_instance( else: instance = FactorioInstance(**common_kwargs) - instance.speed(10) + instance.set_speed(10) return instance diff --git a/fle/eval/algorithms/mcts/__main__chunked.py b/fle/eval/algorithms/mcts/__main__chunked.py index b9445c081..83a711762 100644 --- a/fle/eval/algorithms/mcts/__main__chunked.py +++ b/fle/eval/algorithms/mcts/__main__chunked.py @@ -258,7 +258,7 @@ async def main(): # Set up Factorio instances instances = create_factorio_instances() for instance in instances: - instance.speed(10) + instance.set_speed(10) initial_state = GameState.from_instance(instances[0]) diff --git a/fle/eval/algorithms/mcts/__main__objective_mcts.py b/fle/eval/algorithms/mcts/__main__objective_mcts.py index a6b8506ea..5d415f5b6 100644 --- a/fle/eval/algorithms/mcts/__main__objective_mcts.py +++ b/fle/eval/algorithms/mcts/__main__objective_mcts.py @@ -259,7 +259,7 @@ async def main(): # Set up Factorio instances instances = create_factorio_instances() for instance in instances: - instance.speed(10) + instance.set_speed(10) initial_state = GameState.from_instance(instances[0]) diff --git a/fle/eval/algorithms/mcts/__main__planning_agent.py b/fle/eval/algorithms/mcts/__main__planning_agent.py index 27b49ee47..a19659ca9 100644 --- a/fle/eval/algorithms/mcts/__main__planning_agent.py +++ b/fle/eval/algorithms/mcts/__main__planning_agent.py @@ -184,7 +184,7 @@ async def main(): instances = create_factorio_instances() for instance in instances: - instance.speed(10) # Set the game speed to 10x normal speed for faster testing + instance.set_speed(10) # Set the game speed to 10x normal speed for faster testing # Initialize FactorioEvaluator with the list of instances diff --git a/fle/eval/algorithms/mcts/main.py b/fle/eval/algorithms/mcts/main.py index 790cef0c5..499309418 100644 --- a/fle/eval/algorithms/mcts/main.py +++ b/fle/eval/algorithms/mcts/main.py @@ -89,7 +89,7 @@ async def main(): try: instances = create_factorio_instances() for instance in instances: - instance.speed(10) # Speed up the game for faster evaluation + instance.set_speed(10) # Speed up the game for faster evaluation except Exception: print( "\033[91mError initialising Factorio instances. Are the docker containers running, and have they been activated?\033[91m" diff --git a/fle/eval/tasks/task_abc.py b/fle/eval/tasks/task_abc.py index 178beb4ca..e1a4a8a80 100644 --- a/fle/eval/tasks/task_abc.py +++ b/fle/eval/tasks/task_abc.py @@ -58,7 +58,7 @@ def enhance_response_with_task_output( """Add task specific information to the environment response""" return response - def setup(self, instance): + def setup(self, instance: FactorioInstance): """setup function""" instance.initial_inventory = self.starting_inventory instance.all_technologies_researched = self.all_technology_reserached From 46d3902a7c3882a70356d45d2b038e3f9fc5f57f Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 13:42:23 +0530 Subject: [PATCH 17/53] moved script loading --- fle/env/instance.py | 206 ++++++++++------------------------------- fle/env/lua_manager.py | 63 ++++++++++++- 2 files changed, 107 insertions(+), 162 deletions(-) diff --git a/fle/env/instance.py b/fle/env/instance.py index d306708d9..bb72f79cb 100644 --- a/fle/env/instance.py +++ b/fle/env/instance.py @@ -34,6 +34,7 @@ SystemPromptGenerator, ) from fle.env.utils.rcon import _load_lib +from collections import defaultdict CHUNK_SIZE = 32 MAX_SAMPLES = 5000 @@ -119,10 +120,52 @@ def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: # Only execute if no exception occurred self.execute() +class ToolHookRegistry: + def __init__(self): + # Structure: hooks[tool_name]['pre' or 'post'] -> list of callbacks + self.hooks = defaultdict(lambda: {'pre': [], 'post': []}) + + def register(self, tool_name, phase, callback=None): + """ + Usage: + @registry.register("mine", "pre") + def before_mine(tool, *args): ... + + registry.register("mine", "post", callback_fn) + """ + if callback is None: + # used as decorator + def decorator(fn): + self.hooks[tool_name][phase].append(fn) + return fn + return decorator + else: + # direct call + self.hooks[tool_name][phase].append(callback) + return callback + + def execute(self, tool_name, phase, tool_instance, *args, **kwargs): + for fn in self.hooks[tool_name][phase]: + try: + fn(tool_instance, *args, **kwargs) + except Exception as e: + print(f"[HookError] {phase} hook for {tool_name}: {e}") + + @contextmanager + def around(self, tool_name, tool_instance, *args, **kwargs): + # run all pre-hooks + self.execute(tool_name, 'pre', tool_instance, *args, **kwargs) + try: + yield + finally: + # run all post-hooks + self.execute(tool_name, 'post', tool_instance, *args, **kwargs) + class FactorioServer: def __init__(self, address, tcp_port, cache_scripts=True): self.rcon_client, self.address = self.connect_to_server(address, tcp_port) self.cache_scripts = cache_scripts + self.tool_hook_registry = ToolHookRegistry() # Initialize Lua script manager and script dictionary self.lua_script_manager = LuaScriptManager(self.rcon_client, cache_scripts) self.script_dict = { @@ -188,140 +231,7 @@ def transaction(self): with transaction as t: yield t - def register_post_tool_hook(self, tool_name, callback=None): - """ - Register a hook to be called after a specific tool is executed. - Can be used as a regular function or as a decorator. - - Args: - tool_name (str): Name of the tool to hook into - callback (callable, optional): Function to call after the tool is executed. - Will receive the tool instance and the result as arguments. - - Returns: - If used as a regular function (with callback provided), returns the callback. - If used as a decorator (without callback), returns a decorator function. - """ - # When used as a decorator without parentheses: @register_post_tool_hook - if tool_name is not None and callback is None and callable(tool_name): - callback = tool_name - tool_name = callback.__name__ - if not hasattr(self, "post_tool_hooks"): - self.post_tool_hooks = {} - if tool_name not in self.post_tool_hooks: - self.post_tool_hooks[tool_name] = [] - self.post_tool_hooks[tool_name].append(callback) - return callback - - # When used as a decorator with arguments: @register_post_tool_hook("tool_name") - if callback is None: - - def decorator(func): - if not hasattr(self, "post_tool_hooks"): - self.post_tool_hooks = {} - if tool_name not in self.post_tool_hooks: - self.post_tool_hooks[tool_name] = [] - self.post_tool_hooks[tool_name].append(func) - return func - - return decorator - - # When used as a regular function: register_post_tool_hook("tool_name", callback_func) - if not callable(callback): - raise TypeError("Callback must be callable") - - if not hasattr(self, "post_tool_hooks"): - self.post_tool_hooks = {} - if tool_name not in self.post_tool_hooks: - self.post_tool_hooks[tool_name] = [] - - self.post_tool_hooks[tool_name].append(callback) - return callback - - def register_pre_tool_hook(self, tool_name, callback=None): - """ - Register a hook to be called before a specific tool is executed. - Can be used as a regular function or as a decorator. - - Args: - tool_name (str): Name of the tool to hook into - callback (callable, optional): Function to call before the tool is executed. - Will receive the tool instance and the arguments as parameters. - - Returns: - If used as a regular function (with callback provided), returns the callback. - If used as a decorator (without callback), returns a decorator function. - """ - # When used as a decorator without parentheses: @register_pre_tool_hook - if tool_name is not None and callback is None and callable(tool_name): - callback = tool_name - tool_name = callback.__name__ - if not hasattr(self, "pre_tool_hooks"): - self.pre_tool_hooks = {} - if tool_name not in self.pre_tool_hooks: - self.pre_tool_hooks[tool_name] = [] - self.pre_tool_hooks[tool_name].append(callback) - return callback - - # When used as a decorator with arguments: @register_pre_tool_hook("tool_name") - if callback is None: - - def decorator(func): - if not hasattr(self, "pre_tool_hooks"): - self.pre_tool_hooks = {} - if tool_name not in self.pre_tool_hooks: - self.pre_tool_hooks[tool_name] = [] - self.pre_tool_hooks[tool_name].append(func) - return func - - return decorator - - # When used as a regular function: register_pre_tool_hook("tool_name", callback_func) - if not callable(callback): - raise TypeError("Callback must be callable") - - if not hasattr(self, "pre_tool_hooks"): - self.pre_tool_hooks = {} - if tool_name not in self.pre_tool_hooks: - self.pre_tool_hooks[tool_name] = [] - - self.pre_tool_hooks[tool_name].append(callback) - return callback - - def execute_post_tool_hooks(self, tool_name, tool_instance, result): - """ - Execute all hooks registered for a tool after it has been executed. - - Args: - tool_name (str): Name of the tool - tool_instance: The tool instance that was executed - result: The result of the tool execution - """ - if tool_name in self.post_tool_hooks: - for callback in self.post_tool_hooks[tool_name]: - try: - callback(tool_instance, result) - except Exception as e: - print(f"Error in post-tool hook for {tool_name}: {e}") - - def execute_pre_tool_hooks(self, tool_name, tool_instance, *args, **kwargs): - """ - Execute all hooks registered for a tool before it is executed. - - Args: - tool_name (str): Name of the tool - tool_instance: The tool instance to be executed - *args, **kwargs: The arguments passed to the tool - """ - if tool_name in self.pre_tool_hooks: - for callback in self.pre_tool_hooks[tool_name]: - try: - callback(tool_instance, *args, **kwargs) - except Exception as e: - print(f"Error in pre-tool hook for {tool_name}: {e}") - - - def register_controllers(self, namespaces, invalidate_cache=False): + def register_controllers(self, namespaces: List[FactorioNamespace], invalidate_cache: bool = False): """ Load Python controllers from valid tool directories (those containing both client.py and server.lua) """ @@ -340,30 +250,13 @@ def snake_to_camel(snake_str): return "".join(word.capitalize() for word in snake_str.split("_")) # Create a function that wraps a tool's call method to execute hooks - def create_hook_wrapper(tool_name, original_callable): + def create_hook_wrapper(tool_name, orig_callable): from functools import wraps - @wraps(original_callable) + @wraps(orig_callable) def wrapper(*args, **kwargs): - # Execute pre-tool hooks - try: - self.execute_pre_tool_hooks( - tool_name, original_callable, *args, **kwargs - ) - except Exception as e: - print(f"Error in pre-tool hook for {tool_name}: {e}") - - # Execute the original callable - result = original_callable(*args, **kwargs) - - # Execute post-tool hooks - try: - self.execute_post_tool_hooks(tool_name, original_callable, result) - except Exception as e: - print(f"Error in post-tool hook for {tool_name}: {e}") - - return result - + with self.tool_hook_registry.around(tool_name, orig_callable, *args, **kwargs): + return orig_callable(*args, **kwargs) return wrapper # Walk through all subdirectories @@ -435,8 +328,7 @@ def cleanup(self): if hasattr(self, "rcon_client") and self.rcon_client: self.rcon_client.close() - self.post_tool_hooks = {} - self.pre_tool_hooks = {} + self.tool_hook_registry = ToolHookRegistry() # Join all non-daemon threads for thread in threading.enumerate(): diff --git a/fle/env/lua_manager.py b/fle/env/lua_manager.py index 0cf5b7cda..99cca7182 100644 --- a/fle/env/lua_manager.py +++ b/fle/env/lua_manager.py @@ -2,9 +2,9 @@ import json import os from pathlib import Path - +from collections import defaultdict from lupa.lua54 import LuaRuntime - +from contextlib import contextmanager from factorio_rcon import RCONClient from fle.env.utils.rcon import ( _get_dir, @@ -15,13 +15,54 @@ _load_script, ) +class ToolHookRegistry: + def __init__(self): + # Structure: hooks[tool_name]['pre' or 'post'] -> list of callbacks + self.hooks = defaultdict(lambda: {'pre': [], 'post': []}) + + def register(self, tool_name, phase, callback=None): + """ + Usage: + @registry.register("mine", "pre") + def before_mine(tool, *args): ... + + registry.register("mine", "post", callback_fn) + """ + if callback is None: + # used as decorator + def decorator(fn): + self.hooks[tool_name][phase].append(fn) + return fn + return decorator + else: + # direct call + self.hooks[tool_name][phase].append(callback) + return callback + + def execute(self, tool_name, phase, tool_instance, *args, **kwargs): + for fn in self.hooks[tool_name][phase]: + try: + fn(tool_instance, *args, **kwargs) + except Exception as e: + print(f"[HookError] {phase} hook for {tool_name}: {e}") + + @contextmanager + def around(self, tool_name, tool_instance, *args, **kwargs): + # run all pre-hooks + self.execute(tool_name, 'pre', tool_instance, *args, **kwargs) + try: + yield + finally: + # run all post-hooks + self.execute(tool_name, 'post', tool_instance, *args, **kwargs) + class LuaScriptManager: def __init__(self, rcon_client: RCONClient, cache_scripts: bool = False): self.rcon_client = rcon_client self.cache_scripts = cache_scripts if not cache_scripts: - self._clear_game_checksums(rcon_client) + self._clear_game_checksums() # self.action_directory = _get_action_dir() self.lib_directory = _get_lib_dir() @@ -34,6 +75,18 @@ def __init__(self, rcon_client: RCONClient, cache_scripts: bool = False): self.lua = LuaRuntime(unpack_returned_tuples=True) + def update_game_checksum(self, script_name: str, checksum: str): + self.rcon_client.send_command( + f"/sc global.set_lua_script_checksum('{script_name}', '{checksum}')" + ) + + def _clear_game_checksums(self): + self.rcon_client.send_command("/sc global.clear_lua_script_checksums()") + + def _get_game_checksums(self): + response = self.rcon_client.send_command("/sc rcon.print(global.get_lua_script_checksums())") + return json.loads(response) + def check_lua_syntax(self, script): try: self.lua.execute(script) @@ -75,7 +128,7 @@ def load_tool_into_game(self, name): and self.game_checksums[script_name] == checksum ): continue - self.update_game_checksum(self.rcon_client, script_name, checksum) + self.update_game_checksum(script_name, checksum) correct, error = self.check_lua_syntax(script) if not correct: @@ -96,7 +149,7 @@ def load_init_into_game(self, name): checksum = self.calculate_checksum(script) if name in self.game_checksums and self.game_checksums[name] == checksum: return - self.update_game_checksum(self.rcon_client, name, checksum) + self.update_game_checksum(name, checksum) self.rcon_client.send_command("/sc " + script) From 46c2de19831cc3a5f5ff9b04362b8adae8f5ea9e Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 13:42:57 +0530 Subject: [PATCH 18/53] moved script loading even more --- fle/env/instance.py | 147 ++--------------------------------------- fle/env/lua_manager.py | 70 ++++++++++++++++++++ 2 files changed, 74 insertions(+), 143 deletions(-) diff --git a/fle/env/instance.py b/fle/env/instance.py index bb72f79cb..977b9e8ed 100644 --- a/fle/env/instance.py +++ b/fle/env/instance.py @@ -24,7 +24,7 @@ from fle.env.entities import BoundingBox from fle.env.utils.camera import Camera -from fle.env.lua_manager import LuaScriptManager +from fle.env.lua_manager import LuaScriptManager, ToolHookRegistry from fle.env.namespace import FactorioNamespace from fle.env.utils.rcon import _lua2python, _get_dir from fle.commons.models.research_state import ResearchState @@ -120,47 +120,6 @@ def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: # Only execute if no exception occurred self.execute() -class ToolHookRegistry: - def __init__(self): - # Structure: hooks[tool_name]['pre' or 'post'] -> list of callbacks - self.hooks = defaultdict(lambda: {'pre': [], 'post': []}) - - def register(self, tool_name, phase, callback=None): - """ - Usage: - @registry.register("mine", "pre") - def before_mine(tool, *args): ... - - registry.register("mine", "post", callback_fn) - """ - if callback is None: - # used as decorator - def decorator(fn): - self.hooks[tool_name][phase].append(fn) - return fn - return decorator - else: - # direct call - self.hooks[tool_name][phase].append(callback) - return callback - - def execute(self, tool_name, phase, tool_instance, *args, **kwargs): - for fn in self.hooks[tool_name][phase]: - try: - fn(tool_instance, *args, **kwargs) - except Exception as e: - print(f"[HookError] {phase} hook for {tool_name}: {e}") - - @contextmanager - def around(self, tool_name, tool_instance, *args, **kwargs): - # run all pre-hooks - self.execute(tool_name, 'pre', tool_instance, *args, **kwargs) - try: - yield - finally: - # run all post-hooks - self.execute(tool_name, 'post', tool_instance, *args, **kwargs) - class FactorioServer: def __init__(self, address, tcp_port, cache_scripts=True): self.rcon_client, self.address = self.connect_to_server(address, tcp_port) @@ -182,18 +141,6 @@ def send_command(self, command: str): def load_init_into_game(self, init_scripts): for script_name in init_scripts: self.lua_script_manager.load_init_into_game(script_name) - - def update_game_checksum(self, script_name: str, checksum: str): - self.send_command( - f"/sc global.set_lua_script_checksum('{script_name}', '{checksum}')" - ) - - def _clear_game_checksums(self): - self.send_command("/sc global.clear_lua_script_checksums()") - - def _get_game_checksums(self): - response = self.run_rcon_print("global.get_lua_script_checksums()") - return json.loads(response) def init_action_checksums(self): checksum_init_script = _load_lib("checksum") @@ -231,97 +178,11 @@ def transaction(self): with transaction as t: yield t - def register_controllers(self, namespaces: List[FactorioNamespace], invalidate_cache: bool = False): + def register_controllers(self, namespaces: List["FactorioNamespace"], invalidate_cache: bool = False): """ - Load Python controllers from valid tool directories (those containing both client.py and server.lua) + Load Python controllers from valid tool directories (delegated). """ - - if invalidate_cache: - self.lua_script_manager = LuaScriptManager(self.rcon_client, False) - self.script_dict = { - **self.lua_script_manager.lib_scripts, - **self.lua_script_manager.tool_scripts, - } - - tool_dir = _get_dir("tools") - self.controllers = {} - - def snake_to_camel(snake_str): - return "".join(word.capitalize() for word in snake_str.split("_")) - - # Create a function that wraps a tool's call method to execute hooks - def create_hook_wrapper(tool_name, orig_callable): - from functools import wraps - - @wraps(orig_callable) - def wrapper(*args, **kwargs): - with self.tool_hook_registry.around(tool_name, orig_callable, *args, **kwargs): - return orig_callable(*args, **kwargs) - return wrapper - - # Walk through all subdirectories - for dirpath, _, filenames in os.walk(tool_dir): - # Skip the root directory - if dirpath == tool_dir: - continue - - # Check if this is a valid tool directory - server_file = os.path.join(dirpath, "server.lua") - client_file = os.path.join(dirpath, "client.py") - - if os.path.isfile(server_file) and os.path.isfile(client_file): - # Get the tool name from the directory - tool_name = os.path.basename(dirpath) - - directory_name = Path(dirpath).parent.name - # Load the Python module - module_spec = importlib.util.spec_from_file_location( - tool_name, - client_file, - ) - module = importlib.util.module_from_spec(module_spec) - module_spec.loader.exec_module(module) - - class_name = snake_to_camel(tool_name) - - # Handle special case renames - if tool_name == "place_entity": - class_name = "PlaceObject" - if tool_name == "score": - class_name = "Reward" - - try: - for i in range(len(namespaces)): - # Get and instantiate the controller class - callable_class = getattr(module, class_name) - callable_instance = callable_class( - self, namespaces[i] - ) - - # Create a wrapper that will execute hooks - wrapped_instance = create_hook_wrapper( - tool_name.lower(), callable_instance - ) - - # Store the controller and add it to namespace - self.controllers[tool_name.lower()] = callable_instance - - if directory_name == "admin": - # If this is an admin method, we hide it in the namespace by adding a shebang - setattr( - namespaces[i], - f"_{tool_name.lower()}", - wrapped_instance, - ) - else: - setattr( - namespaces[i], tool_name.lower(), wrapped_instance - ) - - except Exception as e: - raise Exception( - f"Could not instantiate {class_name} from {client_file}. {e}" - ) + self.lua_script_manager.register_controllers(self, namespaces, invalidate_cache) def cleanup(self): # Close the RCON connection diff --git a/fle/env/lua_manager.py b/fle/env/lua_manager.py index 99cca7182..673c27110 100644 --- a/fle/env/lua_manager.py +++ b/fle/env/lua_manager.py @@ -15,6 +15,8 @@ _load_script, ) +import importlib.util + class ToolHookRegistry: def __init__(self): # Structure: hooks[tool_name]['pre' or 'post'] -> list of callbacks @@ -202,3 +204,71 @@ def get_libs_to_load(self): scripts_to_load[name] = content return scripts_to_load + + + def register_controllers(self, server, namespaces: list, invalidate_cache: bool = False): + """ + Load Python controllers from valid tool directories via RCON. + Delegated from FactorioServer.register_controllers. + """ + # Reinitialize manager and script_dict if needed + if invalidate_cache: + server.lua_script_manager = LuaScriptManager(server.rcon_client, False) + manager = server.lua_script_manager + server.script_dict = { + **manager.lib_scripts, + **manager.tool_scripts, + } + else: + manager = self + + tool_dir = _get_dir("tools") + server.controllers = {} + + def snake_to_camel(snake_str): + return "".join(word.capitalize() for word in snake_str.split("_")) + + def create_hook_wrapper(tool_name, orig_callable): + from functools import wraps + + @wraps(orig_callable) + def wrapper(*args, **kwargs): + with server.tool_hook_registry.around(tool_name, orig_callable, *args, **kwargs): + return orig_callable(*args, **kwargs) + + return wrapper + + # Discover and load controllers + for dirpath, _, filenames in os.walk(tool_dir): + if dirpath == tool_dir: + continue + + server_file = os.path.join(dirpath, "server.lua") + client_file = os.path.join(dirpath, "client.py") + + if os.path.isfile(server_file) and os.path.isfile(client_file): + tool_name = os.path.basename(dirpath) + directory_name = Path(dirpath).parent.name + + # Load Python module + module_spec = importlib.util.spec_from_file_location(tool_name, client_file) + module = importlib.util.module_from_spec(module_spec) + module_spec.loader.exec_module(module) + + class_name = snake_to_camel(tool_name) + if tool_name == "place_entity": + class_name = "PlaceObject" + if tool_name == "score": + class_name = "Reward" + + try: + for i, namespace in enumerate(namespaces): + callable_class = getattr(module, class_name) + callable_instance = callable_class(server, namespace) + wrapped_instance = create_hook_wrapper(tool_name.lower(), callable_instance) + server.controllers[tool_name.lower()] = callable_instance + + attr = f"_{tool_name.lower()}" if directory_name == "admin" else tool_name.lower() + setattr(namespace, attr, wrapped_instance) + except Exception as e: + raise Exception(f"Could not instantiate {class_name} from {client_file}. {e}") From a9b65866dd27621aa104d3455c0c358452f6bc0c Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 14:49:10 +0530 Subject: [PATCH 19/53] tests passing --- fle/env/instance.py | 47 +++++++++++++++++++++++------------------- fle/env/lua_manager.py | 12 ++++++++--- fle/env/tools/tool.py | 3 ++- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/fle/env/instance.py b/fle/env/instance.py index 977b9e8ed..fbff8c43c 100644 --- a/fle/env/instance.py +++ b/fle/env/instance.py @@ -33,8 +33,6 @@ from fle.env.utils.controller_loader.system_prompt_generator import ( SystemPromptGenerator, ) -from fle.env.utils.rcon import _load_lib -from collections import defaultdict CHUNK_SIZE = 32 MAX_SAMPLES = 5000 @@ -126,12 +124,17 @@ def __init__(self, address, tcp_port, cache_scripts=True): self.cache_scripts = cache_scripts self.tool_hook_registry = ToolHookRegistry() # Initialize Lua script manager and script dictionary - self.lua_script_manager = LuaScriptManager(self.rcon_client, cache_scripts) + self.lua_script_manager = LuaScriptManager(self.rcon_client, self.tool_hook_registry, cache_scripts) self.script_dict = { **self.lua_script_manager.lib_scripts, **self.lua_script_manager.tool_scripts, } + def ensure_rcon_client(self): + if not self.rcon_client: + self.rcon_client = RCONClient(self.address, self.tcp_port, "factorio") + return self.rcon_client + def run_rcon_print(self, command: str): return self.rcon_client.send_command(f"/sc rcon.print({command})") @@ -139,13 +142,14 @@ def send_command(self, command: str): return self.rcon_client.send_command(command) def load_init_into_game(self, init_scripts): + if type(init_scripts) == str: + init_scripts = [init_scripts] for script_name in init_scripts: self.lua_script_manager.load_init_into_game(script_name) + + def load_tool_into_game(self, name): + self.lua_script_manager.load_tool_into_game(name) - def init_action_checksums(self): - checksum_init_script = _load_lib("checksum") - response = self.send_command("/sc " + checksum_init_script) - return response def connect_to_server(self, address, tcp_port): try: @@ -174,9 +178,15 @@ def _get_command(self, command, parameters=[], measured=True): @contextmanager def transaction(self): """Context manager for backward compatibility""" - transaction = FactorioTransaction(self) - with transaction as t: - yield t + tx = FactorioTransaction(self) + try: + yield tx + except: + # On error, do not execute queued commands + raise + else: + # Only execute if no exception occurred + tx.execute() def register_controllers(self, namespaces: List["FactorioNamespace"], invalidate_cache: bool = False): """ @@ -207,7 +217,7 @@ def cleanup(self): class FactorioInstance: namespace_class = FactorioNamespace _cleanup_registered = False # Only register cleanup once per process - namespaces = List[FactorioNamespace] + namespaces: List[FactorioNamespace] def __init__( self, @@ -327,17 +337,15 @@ def _reset_static_achievement_counters(self): """ This resets the cached production flows that we track for achievements and diversity sampling. """ - self.server.add_command( - "/sc global.crafted_items = {}; global.harvested_items = {}", raw=True - ) - self.server.execute_transaction() + with self.server.transaction() as t: + t.add_command("/sc global.crafted_items = {}; global.harvested_items = {}", raw=True) def _reset_elapsed_ticks(self): """ This resets the cached production flows that we track for achievements and diversity sampling. """ - self.server.add_command("/sc global.elapsed_ticks = 0", raw=True) - self.server.execute_transaction() + with self.server.transaction() as t: + t.add_command("/sc global.elapsed_ticks = 0", raw=True) def _reset(self, inventories: List[Dict[str, Any]]): with self.server.transaction() as t: @@ -632,7 +640,4 @@ def run_snippet_file_in_factorio_env(self, file_path, clean=True): self.cleanup() def cleanup(self): - self.server.cleanup() - - def reset(self, game_state: Optional[GameState] = None): - self.server.reset(game_state) \ No newline at end of file + self.server.cleanup() \ No newline at end of file diff --git a/fle/env/lua_manager.py b/fle/env/lua_manager.py index 673c27110..3c346472f 100644 --- a/fle/env/lua_manager.py +++ b/fle/env/lua_manager.py @@ -60,8 +60,9 @@ def around(self, tool_name, tool_instance, *args, **kwargs): class LuaScriptManager: - def __init__(self, rcon_client: RCONClient, cache_scripts: bool = False): + def __init__(self, rcon_client: RCONClient, tool_hook_registry: ToolHookRegistry, cache_scripts: bool = False): self.rcon_client = rcon_client + self.tool_hook_registry = tool_hook_registry self.cache_scripts = cache_scripts if not cache_scripts: self._clear_game_checksums() @@ -82,6 +83,11 @@ def update_game_checksum(self, script_name: str, checksum: str): f"/sc global.set_lua_script_checksum('{script_name}', '{checksum}')" ) + def init_action_checksums(self): + checksum_init_script = _load_lib("checksum") + response = self.rcon_client.send_command("/sc " + checksum_init_script) + return response + def _clear_game_checksums(self): self.rcon_client.send_command("/sc global.clear_lua_script_checksums()") @@ -213,7 +219,7 @@ def register_controllers(self, server, namespaces: list, invalidate_cache: bool """ # Reinitialize manager and script_dict if needed if invalidate_cache: - server.lua_script_manager = LuaScriptManager(server.rcon_client, False) + server.lua_script_manager = LuaScriptManager(server.rcon_client, server.tool_hook_registry, False) manager = server.lua_script_manager server.script_dict = { **manager.lib_scripts, @@ -233,7 +239,7 @@ def create_hook_wrapper(tool_name, orig_callable): @wraps(orig_callable) def wrapper(*args, **kwargs): - with server.tool_hook_registry.around(tool_name, orig_callable, *args, **kwargs): + with self.tool_hook_registry.around(tool_name, orig_callable, *args, **kwargs): return orig_callable(*args, **kwargs) return wrapper diff --git a/fle/env/tools/tool.py b/fle/env/tools/tool.py index 0075ba078..586f50a01 100644 --- a/fle/env/tools/tool.py +++ b/fle/env/tools/tool.py @@ -1,7 +1,8 @@ from typing import Tuple, Union from fle.env.entities import Position, Entity -from fle.env.namespace import FactorioNamespace, FactorioServer +from fle.env.namespace import FactorioNamespace +from fle.env.instance import FactorioServer from fle.env.tools.controller import Controller From 7921aea7965a0a1912d95b8977c69ec971ebcf0a Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 15:07:10 +0530 Subject: [PATCH 20/53] minor fixes all actions tests passed --- fle/env/instance.py | 2 +- fle/env/lua_manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fle/env/instance.py b/fle/env/instance.py index fbff8c43c..3898f54b7 100644 --- a/fle/env/instance.py +++ b/fle/env/instance.py @@ -177,7 +177,7 @@ def _get_command(self, command, parameters=[], measured=True): @contextmanager def transaction(self): - """Context manager for backward compatibility""" + """Context manager for executing transactions""" tx = FactorioTransaction(self) try: yield tx diff --git a/fle/env/lua_manager.py b/fle/env/lua_manager.py index 3c346472f..502c0a862 100644 --- a/fle/env/lua_manager.py +++ b/fle/env/lua_manager.py @@ -71,7 +71,7 @@ def __init__(self, rcon_client: RCONClient, tool_hook_registry: ToolHookRegistry self.lib_directory = _get_lib_dir() if cache_scripts: self.init_action_checksums() - self.game_checksums = self._get_game_checksums(rcon_client) + self.game_checksums = self._get_game_checksums() self.tool_scripts = self.get_tools_to_load() self.lib_scripts = self.get_libs_to_load() From 124bbe2013a005934a24ad7462c744e90b12f77c Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 15:08:00 +0530 Subject: [PATCH 21/53] formatted, no change --- fle/env/instance.py | 45 ++++++++++++++++++++++--------------- fle/env/lua_manager.py | 51 ++++++++++++++++++++++++++++++------------ 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/fle/env/instance.py b/fle/env/instance.py index 3898f54b7..afa7d7382 100644 --- a/fle/env/instance.py +++ b/fle/env/instance.py @@ -75,7 +75,9 @@ def from_factorio_direction(cls, direction): class FactorioTransaction: def __init__(self, server): self.server = server - self.commands: List[Tuple[str, List[Any], bool]] = [] # (command, parameters, is_raw) + self.commands: List[Tuple[str, List[Any], bool]] = ( + [] + ) # (command, parameters, is_raw) def add_command(self, command: str, *parameters, raw=False): self.commands.append((command, list(parameters), raw)) @@ -90,12 +92,14 @@ def execute(self) -> Dict[str, Any]: """Execute all commands in this transaction""" start = timer() rcon_commands = {} - + for idx, (command, parameters, is_raw) in enumerate(self.commands): if is_raw: rcon_commands[f"{idx}_{command}"] = command else: - script = self.server._get_command(command, parameters=parameters, measured=False) + script = self.server._get_command( + command, parameters=parameters, measured=False + ) rcon_commands[f"{idx}_{command}"] = script lua_responses = self.server.rcon_client.send_commands(rcon_commands) @@ -118,29 +122,32 @@ def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is None: # Only execute if no exception occurred self.execute() + class FactorioServer: def __init__(self, address, tcp_port, cache_scripts=True): self.rcon_client, self.address = self.connect_to_server(address, tcp_port) self.cache_scripts = cache_scripts self.tool_hook_registry = ToolHookRegistry() # Initialize Lua script manager and script dictionary - self.lua_script_manager = LuaScriptManager(self.rcon_client, self.tool_hook_registry, cache_scripts) + self.lua_script_manager = LuaScriptManager( + self.rcon_client, self.tool_hook_registry, cache_scripts + ) self.script_dict = { **self.lua_script_manager.lib_scripts, **self.lua_script_manager.tool_scripts, } - + def ensure_rcon_client(self): if not self.rcon_client: self.rcon_client = RCONClient(self.address, self.tcp_port, "factorio") return self.rcon_client - + def run_rcon_print(self, command: str): return self.rcon_client.send_command(f"/sc rcon.print({command})") - + def send_command(self, command: str): return self.rcon_client.send_command(command) - + def load_init_into_game(self, init_scripts): if type(init_scripts) == str: init_scripts = [init_scripts] @@ -149,8 +156,7 @@ def load_init_into_game(self, init_scripts): def load_tool_into_game(self, name): self.lua_script_manager.load_tool_into_game(name) - - + def connect_to_server(self, address, tcp_port): try: rcon_client = RCONClient(address, tcp_port, "factorio") @@ -188,7 +194,9 @@ def transaction(self): # Only execute if no exception occurred tx.execute() - def register_controllers(self, namespaces: List["FactorioNamespace"], invalidate_cache: bool = False): + def register_controllers( + self, namespaces: List["FactorioNamespace"], invalidate_cache: bool = False + ): """ Load Python controllers from valid tool directories (delegated). """ @@ -245,7 +253,6 @@ def __init__( self.peaceful = peaceful self.namespaces = [self.namespace_class(self, i) for i in range(num_agents)] - # Register controllers with the server self.server.register_controllers(self.namespaces) @@ -338,7 +345,9 @@ def _reset_static_achievement_counters(self): This resets the cached production flows that we track for achievements and diversity sampling. """ with self.server.transaction() as t: - t.add_command("/sc global.crafted_items = {}; global.harvested_items = {}", raw=True) + t.add_command( + "/sc global.crafted_items = {}; global.harvested_items = {}", raw=True + ) def _reset_elapsed_ticks(self): """ @@ -386,9 +395,9 @@ def _reset(self, inventories: List[Dict[str, Any]]): def reset(self, game_state: Optional[GameState] = None): # Reset the namespace (clear variables, functions etc) - assert not game_state or len(game_state.inventories) == self.num_agents, ( - "Game state must have the same number of inventories as num_agents" - ) + assert ( + not game_state or len(game_state.inventories) == self.num_agents + ), "Game state must have the same number of inventories as num_agents" for namespace in self.namespaces: namespace.reset() @@ -480,7 +489,7 @@ def initialise(self, fast=True): ] if self.peaceful: init_scripts.append("enemies") - + self.server.load_init_into_game(init_scripts) inventories = [self.initial_inventory] * self.num_agents @@ -640,4 +649,4 @@ def run_snippet_file_in_factorio_env(self, file_path, clean=True): self.cleanup() def cleanup(self): - self.server.cleanup() \ No newline at end of file + self.server.cleanup() diff --git a/fle/env/lua_manager.py b/fle/env/lua_manager.py index 502c0a862..4260167ae 100644 --- a/fle/env/lua_manager.py +++ b/fle/env/lua_manager.py @@ -17,10 +17,11 @@ import importlib.util + class ToolHookRegistry: def __init__(self): # Structure: hooks[tool_name]['pre' or 'post'] -> list of callbacks - self.hooks = defaultdict(lambda: {'pre': [], 'post': []}) + self.hooks = defaultdict(lambda: {"pre": [], "post": []}) def register(self, tool_name, phase, callback=None): """ @@ -35,6 +36,7 @@ def before_mine(tool, *args): ... def decorator(fn): self.hooks[tool_name][phase].append(fn) return fn + return decorator else: # direct call @@ -51,16 +53,21 @@ def execute(self, tool_name, phase, tool_instance, *args, **kwargs): @contextmanager def around(self, tool_name, tool_instance, *args, **kwargs): # run all pre-hooks - self.execute(tool_name, 'pre', tool_instance, *args, **kwargs) + self.execute(tool_name, "pre", tool_instance, *args, **kwargs) try: yield finally: # run all post-hooks - self.execute(tool_name, 'post', tool_instance, *args, **kwargs) + self.execute(tool_name, "post", tool_instance, *args, **kwargs) class LuaScriptManager: - def __init__(self, rcon_client: RCONClient, tool_hook_registry: ToolHookRegistry, cache_scripts: bool = False): + def __init__( + self, + rcon_client: RCONClient, + tool_hook_registry: ToolHookRegistry, + cache_scripts: bool = False, + ): self.rcon_client = rcon_client self.tool_hook_registry = tool_hook_registry self.cache_scripts = cache_scripts @@ -77,7 +84,6 @@ def __init__(self, rcon_client: RCONClient, tool_hook_registry: ToolHookRegistry self.lib_scripts = self.get_libs_to_load() self.lua = LuaRuntime(unpack_returned_tuples=True) - def update_game_checksum(self, script_name: str, checksum: str): self.rcon_client.send_command( f"/sc global.set_lua_script_checksum('{script_name}', '{checksum}')" @@ -92,7 +98,9 @@ def _clear_game_checksums(self): self.rcon_client.send_command("/sc global.clear_lua_script_checksums()") def _get_game_checksums(self): - response = self.rcon_client.send_command("/sc rcon.print(global.get_lua_script_checksums())") + response = self.rcon_client.send_command( + "/sc rcon.print(global.get_lua_script_checksums())" + ) return json.loads(response) def check_lua_syntax(self, script): @@ -211,15 +219,18 @@ def get_libs_to_load(self): return scripts_to_load - - def register_controllers(self, server, namespaces: list, invalidate_cache: bool = False): + def register_controllers( + self, server, namespaces: list, invalidate_cache: bool = False + ): """ Load Python controllers from valid tool directories via RCON. Delegated from FactorioServer.register_controllers. """ # Reinitialize manager and script_dict if needed if invalidate_cache: - server.lua_script_manager = LuaScriptManager(server.rcon_client, server.tool_hook_registry, False) + server.lua_script_manager = LuaScriptManager( + server.rcon_client, server.tool_hook_registry, False + ) manager = server.lua_script_manager server.script_dict = { **manager.lib_scripts, @@ -239,7 +250,9 @@ def create_hook_wrapper(tool_name, orig_callable): @wraps(orig_callable) def wrapper(*args, **kwargs): - with self.tool_hook_registry.around(tool_name, orig_callable, *args, **kwargs): + with self.tool_hook_registry.around( + tool_name, orig_callable, *args, **kwargs + ): return orig_callable(*args, **kwargs) return wrapper @@ -257,7 +270,9 @@ def wrapper(*args, **kwargs): directory_name = Path(dirpath).parent.name # Load Python module - module_spec = importlib.util.spec_from_file_location(tool_name, client_file) + module_spec = importlib.util.spec_from_file_location( + tool_name, client_file + ) module = importlib.util.module_from_spec(module_spec) module_spec.loader.exec_module(module) @@ -271,10 +286,18 @@ def wrapper(*args, **kwargs): for i, namespace in enumerate(namespaces): callable_class = getattr(module, class_name) callable_instance = callable_class(server, namespace) - wrapped_instance = create_hook_wrapper(tool_name.lower(), callable_instance) + wrapped_instance = create_hook_wrapper( + tool_name.lower(), callable_instance + ) server.controllers[tool_name.lower()] = callable_instance - attr = f"_{tool_name.lower()}" if directory_name == "admin" else tool_name.lower() + attr = ( + f"_{tool_name.lower()}" + if directory_name == "admin" + else tool_name.lower() + ) setattr(namespace, attr, wrapped_instance) except Exception as e: - raise Exception(f"Could not instantiate {class_name} from {client_file}. {e}") + raise Exception( + f"Could not instantiate {class_name} from {client_file}. {e}" + ) From d088d118b8317fa52b9273b953cd16a177338382 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 15:19:26 +0530 Subject: [PATCH 22/53] api changes + verbose set_speed method --- tests/actions/test_harvest_resource.py | 8 +-- tests/actions/test_sleep.py | 2 +- tests/benchmarks/benchmark_api.py | 2 +- tests/benchmarks/benchmark_interpreter.py | 2 +- tests/complex/test_slow.py | 2 +- tests/entities/test_rockets.py | 10 ++-- tests/eval/test_achievements.py | 2 +- tests/eval/test_game_state.py | 2 +- tests/eval/test_production_divergence.py | 2 +- tests/eval/test_profits.py | 2 +- tests/test_inventory_deduction.py | 68 +++++++++++++++++++++++ 11 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 tests/test_inventory_deduction.py diff --git a/tests/actions/test_harvest_resource.py b/tests/actions/test_harvest_resource.py index f1009f386..e692cabf4 100644 --- a/tests/actions/test_harvest_resource.py +++ b/tests/actions/test_harvest_resource.py @@ -78,8 +78,8 @@ def test_harvest_resource(game): def test_harvest_stump(game): instance = game.instance - instance.add_command(f"/c {create_stump}", raw=True) - instance.execute_transaction() + with instance.server.transaction() as t: + t.add_command(f"/c {create_stump}", raw=True) harvested = game.harvest_resource(Position(x=0, y=0), quantity=1) assert harvested == 2 @@ -96,8 +96,8 @@ def test_harvest_stump(game): def test_harvest_rock(game): instance = game.instance - instance.add_command(f"/c {create_rock}", raw=True) - instance.execute_transaction() + with instance.server.transaction() as t: + t.add_command(f"/c {create_rock}", raw=True) harvested = game.harvest_resource(Position(x=0, y=0), quantity=1) assert harvested == 20 diff --git a/tests/actions/test_sleep.py b/tests/actions/test_sleep.py index 97a79ed34..a5bb7f420 100644 --- a/tests/actions/test_sleep.py +++ b/tests/actions/test_sleep.py @@ -12,7 +12,7 @@ def game(instance): def test_sleep(game): for i in range(10): - game.instance.speed(i) + game.instance.set_speed(i) speed = game.instance.get_speed() start_time = time.time() game.sleep(10) diff --git a/tests/benchmarks/benchmark_api.py b/tests/benchmarks/benchmark_api.py index d68efd257..6107f6add 100644 --- a/tests/benchmarks/benchmark_api.py +++ b/tests/benchmarks/benchmark_api.py @@ -21,7 +21,7 @@ def run_benchmark(game: FactorioInstance, num_iterations: int = 100): results = {} - game.speed(10) + game.set_speed(10) for name, func in benchmarks.items(): start_time = time.time() count = func(game, num_iterations) diff --git a/tests/benchmarks/benchmark_interpreter.py b/tests/benchmarks/benchmark_interpreter.py index 6f18708af..2db95e2bd 100644 --- a/tests/benchmarks/benchmark_interpreter.py +++ b/tests/benchmarks/benchmark_interpreter.py @@ -20,7 +20,7 @@ def run_string_benchmark(game: FactorioInstance, num_iterations: int = 100): results = {} - game.speed(10) + game.set_speed(10) for name, func in benchmarks.items(): start_time = time.time() count = func(game, num_iterations) diff --git a/tests/complex/test_slow.py b/tests/complex/test_slow.py index 954a19dfe..2a1aa5e36 100644 --- a/tests/complex/test_slow.py +++ b/tests/complex/test_slow.py @@ -8,7 +8,7 @@ def game(): instance = FactorioInstance( address="localhost", bounding_box=200, tcp_port=27000, fast=True, inventory={} ) - instance.speed(1) + instance.set_speed(1) instance.reset() yield instance.namespace diff --git a/tests/entities/test_rockets.py b/tests/entities/test_rockets.py index f7f9c2d4b..e996ad2c1 100644 --- a/tests/entities/test_rockets.py +++ b/tests/entities/test_rockets.py @@ -146,11 +146,11 @@ def test_rocket_launch(game): "low-density-structure": 112, } inventory_items_json = json.dumps(inventory_items) - game.instance.add_command( - f"/c global.actions.initialise_inventory({1}, '{inventory_items_json}')", - raw=True, - ) - game.instance.execute_transaction() + with game.instance.server.transaction() as t: + t.add_command( + f"/c global.actions.initialise_inventory({1}, '{inventory_items_json}')", + raw=True, + ) # Verify initial state # assert silo.status == EntityStatus.NORMAL diff --git a/tests/eval/test_achievements.py b/tests/eval/test_achievements.py index 14db33f73..51629f5b5 100644 --- a/tests/eval/test_achievements.py +++ b/tests/eval/test_achievements.py @@ -14,7 +14,7 @@ def test_achievements(self): # cache_scripts=False, inventory={}, ) - instance.speed(10) + instance.set_speed(10) test_string_1 = "pos = nearest(Resource.Stone)\nmove_to(pos)\nharvest_resource(pos, 10)\ncraft_item(Prototype.StoneFurnace, 1)\npos = nearest(Resource.Coal)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = nearest(Resource.IronOre)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = Position(x = 0, y = 0)\nmove_to(pos)\nfurnace = place_entity(Prototype.StoneFurnace, position = pos)\ninsert_item(Prototype.IronOre, furnace, 5)\ninsert_item(Prototype.Coal, furnace, 5)\nsleep(16)\nextract_item(Prototype.IronPlate, furnace.position, 10)" _, _, _, achievements = eval_program_with_achievements(instance, test_string_1) diff --git a/tests/eval/test_game_state.py b/tests/eval/test_game_state.py index d5ad1c191..0ab820d77 100644 --- a/tests/eval/test_game_state.py +++ b/tests/eval/test_game_state.py @@ -34,7 +34,7 @@ def test_game_state(): # cache_scripts=False, inventory={}, ) - instance.speed(10) + instance.set_speed(10) instance.reset(game_state) pass diff --git a/tests/eval/test_production_divergence.py b/tests/eval/test_production_divergence.py index 7e96611bf..573105333 100644 --- a/tests/eval/test_production_divergence.py +++ b/tests/eval/test_production_divergence.py @@ -53,7 +53,7 @@ def test_achievements(self): # cache_scripts=False, inventory={}, ) - instance.speed(10) + instance.set_speed(10) _, _, _, achievements = eval_program_with_achievements(instance, test_string_1) ground_truth_achievement = { diff --git a/tests/eval/test_profits.py b/tests/eval/test_profits.py index 0233e08fc..4326bbcc7 100644 --- a/tests/eval/test_profits.py +++ b/tests/eval/test_profits.py @@ -12,7 +12,7 @@ def test_profits(): # cache_scripts=False, inventory={}, ) - instance.speed(10) + instance.set_speed(10) profit_config = {"max_static_unit_profit_cap": 5, "dynamic_profit_multiplier": 10} test_string_1 = "pos = nearest(Resource.Stone)\nmove_to(pos)\nharvest_resource(pos, 10)\ncraft_item(Prototype.StoneFurnace, 2)\npos = nearest(Resource.Coal)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = nearest(Resource.IronOre)\nmove_to(pos)\nharvest_resource(pos, 10)\npos = Position(x = 0, y = 0)\nmove_to(pos)\nfurnace = place_entity(Prototype.StoneFurnace, position = pos)\ninsert_item(Prototype.IronOre, furnace, 5)\ninsert_item(Prototype.Coal, furnace, 5)\nsleep(25)\nextract_item(Prototype.IronPlate, furnace.position, 10)" _, _, _, profits = eval_program_with_profits(instance, test_string_1, profit_config) diff --git a/tests/test_inventory_deduction.py b/tests/test_inventory_deduction.py new file mode 100644 index 000000000..53782b95c --- /dev/null +++ b/tests/test_inventory_deduction.py @@ -0,0 +1,68 @@ +import pytest +from fle.env import FactorioInstance +from fle.env.game_types import Prototype +from fle.env.entities import Position, Direction + +@pytest.fixture() +def game(instance): + instance.initial_inventory = { + 'pipe': 10, + 'small-electric-pole': 5, + 'transport-belt': 20 + } + instance.reset() + yield instance.namespace + instance.reset() + +def test_inventory_deduction_after_placement(game): + """Test that items are properly deducted from inventory after entity placement""" + + # Check initial inventory + initial_inventory = game.inspect_inventory() + initial_pipe_count = initial_inventory.get(Prototype.Pipe, 0) + initial_pole_count = initial_inventory.get(Prototype.SmallElectricPole, 0) + + print(f"Initial pipes: {initial_pipe_count}") + print(f"Initial poles: {initial_pole_count}") + + # Place a single pipe + game.move_to(Position(0, 0)) + pipe = game.place_entity(Prototype.Pipe, Direction.UP, Position(0, 0)) + + # Check inventory after placing pipe + inventory_after_pipe = game.inspect_inventory() + pipes_after_placement = inventory_after_pipe.get(Prototype.Pipe, 0) + + print(f"Pipes after placement: {pipes_after_placement}") + + # Assert that exactly 1 pipe was deducted + assert pipes_after_placement == initial_pipe_count - 1, f"Expected {initial_pipe_count - 1} pipes, got {pipes_after_placement}" + + # Test connect_entities to see if it properly deducts items + game.move_to(Position(5, 0)) + pole1 = game.place_entity(Prototype.SmallElectricPole, Direction.UP, Position(5, 0)) + + game.move_to(Position(10, 0)) + pole2 = game.place_entity(Prototype.SmallElectricPole, Direction.UP, Position(10, 0)) + + # Check inventory before connection + inventory_before_connect = game.inspect_inventory() + pipes_before_connect = inventory_before_connect.get(Prototype.Pipe, 0) + + print(f"Pipes before connection: {pipes_before_connect}") + + # Connect the poles with pipes + game.connect_entities(pole1, pole2, connection_type=Prototype.Pipe) + + # Check inventory after connection + inventory_after_connect = game.inspect_inventory() + pipes_after_connect = inventory_after_connect.get(Prototype.Pipe, 0) + + print(f"Pipes after connection: {pipes_after_connect}") + + # Assert that pipes were deducted during connection + # The exact number depends on the distance and path, but it should be less than before + assert pipes_after_connect < pipes_before_connect, f"Expected fewer pipes after connection, before: {pipes_before_connect}, after: {pipes_after_connect}" + +if __name__ == "__main__": + pytest.main([__file__]) \ No newline at end of file From 99e5a212c2935559bacb57c8ace32db3bd8d6e22 Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 19:19:08 +0530 Subject: [PATCH 23/53] minor changes --- fle/cluster/factorio-server/run-envs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fle/cluster/factorio-server/run-envs.py b/fle/cluster/factorio-server/run-envs.py index 65e622548..2f7bb8d5b 100644 --- a/fle/cluster/factorio-server/run-envs.py +++ b/fle/cluster/factorio-server/run-envs.py @@ -42,6 +42,8 @@ def __init__(self): self.server_config_dir = Path(__file__).parent.resolve() / "config" self.mode = Mode.SAVE_BASED.value self.temp_playing_dir = "/opt/factorio/temp/currently-playing" + with open(self.server_config_dir / "rconpw", "r") as f: + self.factorio_password = f.read().strip() def _detect_mods_path(self) -> str: if any(x in self.os_name for x in ("MINGW", "MSYS", "CYGWIN")): From ed24b00d52be578bab318aa202df82101884940e Mon Sep 17 00:00:00 2001 From: hrshtt Date: Tue, 29 Jul 2025 19:24:56 +0530 Subject: [PATCH 24/53] dir/module name change --- .../default_lab_scenario/blueprint.zip | Bin 215969 -> 0 bytes .../default_lab_scenario/control.lua | 439 ------------------ .../default_lab_scenario/control2.lua | 6 - .../default_lab_scenario/description.json | 5 - .../default_lab_scenario/freeplay.lua | 299 ------------ .../scenarios/default_lab_scenario/info.json | 1 - .../locale/en/freeplay.cfg | 4 - .../locale/en/level-01-locale.cfg | 71 --- .../default_lab_scenario/paperclips.lua | 81 ---- .../default_lab_scenario/planet1.png | Bin 505684 -> 0 bytes .../scenarios/default_lab_scenario/script.dat | Bin 1441 -> 0 bytes .../scenarios/open_world/README.md | 1 - fle/cluster/factorio_server/__init__.py | 0 .../config/map-gen-settings.json | 0 .../config/map-settings.json | 0 .../config/rconpw | 0 .../config/server-settings.json | 0 .../run_envs.py} | 0 .../factorio_server/server_controller.py | 63 +++ 19 files changed, 63 insertions(+), 907 deletions(-) delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/blueprint.zip delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/control.lua delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/control2.lua delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/description.json delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/freeplay.lua delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/info.json delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/freeplay.cfg delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/locale/en/level-01-locale.cfg delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/paperclips.lua delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/planet1.png delete mode 100644 fle/cluster/factorio-server/scenarios/default_lab_scenario/script.dat delete mode 100644 fle/cluster/factorio-server/scenarios/open_world/README.md create mode 100644 fle/cluster/factorio_server/__init__.py rename fle/cluster/{factorio-server => factorio_server}/config/map-gen-settings.json (100%) rename fle/cluster/{factorio-server => factorio_server}/config/map-settings.json (100%) rename fle/cluster/{factorio-server => factorio_server}/config/rconpw (100%) rename fle/cluster/{factorio-server => factorio_server}/config/server-settings.json (100%) rename fle/cluster/{factorio-server/run-envs.py => factorio_server/run_envs.py} (100%) create mode 100644 fle/cluster/factorio_server/server_controller.py diff --git a/fle/cluster/factorio-server/scenarios/default_lab_scenario/blueprint.zip b/fle/cluster/factorio-server/scenarios/default_lab_scenario/blueprint.zip deleted file mode 100644 index e603e83157500bd58d9179383dfdfae913250b94..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 215969 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