synth-ai 0.2.13.dev1__py3-none-any.whl → 0.2.13.dev2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synth-ai might be problematic. Click here for more details.
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +12 -1
- examples/swe/task_app/grpo_swe_mini.py +55 -26
- examples/swe/task_app/hosted/rollout.py +40 -0
- examples/swe/task_app/hosted/test_service.py +5 -6
- examples/task_apps/TESTING.md +275 -0
- examples/task_apps/__init__.py +0 -0
- examples/task_apps/crafter/__init__.py +0 -0
- examples/task_apps/crafter/task_app/__init__.py +2 -0
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter.py +18 -13
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/grpo_crafter_task_app.py +1 -1
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/policy.py +60 -4
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/policy_routes.py +25 -3
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/rollout.py +10 -0
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_service.py +5 -6
- examples/task_apps/dev/pokemon_emerald/__init__.py +2 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/README.md +811 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/__init__.py +120 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/action.py +160 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/memory.py +155 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/perception.py +69 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/planning.py +96 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/simple.py +1502 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/agent/system_prompt.py +4 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/grab_map.py +68 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/manual.py +216 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/__init__.py +35 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emerald_utils.py +631 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/emulator.py +1544 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/enums.py +1428 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/memory_reader.py +4848 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/types.py +41 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pokemon_env/utils.py +298 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/pyproject.toml +95 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/run.py +204 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/app.py +2152 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/client.py +429 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/server/frame_server.py +155 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/README.md +78 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/run_tests.py +122 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_direct.py +76 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_agent_prompts.py +413 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_battle_state_formatting.py +204 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection.py +133 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_dialogue_detection_comprehensive.py +229 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_direct_agent_emulator.py +300 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_fps_adjustment_pytest.py +205 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_direct.py +200 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_house_to_outside_transition.py +284 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_map_ground_truth_comparison.py +468 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_memory_map.py +575 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_server_map_validation.py +311 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/tests/test_torchic_state.py +259 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/__init__.py +0 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/anticheat.py +372 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/checkpoint.py +296 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/error_handler.py +275 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/get_local_ip.py +22 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/helpers.py +44 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/llm_logger.py +514 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_formatter.py +415 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher.py +1763 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_stitcher_singleton.py +33 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_trimmer.py +106 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/map_visualizer.py +334 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/ocr_dialogue.py +1020 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/recording.py +188 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/state_formatter.py +1481 -0
- examples/task_apps/dev/pokemon_emerald/external/pokeagent-speedrun/utils/vlm.py +862 -0
- examples/task_apps/dev/pokemon_emerald/modal_app.py +114 -0
- examples/task_apps/dev/pokemon_emerald/task_app/README.md +81 -0
- examples/task_apps/dev/pokemon_emerald/task_app/__init__.py +6 -0
- examples/task_apps/dev/pokemon_emerald/task_app/pokemon_emerald.py +685 -0
- examples/task_apps/enron/__init__.py +1 -0
- examples/task_apps/enron/eval_groq_qwen32.toml +16 -0
- examples/task_apps/enron/task_app/README.md +14 -0
- examples/task_apps/enron/task_app/__init__.py +1 -0
- examples/task_apps/enron/task_app/grpo_enron.py +906 -0
- examples/task_apps/enron/task_app/grpo_enron_task_app.py +146 -0
- examples/task_apps/enron/tests/__init__.py +2 -0
- examples/task_apps/enron/tests/conftest.py +115 -0
- examples/task_apps/enron/tests/integration/__init__.py +2 -0
- examples/task_apps/enron/tests/integration/test_enron_eval.py +177 -0
- examples/task_apps/enron/tests/integration/test_enron_rollout.py +135 -0
- examples/task_apps/enron/tests/unit/__init__.py +2 -0
- examples/task_apps/enron/tests/unit/test_enron_environment.py +126 -0
- examples/task_apps/math/__init__.py +0 -0
- examples/{rl/task_app → task_apps/math}/math_single_step.py +19 -10
- examples/task_apps/pokemon_battle/__init__.py +2 -0
- examples/task_apps/pokemon_battle/modal_app.py +104 -0
- examples/task_apps/pokemon_battle/task_app/README.md +68 -0
- examples/task_apps/pokemon_battle/task_app/__init__.py +6 -0
- examples/task_apps/pokemon_battle/task_app/pokemon_showdown.py +932 -0
- examples/task_apps/pokemon_red/README.md +357 -0
- examples/task_apps/pokemon_red/__init__.py +3 -0
- examples/task_apps/pokemon_red/eval_pokemon_red_policy.py +225 -0
- examples/task_apps/pokemon_red/pallet_town_rl_config.toml +73 -0
- examples/task_apps/pokemon_red/task_app.py +606 -0
- examples/task_apps/pokemon_red/test_pallet_town_rewards.py +191 -0
- examples/task_apps/sokoban/README.md +307 -0
- examples/task_apps/sokoban/__init__.py +3 -0
- examples/task_apps/sokoban/eval_groq_qwen32.toml +16 -0
- examples/task_apps/sokoban/eval_openai_gpt5.toml +16 -0
- examples/task_apps/sokoban/task_app.py +1058 -0
- examples/task_apps/sokoban/tests/__init__.py +2 -0
- examples/task_apps/sokoban/tests/conftest.py +113 -0
- examples/task_apps/sokoban/tests/integration/__init__.py +2 -0
- examples/task_apps/sokoban/tests/integration/test_sokoban_eval.py +57 -0
- examples/task_apps/sokoban/tests/integration/test_sokoban_rollout.py +198 -0
- examples/task_apps/sokoban/tests/unit/__init__.py +2 -0
- examples/task_apps/sokoban/tests/unit/test_sokoban_environment.py +114 -0
- examples/task_apps/verilog/__init__.py +1 -0
- examples/task_apps/verilog/eval_groq_qwen32b.toml +20 -0
- examples/task_apps/verilog/task_app/README.md +12 -0
- examples/task_apps/verilog/task_app/__init__.py +1 -0
- examples/task_apps/verilog/task_app/grpo_verilog.py +931 -0
- examples/task_apps/verilog/task_app/grpo_verilog_task_app.py +145 -0
- examples/task_apps/verilog/tests/__init__.py +2 -0
- examples/task_apps/verilog/tests/conftest.py +115 -0
- examples/task_apps/verilog/tests/integration/__init__.py +2 -0
- examples/task_apps/verilog/tests/integration/test_verilog_eval.py +179 -0
- examples/task_apps/verilog/tests/integration/test_verilog_rollout.py +55 -0
- examples/task_apps/verilog/tests/unit/__init__.py +2 -0
- examples/task_apps/verilog/tests/unit/test_verilog_scoring.py +118 -0
- examples/vlm/crafter_openai_vlm_agent.py +4 -4
- examples/vlm/run_crafter_vlm_benchmark.py +4 -4
- examples/workflows/__init__.py +0 -0
- examples/workflows/math_rl/__init__.py +0 -0
- examples/workflows/math_rl/download_dataset.py +80 -0
- synth_ai/__init__.py +2 -2
- synth_ai/api/train/builders.py +25 -11
- synth_ai/api/train/cli.py +12 -6
- synth_ai/api/train/configs/__init__.py +10 -10
- synth_ai/api/train/configs/rl.py +5 -4
- synth_ai/api/train/configs/sft.py +4 -3
- synth_ai/api/train/env_resolver.py +5 -2
- synth_ai/api/train/supported_algos.py +10 -5
- synth_ai/api/train/utils.py +7 -4
- synth_ai/cli/__init__.py +7 -51
- synth_ai/cli/_storage.py +4 -3
- synth_ai/cli/_validate_task_app.py +11 -0
- synth_ai/cli/balance.py +4 -3
- synth_ai/cli/calc.py +2 -2
- synth_ai/cli/demo.py +14 -7
- synth_ai/cli/legacy_root_backup.py +1 -1
- synth_ai/cli/rl_demo.py +8 -7
- synth_ai/cli/root.py +0 -97
- synth_ai/cli/task_apps.py +1707 -186
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +28 -16
- synth_ai/environments/examples/enron/engine.py +7 -2
- synth_ai/environments/examples/enron/environment.py +68 -0
- synth_ai/environments/examples/red/engine.py +27 -0
- synth_ai/environments/examples/red/engine_helpers/memory_map.py +7 -0
- synth_ai/environments/examples/red/engine_helpers/reward_library/pallet_town_progression.py +477 -0
- synth_ai/environments/examples/red/engine_helpers/state_extraction.py +32 -0
- synth_ai/environments/examples/red/environment.py +60 -0
- synth_ai/environments/examples/sokoban/taskset.py +116 -0
- synth_ai/environments/examples/verilog/engine.py +30 -4
- synth_ai/evals/client.py +58 -61
- synth_ai/jobs/client.py +16 -4
- synth_ai/judge_schemas.py +16 -16
- synth_ai/py.typed +0 -0
- synth_ai/task/__init__.py +14 -5
- synth_ai/task/contracts.py +124 -38
- synth_ai/task/proxy.py +48 -56
- synth_ai/task/rubrics/__init__.py +53 -0
- synth_ai/task/rubrics/loaders.py +133 -0
- synth_ai/task/rubrics/models.py +57 -0
- synth_ai/task/rubrics/scoring.py +113 -0
- synth_ai/{rubrics/validators.py → task/rubrics/strict.py} +53 -30
- synth_ai/task/server.py +8 -7
- synth_ai/task/validators.py +269 -6
- synth_ai/tracing_v3/decorators.py +7 -3
- synth_ai/tracing_v3/replica_sync.py +4 -4
- synth_ai/tracing_v3/serialization.py +5 -5
- synth_ai/tracing_v3/trace_utils.py +317 -0
- synth_ai/tracing_v3/turso/native_manager.py +3 -3
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/METADATA +4 -1
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/RECORD +214 -101
- examples/agora_ex/README_MoE.md +0 -224
- examples/agora_ex/__init__.py +0 -7
- examples/agora_ex/agora_ex.py +0 -65
- examples/agora_ex/agora_ex_task_app.py +0 -590
- examples/agora_ex/configs/rl_lora_qwen3_moe_2xh200.toml +0 -121
- examples/agora_ex/reward_fn_grpo-human.py +0 -129
- examples/agora_ex/system_prompt_CURRENT.md +0 -63
- examples/agora_ex/task_app/agora_ex_task_app.py +0 -590
- examples/agora_ex/task_app/reward_fn_grpo-human.py +0 -129
- examples/agora_ex/task_app/system_prompt_CURRENT.md +0 -63
- synth_ai/rubrics/__init__.py +0 -22
- synth_ai/task/rubrics.py +0 -219
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/README.md +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/README.md +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/branching.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/environment_routes.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/app.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/environment.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/react_agent.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/shared.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/envs/crafter/tools.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/hosted_app.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/inference/openai_client.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/main.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/registry.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/__init__.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/storage/volume.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/test_agents.py +0 -0
- /examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/utils.py +0 -0
- /examples/{rl/task_app → task_apps/math}/README.md +0 -0
- /examples/{rl/task_app → task_apps/math}/math_task_app.py +0 -0
- /examples/{rl → workflows/math_rl}/configs/eval_base_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/eval_rl_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_base_qwen17.toml +0 -0
- /examples/{rl → workflows/math_rl}/configs/rl_from_ft_qwen.toml +0 -0
- /examples/{rl → workflows/math_rl}/run_eval.py +0 -0
- /examples/{rl → workflows/math_rl}/run_rl_and_save.py +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/entry_points.txt +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.13.dev1.dist-info → synth_ai-0.2.13.dev2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from .enums import StatusCondition, PokemonType
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class PokemonData:
|
|
7
|
+
"""Complete Pokemon data structure for Emerald"""
|
|
8
|
+
species_id: int
|
|
9
|
+
species_name: str
|
|
10
|
+
current_hp: int
|
|
11
|
+
max_hp: int
|
|
12
|
+
level: int
|
|
13
|
+
status: StatusCondition
|
|
14
|
+
type1: PokemonType
|
|
15
|
+
type2: Optional[PokemonType]
|
|
16
|
+
moves: list[str] # Move names
|
|
17
|
+
move_pp: list[int] # PP for each move
|
|
18
|
+
trainer_id: int
|
|
19
|
+
nickname: Optional[str] = None
|
|
20
|
+
experience: Optional[int] = None
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def is_asleep(self) -> bool:
|
|
24
|
+
"""Check if the Pokémon is asleep"""
|
|
25
|
+
return self.status.is_asleep
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def status_name(self) -> str:
|
|
29
|
+
"""Return a human-readable status name"""
|
|
30
|
+
if self.is_asleep:
|
|
31
|
+
return "SLEEP"
|
|
32
|
+
elif self.status & StatusCondition.PARALYSIS:
|
|
33
|
+
return "PARALYSIS"
|
|
34
|
+
elif self.status & StatusCondition.FREEZE:
|
|
35
|
+
return "FREEZE"
|
|
36
|
+
elif self.status & StatusCondition.BURN:
|
|
37
|
+
return "BURN"
|
|
38
|
+
elif self.status & StatusCondition.POISON:
|
|
39
|
+
return "POISON"
|
|
40
|
+
else:
|
|
41
|
+
return "OK"
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for Pokemon Emerald memory reading
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List, Tuple, Optional
|
|
6
|
+
from .enums import MetatileBehavior, PokemonType, PokemonSpecies, Move
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def is_passable_behavior(behavior: MetatileBehavior) -> bool:
|
|
10
|
+
"""
|
|
11
|
+
Check if a metatile behavior allows the player to walk on it
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
behavior: The metatile behavior to check
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
True if the tile is passable, False otherwise
|
|
18
|
+
"""
|
|
19
|
+
# List of behaviors that are passable
|
|
20
|
+
passable_behaviors = {
|
|
21
|
+
MetatileBehavior.NORMAL,
|
|
22
|
+
MetatileBehavior.TALL_GRASS,
|
|
23
|
+
MetatileBehavior.LONG_GRASS,
|
|
24
|
+
MetatileBehavior.SHORT_GRASS,
|
|
25
|
+
MetatileBehavior.SAND,
|
|
26
|
+
MetatileBehavior.ASHGRASS,
|
|
27
|
+
MetatileBehavior.FOOTPRINTS,
|
|
28
|
+
MetatileBehavior.PUDDLE,
|
|
29
|
+
MetatileBehavior.SHALLOW_WATER,
|
|
30
|
+
MetatileBehavior.ICE,
|
|
31
|
+
MetatileBehavior.THIN_ICE,
|
|
32
|
+
MetatileBehavior.CRACKED_ICE,
|
|
33
|
+
MetatileBehavior.HOT_SPRINGS,
|
|
34
|
+
MetatileBehavior.MUDDY_SLOPE,
|
|
35
|
+
MetatileBehavior.BUMPY_SLOPE,
|
|
36
|
+
MetatileBehavior.CRACKED_FLOOR,
|
|
37
|
+
MetatileBehavior.VERTICAL_RAIL,
|
|
38
|
+
MetatileBehavior.HORIZONTAL_RAIL,
|
|
39
|
+
MetatileBehavior.ISOLATED_VERTICAL_RAIL,
|
|
40
|
+
MetatileBehavior.ISOLATED_HORIZONTAL_RAIL,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return behavior in passable_behaviors
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_encounter_behavior(behavior: MetatileBehavior) -> bool:
|
|
47
|
+
"""
|
|
48
|
+
Check if a metatile behavior can trigger wild Pokemon encounters
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
behavior: The metatile behavior to check
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if the tile can trigger encounters, False otherwise
|
|
55
|
+
"""
|
|
56
|
+
encounter_behaviors = {
|
|
57
|
+
MetatileBehavior.TALL_GRASS,
|
|
58
|
+
MetatileBehavior.LONG_GRASS,
|
|
59
|
+
MetatileBehavior.INDOOR_ENCOUNTER,
|
|
60
|
+
MetatileBehavior.CAVE,
|
|
61
|
+
MetatileBehavior.DEEP_WATER,
|
|
62
|
+
MetatileBehavior.OCEAN_WATER,
|
|
63
|
+
MetatileBehavior.SHALLOW_WATER,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return behavior in encounter_behaviors
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def is_surfable_behavior(behavior: MetatileBehavior) -> bool:
|
|
70
|
+
"""
|
|
71
|
+
Check if a metatile behavior allows surfing
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
behavior: The metatile behavior to check
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
True if the tile is surfable, False otherwise
|
|
78
|
+
"""
|
|
79
|
+
surfable_behaviors = {
|
|
80
|
+
MetatileBehavior.DEEP_WATER,
|
|
81
|
+
MetatileBehavior.OCEAN_WATER,
|
|
82
|
+
MetatileBehavior.SHALLOW_WATER,
|
|
83
|
+
MetatileBehavior.POND_WATER,
|
|
84
|
+
MetatileBehavior.INTERIOR_DEEP_WATER,
|
|
85
|
+
MetatileBehavior.SOOTOPOLIS_DEEP_WATER,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return behavior in surfable_behaviors
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def get_type_effectiveness(attacking_type: PokemonType, defending_type: PokemonType) -> float:
|
|
92
|
+
"""
|
|
93
|
+
Calculate type effectiveness between two Pokemon types
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
attacking_type: The type of the attacking move
|
|
97
|
+
defending_type: The type of the defending Pokemon
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Effectiveness multiplier (0.0, 0.25, 0.5, 1.0, 2.0, or 4.0)
|
|
101
|
+
"""
|
|
102
|
+
# Type effectiveness chart (simplified - only includes common types)
|
|
103
|
+
# This is a basic implementation - a full chart would be much larger
|
|
104
|
+
effectiveness_chart = {
|
|
105
|
+
PokemonType.NORMAL: {
|
|
106
|
+
PokemonType.ROCK: 0.5,
|
|
107
|
+
PokemonType.GHOST: 0.0,
|
|
108
|
+
PokemonType.STEEL: 0.5,
|
|
109
|
+
},
|
|
110
|
+
PokemonType.FIRE: {
|
|
111
|
+
PokemonType.FIRE: 0.5,
|
|
112
|
+
PokemonType.WATER: 0.5,
|
|
113
|
+
PokemonType.GRASS: 2.0,
|
|
114
|
+
PokemonType.ICE: 2.0,
|
|
115
|
+
PokemonType.BUG: 2.0,
|
|
116
|
+
PokemonType.ROCK: 0.5,
|
|
117
|
+
PokemonType.DRAGON: 0.5,
|
|
118
|
+
PokemonType.STEEL: 2.0,
|
|
119
|
+
},
|
|
120
|
+
PokemonType.WATER: {
|
|
121
|
+
PokemonType.FIRE: 2.0,
|
|
122
|
+
PokemonType.WATER: 0.5,
|
|
123
|
+
PokemonType.GRASS: 0.5,
|
|
124
|
+
PokemonType.GROUND: 2.0,
|
|
125
|
+
PokemonType.ROCK: 2.0,
|
|
126
|
+
PokemonType.DRAGON: 0.5,
|
|
127
|
+
},
|
|
128
|
+
PokemonType.GRASS: {
|
|
129
|
+
PokemonType.FIRE: 0.5,
|
|
130
|
+
PokemonType.WATER: 2.0,
|
|
131
|
+
PokemonType.GRASS: 0.5,
|
|
132
|
+
PokemonType.POISON: 0.5,
|
|
133
|
+
PokemonType.GROUND: 2.0,
|
|
134
|
+
PokemonType.FLYING: 0.5,
|
|
135
|
+
PokemonType.BUG: 0.5,
|
|
136
|
+
PokemonType.ROCK: 2.0,
|
|
137
|
+
PokemonType.DRAGON: 0.5,
|
|
138
|
+
PokemonType.STEEL: 0.5,
|
|
139
|
+
},
|
|
140
|
+
PokemonType.ELECTRIC: {
|
|
141
|
+
PokemonType.WATER: 2.0,
|
|
142
|
+
PokemonType.GRASS: 0.5,
|
|
143
|
+
PokemonType.ELECTRIC: 0.5,
|
|
144
|
+
PokemonType.GROUND: 0.0,
|
|
145
|
+
PokemonType.FLYING: 2.0,
|
|
146
|
+
PokemonType.DRAGON: 0.5,
|
|
147
|
+
},
|
|
148
|
+
PokemonType.ICE: {
|
|
149
|
+
PokemonType.FIRE: 0.5,
|
|
150
|
+
PokemonType.WATER: 0.5,
|
|
151
|
+
PokemonType.GRASS: 2.0,
|
|
152
|
+
PokemonType.ICE: 0.5,
|
|
153
|
+
PokemonType.GROUND: 2.0,
|
|
154
|
+
PokemonType.FLYING: 2.0,
|
|
155
|
+
PokemonType.DRAGON: 2.0,
|
|
156
|
+
PokemonType.STEEL: 0.5,
|
|
157
|
+
},
|
|
158
|
+
PokemonType.FIGHTING: {
|
|
159
|
+
PokemonType.NORMAL: 2.0,
|
|
160
|
+
PokemonType.ICE: 2.0,
|
|
161
|
+
PokemonType.POISON: 0.5,
|
|
162
|
+
PokemonType.FLYING: 0.5,
|
|
163
|
+
PokemonType.PSYCHIC: 0.5,
|
|
164
|
+
PokemonType.BUG: 0.5,
|
|
165
|
+
PokemonType.ROCK: 2.0,
|
|
166
|
+
PokemonType.GHOST: 0.0,
|
|
167
|
+
PokemonType.STEEL: 2.0,
|
|
168
|
+
PokemonType.DARK: 2.0,
|
|
169
|
+
},
|
|
170
|
+
PokemonType.POISON: {
|
|
171
|
+
PokemonType.GRASS: 2.0,
|
|
172
|
+
PokemonType.POISON: 0.5,
|
|
173
|
+
PokemonType.GROUND: 0.5,
|
|
174
|
+
PokemonType.ROCK: 0.5,
|
|
175
|
+
PokemonType.GHOST: 0.5,
|
|
176
|
+
PokemonType.STEEL: 0.0,
|
|
177
|
+
},
|
|
178
|
+
PokemonType.GROUND: {
|
|
179
|
+
PokemonType.FIRE: 2.0,
|
|
180
|
+
PokemonType.GRASS: 0.5,
|
|
181
|
+
PokemonType.ELECTRIC: 2.0,
|
|
182
|
+
PokemonType.POISON: 2.0,
|
|
183
|
+
PokemonType.FLYING: 0.0,
|
|
184
|
+
PokemonType.BUG: 0.5,
|
|
185
|
+
PokemonType.ROCK: 2.0,
|
|
186
|
+
PokemonType.STEEL: 2.0,
|
|
187
|
+
},
|
|
188
|
+
PokemonType.FLYING: {
|
|
189
|
+
PokemonType.GRASS: 2.0,
|
|
190
|
+
PokemonType.ELECTRIC: 0.5,
|
|
191
|
+
PokemonType.FIGHTING: 2.0,
|
|
192
|
+
PokemonType.BUG: 2.0,
|
|
193
|
+
PokemonType.ROCK: 0.5,
|
|
194
|
+
PokemonType.STEEL: 0.5,
|
|
195
|
+
},
|
|
196
|
+
PokemonType.PSYCHIC: {
|
|
197
|
+
PokemonType.FIGHTING: 2.0,
|
|
198
|
+
PokemonType.POISON: 2.0,
|
|
199
|
+
PokemonType.PSYCHIC: 0.5,
|
|
200
|
+
PokemonType.DARK: 0.0,
|
|
201
|
+
PokemonType.STEEL: 0.5,
|
|
202
|
+
},
|
|
203
|
+
PokemonType.BUG: {
|
|
204
|
+
PokemonType.FIRE: 0.5,
|
|
205
|
+
PokemonType.GRASS: 2.0,
|
|
206
|
+
PokemonType.FIGHTING: 0.5,
|
|
207
|
+
PokemonType.POISON: 0.5,
|
|
208
|
+
PokemonType.FLYING: 0.5,
|
|
209
|
+
PokemonType.PSYCHIC: 2.0,
|
|
210
|
+
PokemonType.GHOST: 0.5,
|
|
211
|
+
PokemonType.DARK: 2.0,
|
|
212
|
+
PokemonType.STEEL: 0.5,
|
|
213
|
+
},
|
|
214
|
+
PokemonType.ROCK: {
|
|
215
|
+
PokemonType.FIRE: 2.0,
|
|
216
|
+
PokemonType.ICE: 2.0,
|
|
217
|
+
PokemonType.FIGHTING: 0.5,
|
|
218
|
+
PokemonType.GROUND: 0.5,
|
|
219
|
+
PokemonType.FLYING: 2.0,
|
|
220
|
+
PokemonType.BUG: 2.0,
|
|
221
|
+
PokemonType.STEEL: 0.5,
|
|
222
|
+
},
|
|
223
|
+
PokemonType.GHOST: {
|
|
224
|
+
PokemonType.NORMAL: 0.0,
|
|
225
|
+
PokemonType.PSYCHIC: 2.0,
|
|
226
|
+
PokemonType.GHOST: 2.0,
|
|
227
|
+
PokemonType.DARK: 0.5,
|
|
228
|
+
},
|
|
229
|
+
PokemonType.DRAGON: {
|
|
230
|
+
PokemonType.DRAGON: 2.0,
|
|
231
|
+
PokemonType.STEEL: 0.5,
|
|
232
|
+
},
|
|
233
|
+
PokemonType.DARK: {
|
|
234
|
+
PokemonType.FIGHTING: 0.5,
|
|
235
|
+
PokemonType.PSYCHIC: 2.0,
|
|
236
|
+
PokemonType.GHOST: 2.0,
|
|
237
|
+
PokemonType.DARK: 0.5,
|
|
238
|
+
PokemonType.STEEL: 0.5,
|
|
239
|
+
},
|
|
240
|
+
PokemonType.STEEL: {
|
|
241
|
+
PokemonType.FIRE: 0.5,
|
|
242
|
+
PokemonType.WATER: 0.5,
|
|
243
|
+
PokemonType.ELECTRIC: 0.5,
|
|
244
|
+
PokemonType.ICE: 2.0,
|
|
245
|
+
PokemonType.ROCK: 2.0,
|
|
246
|
+
PokemonType.STEEL: 0.5,
|
|
247
|
+
},
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if attacking_type in effectiveness_chart and defending_type in effectiveness_chart[attacking_type]:
|
|
251
|
+
return effectiveness_chart[attacking_type][defending_type]
|
|
252
|
+
|
|
253
|
+
return 1.0 # Normal effectiveness
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def format_time(hours: int, minutes: int, seconds: int) -> str:
|
|
257
|
+
"""
|
|
258
|
+
Format game time in a human-readable format
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
hours: Number of hours
|
|
262
|
+
minutes: Number of minutes
|
|
263
|
+
seconds: Number of seconds
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Formatted time string
|
|
267
|
+
"""
|
|
268
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def format_money(amount: int) -> str:
|
|
272
|
+
"""
|
|
273
|
+
Format money amount with commas
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
amount: Money amount in cents
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Formatted money string
|
|
280
|
+
"""
|
|
281
|
+
return f"${amount:,}"
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def get_pokemon_type_names(type1: PokemonType, type2: Optional[PokemonType] = None) -> str:
|
|
285
|
+
"""
|
|
286
|
+
Get formatted type names for a Pokemon
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
type1: Primary type
|
|
290
|
+
type2: Secondary type (optional)
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Formatted type string
|
|
294
|
+
"""
|
|
295
|
+
if type2 is None or type1 == type2:
|
|
296
|
+
return type1.name.replace("_", " ")
|
|
297
|
+
else:
|
|
298
|
+
return f"{type1.name.replace('_', ' ')} / {type2.name.replace('_', ' ')}"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pokeagent-speedrun"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "AI agent that plays Pokémon Emerald using vision-language models"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10,<3.12"
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
"flask==3.0.2",
|
|
10
|
+
"flask-socketio==5.3.6",
|
|
11
|
+
"python-socketio==5.11.1",
|
|
12
|
+
"python-engineio==4.9.0",
|
|
13
|
+
"numpy==1.26.4",
|
|
14
|
+
"opencv-python==4.9.0.80",
|
|
15
|
+
"pillow==10.2.0",
|
|
16
|
+
"pygame==2.6.1",
|
|
17
|
+
"requests>=2.32.2",
|
|
18
|
+
"python-dotenv==1.0.1",
|
|
19
|
+
"eventlet==0.35.2",
|
|
20
|
+
"gevent==23.9.1",
|
|
21
|
+
"gevent-websocket==0.10.1",
|
|
22
|
+
"fastapi>=0.104.0",
|
|
23
|
+
"uvicorn[standard]>=0.24.0",
|
|
24
|
+
"pydantic>=2.0.0",
|
|
25
|
+
"mgba==0.10.2",
|
|
26
|
+
"pyyaml>=5.1",
|
|
27
|
+
"xxhash",
|
|
28
|
+
"more-itertools",
|
|
29
|
+
"pytz>=2020.1",
|
|
30
|
+
"six>=1.14.0",
|
|
31
|
+
"colorama",
|
|
32
|
+
"grpcio>=1.48.2",
|
|
33
|
+
"protobuf!=4.24.0,<5.0.0,>=3.19.6",
|
|
34
|
+
"openai==1.90.0",
|
|
35
|
+
"google-generativeai>=0.3.0",
|
|
36
|
+
"torch>=2.0.0",
|
|
37
|
+
"transformers>=4.36.0",
|
|
38
|
+
"bitsandbytes>=0.41.0",
|
|
39
|
+
"accelerate>=0.25.0",
|
|
40
|
+
"sentencepiece>=0.1.99",
|
|
41
|
+
"cffi>=1.6",
|
|
42
|
+
"pytesseract>=0.3.10",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
dev = [
|
|
47
|
+
"pytest>=7.0.0",
|
|
48
|
+
"pytest-cov>=4.0.0",
|
|
49
|
+
"ruff>=0.1.0",
|
|
50
|
+
"mypy>=1.0.0",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[build-system]
|
|
54
|
+
requires = ["hatchling"]
|
|
55
|
+
build-backend = "hatchling.build"
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.wheel]
|
|
58
|
+
packages = ["agent", "pokemon_env", "utils", "server"]
|
|
59
|
+
|
|
60
|
+
[tool.uv]
|
|
61
|
+
dev-dependencies = [
|
|
62
|
+
"pytest>=7.0.0",
|
|
63
|
+
"pytest-cov>=4.0.0",
|
|
64
|
+
"ruff>=0.1.0",
|
|
65
|
+
"mypy>=1.0.0",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[tool.ruff]
|
|
69
|
+
line-length = 120
|
|
70
|
+
target-version = "py310"
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint]
|
|
73
|
+
select = [
|
|
74
|
+
"E", # pycodestyle errors
|
|
75
|
+
"W", # pycodestyle warnings
|
|
76
|
+
"F", # pyflakes
|
|
77
|
+
"I", # isort
|
|
78
|
+
"B", # flake8-bugbear
|
|
79
|
+
"C4", # flake8-comprehensions
|
|
80
|
+
"UP", # pyupgrade
|
|
81
|
+
]
|
|
82
|
+
ignore = [
|
|
83
|
+
"E501", # line too long
|
|
84
|
+
"B008", # do not perform function calls in argument defaults
|
|
85
|
+
"W191", # indentation contains tabs
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.ruff.lint.isort]
|
|
89
|
+
known-first-party = ["agent", "pokemon_env", "utils", "server"]
|
|
90
|
+
|
|
91
|
+
[tool.mypy]
|
|
92
|
+
python_version = "3.10"
|
|
93
|
+
warn_return_any = true
|
|
94
|
+
warn_unused_configs = true
|
|
95
|
+
disallow_untyped_defs = false
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Main entry point for the Pokemon Agent.
|
|
4
|
+
This is a streamlined version that focuses on multiprocess mode only.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
import argparse
|
|
11
|
+
import subprocess
|
|
12
|
+
import signal
|
|
13
|
+
|
|
14
|
+
# Add parent directory to path for imports
|
|
15
|
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
16
|
+
from server.client import run_multiprocess_client
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def start_server(args):
|
|
20
|
+
"""Start the server process with appropriate arguments"""
|
|
21
|
+
# Use the same Python executable that's running this script
|
|
22
|
+
python_exe = sys.executable
|
|
23
|
+
server_cmd = [python_exe, "-m", "server.app", "--port", str(args.port)]
|
|
24
|
+
|
|
25
|
+
# Pass through server-relevant arguments
|
|
26
|
+
if args.record:
|
|
27
|
+
server_cmd.append("--record")
|
|
28
|
+
|
|
29
|
+
if args.load_checkpoint:
|
|
30
|
+
# Auto-load checkpoint.state when --load-checkpoint is used
|
|
31
|
+
checkpoint_state = ".pokeagent_cache/checkpoint.state"
|
|
32
|
+
if os.path.exists(checkpoint_state):
|
|
33
|
+
server_cmd.extend(["--load-state", checkpoint_state])
|
|
34
|
+
# Set environment variable to enable LLM checkpoint loading
|
|
35
|
+
os.environ["LOAD_CHECKPOINT_MODE"] = "true"
|
|
36
|
+
print(f"🔄 Server will load checkpoint: {checkpoint_state}")
|
|
37
|
+
print(f"🔄 LLM metrics will be restored from .pokeagent_cache/checkpoint_llm.txt")
|
|
38
|
+
else:
|
|
39
|
+
print(f"⚠️ Checkpoint file not found: {checkpoint_state}")
|
|
40
|
+
elif args.load_state:
|
|
41
|
+
server_cmd.extend(["--load-state", args.load_state])
|
|
42
|
+
|
|
43
|
+
# Don't pass --manual to server - server should always run in server mode
|
|
44
|
+
# The --manual flag only affects client behavior
|
|
45
|
+
|
|
46
|
+
if args.no_ocr:
|
|
47
|
+
server_cmd.append("--no-ocr")
|
|
48
|
+
|
|
49
|
+
# Server always runs headless - display handled by client
|
|
50
|
+
|
|
51
|
+
# Start server as subprocess
|
|
52
|
+
try:
|
|
53
|
+
print(f"📋 Server command: {' '.join(server_cmd)}")
|
|
54
|
+
server_process = subprocess.Popen(
|
|
55
|
+
server_cmd,
|
|
56
|
+
universal_newlines=True,
|
|
57
|
+
bufsize=1
|
|
58
|
+
)
|
|
59
|
+
print(f"✅ Server started with PID {server_process.pid}")
|
|
60
|
+
print("⏳ Waiting 3 seconds for server to initialize...")
|
|
61
|
+
time.sleep(3)
|
|
62
|
+
|
|
63
|
+
return server_process
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
print(f"❌ Failed to start server: {e}")
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def start_frame_server():
|
|
71
|
+
"""Start the lightweight frame server for stream.html visualization"""
|
|
72
|
+
try:
|
|
73
|
+
frame_cmd = ["python", "-m", "server.frame_server"]
|
|
74
|
+
frame_process = subprocess.Popen(
|
|
75
|
+
frame_cmd,
|
|
76
|
+
stdout=subprocess.PIPE,
|
|
77
|
+
stderr=subprocess.PIPE
|
|
78
|
+
)
|
|
79
|
+
print(f"🖼️ Frame server started with PID {frame_process.pid}")
|
|
80
|
+
return frame_process
|
|
81
|
+
except Exception as e:
|
|
82
|
+
print(f"⚠️ Could not start frame server: {e}")
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def main():
|
|
87
|
+
"""Main entry point for the Pokemon Agent"""
|
|
88
|
+
parser = argparse.ArgumentParser(description="Pokemon Emerald AI Agent")
|
|
89
|
+
|
|
90
|
+
# Core arguments
|
|
91
|
+
parser.add_argument("--rom", type=str, default="Emerald-GBAdvance/rom.gba",
|
|
92
|
+
help="Path to ROM file")
|
|
93
|
+
parser.add_argument("--port", type=int, default=8000,
|
|
94
|
+
help="Port for web interface")
|
|
95
|
+
|
|
96
|
+
# State loading
|
|
97
|
+
parser.add_argument("--load-state", type=str,
|
|
98
|
+
help="Load a saved state file on startup")
|
|
99
|
+
parser.add_argument("--load-checkpoint", action="store_true",
|
|
100
|
+
help="Load from checkpoint files")
|
|
101
|
+
|
|
102
|
+
# Agent configuration
|
|
103
|
+
parser.add_argument("--backend", type=str, default="gemini",
|
|
104
|
+
help="VLM backend (openai, gemini, local, openrouter)")
|
|
105
|
+
parser.add_argument("--model-name", type=str, default="gemini-2.5-flash",
|
|
106
|
+
help="Model name to use")
|
|
107
|
+
parser.add_argument("--simple", action="store_true",
|
|
108
|
+
help="Simple mode: direct frame->action without 4-module architecture")
|
|
109
|
+
|
|
110
|
+
# Operation modes
|
|
111
|
+
parser.add_argument("--headless", action="store_true",
|
|
112
|
+
help="Run without pygame display (headless)")
|
|
113
|
+
parser.add_argument("--agent-auto", action="store_true",
|
|
114
|
+
help="Agent acts automatically")
|
|
115
|
+
parser.add_argument("--manual", action="store_true",
|
|
116
|
+
help="Start in manual mode instead of agent mode")
|
|
117
|
+
|
|
118
|
+
# Features
|
|
119
|
+
parser.add_argument("--record", action="store_true",
|
|
120
|
+
help="Record video of the gameplay")
|
|
121
|
+
parser.add_argument("--no-ocr", action="store_true",
|
|
122
|
+
help="Disable OCR dialogue detection")
|
|
123
|
+
|
|
124
|
+
args = parser.parse_args()
|
|
125
|
+
|
|
126
|
+
print("=" * 60)
|
|
127
|
+
print("🎮 Pokemon Emerald AI Agent")
|
|
128
|
+
print("=" * 60)
|
|
129
|
+
|
|
130
|
+
server_process = None
|
|
131
|
+
frame_server_process = None
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
# Auto-start server if requested
|
|
135
|
+
if args.agent_auto or args.manual:
|
|
136
|
+
print("\n📡 Starting server process...")
|
|
137
|
+
server_process = start_server(args)
|
|
138
|
+
|
|
139
|
+
if not server_process:
|
|
140
|
+
print("❌ Failed to start server, exiting...")
|
|
141
|
+
return 1
|
|
142
|
+
|
|
143
|
+
# Also start frame server for web visualization
|
|
144
|
+
frame_server_process = start_frame_server()
|
|
145
|
+
else:
|
|
146
|
+
print("\n📋 Manual server mode - start server separately with:")
|
|
147
|
+
print(" python -m server.app --port", args.port)
|
|
148
|
+
if args.load_state:
|
|
149
|
+
print(f" (Add --load-state {args.load_state} to server command)")
|
|
150
|
+
print("\n⏳ Waiting 3 seconds for manual server startup...")
|
|
151
|
+
time.sleep(3)
|
|
152
|
+
|
|
153
|
+
# Display configuration
|
|
154
|
+
print("\n🤖 Agent Configuration:")
|
|
155
|
+
print(f" Backend: {args.backend}")
|
|
156
|
+
print(f" Model: {args.model_name}")
|
|
157
|
+
if args.simple:
|
|
158
|
+
print(" Mode: Simple (direct frame->action)")
|
|
159
|
+
else:
|
|
160
|
+
print(" Mode: Four-module architecture")
|
|
161
|
+
if args.no_ocr:
|
|
162
|
+
print(" OCR: Disabled")
|
|
163
|
+
if args.record:
|
|
164
|
+
print(" Recording: Enabled")
|
|
165
|
+
|
|
166
|
+
print(f"\n🌐 Web Interface: http://127.0.0.1:{args.port}")
|
|
167
|
+
print(f"🎥 Stream View: http://127.0.0.1:{args.port}/stream.html")
|
|
168
|
+
|
|
169
|
+
print("\n🚀 Starting client...")
|
|
170
|
+
print("-" * 60)
|
|
171
|
+
|
|
172
|
+
# Run the client
|
|
173
|
+
success = run_multiprocess_client(server_port=args.port, args=args)
|
|
174
|
+
|
|
175
|
+
return 0 if success else 1
|
|
176
|
+
|
|
177
|
+
except KeyboardInterrupt:
|
|
178
|
+
print("\n\n🛑 Shutdown requested by user")
|
|
179
|
+
return 0
|
|
180
|
+
|
|
181
|
+
finally:
|
|
182
|
+
# Clean up server processes
|
|
183
|
+
if server_process:
|
|
184
|
+
print("\n📡 Stopping server process...")
|
|
185
|
+
server_process.terminate()
|
|
186
|
+
try:
|
|
187
|
+
server_process.wait(timeout=5)
|
|
188
|
+
except subprocess.TimeoutExpired:
|
|
189
|
+
print(" Force killing server...")
|
|
190
|
+
server_process.kill()
|
|
191
|
+
|
|
192
|
+
if frame_server_process:
|
|
193
|
+
print("🖼️ Stopping frame server...")
|
|
194
|
+
frame_server_process.terminate()
|
|
195
|
+
try:
|
|
196
|
+
frame_server_process.wait(timeout=2)
|
|
197
|
+
except:
|
|
198
|
+
frame_server_process.kill()
|
|
199
|
+
|
|
200
|
+
print("👋 Goodbye!")
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
if __name__ == "__main__":
|
|
204
|
+
sys.exit(main())
|
|
File without changes
|