synth-ai 0.2.12__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_outcome.toml +74 -0
- examples/multi_step/configs/crafter_rl_stepwise_hosted_judge.toml +186 -0
- examples/multi_step/configs/crafter_rl_stepwise_shaped.toml +83 -0
- examples/multi_step/configs/crafter_rl_stepwise_simple.toml +78 -0
- examples/multi_step/crafter_rl_lora.md +51 -10
- examples/multi_step/sse_metrics_streaming_notes.md +357 -0
- examples/multi_step/task_app_config_notes.md +7 -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 +21 -46
- 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/inference/openai_client.py +109 -45
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/policy_routes.py +67 -49
- examples/{warming_up_to_rl → task_apps/crafter}/task_app/synth_envs_hosted/rollout.py +242 -193
- 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/warming_up_to_rl/configs/eval_stepwise_complex.toml +4 -2
- examples/warming_up_to_rl/configs/eval_stepwise_simple.toml +4 -2
- examples/warming_up_to_rl/run_eval.py +127 -18
- 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 +41 -1
- synth_ai/api/train/builders.py +73 -29
- synth_ai/api/train/cli.py +12 -6
- synth_ai/api/train/configs/__init__.py +44 -0
- synth_ai/api/train/configs/rl.py +134 -0
- synth_ai/api/train/configs/sft.py +95 -0
- synth_ai/api/train/configs/shared.py +24 -0
- 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 +49 -43
- synth_ai/cli/legacy_root_backup.py +1 -1
- synth_ai/cli/rl_demo.py +86 -106
- synth_ai/cli/root.py +0 -97
- synth_ai/cli/task_apps.py +1710 -186
- synth_ai/demos/core/cli.py +121 -159
- synth_ai/demos/demo_task_apps/crafter/grpo_crafter_task_app.py +28 -16
- synth_ai/environments/examples/crafter_classic/environment.py +16 -0
- 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/__init__.py +15 -0
- synth_ai/evals/client.py +82 -0
- synth_ai/evals/types.py +42 -0
- synth_ai/jobs/client.py +16 -4
- synth_ai/judge_schemas.py +127 -0
- 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/task/rubrics/strict.py +149 -0
- 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 +130 -0
- synth_ai/tracing_v3/trace_utils.py +317 -0
- synth_ai/tracing_v3/turso/native_manager.py +3 -3
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/METADATA +4 -1
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/RECORD +228 -89
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/entry_points.txt +0 -1
- 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/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.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/WHEEL +0 -0
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/licenses/LICENSE +0 -0
- {synth_ai-0.2.12.dist-info → synth_ai-0.2.13.dev2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pytest
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import numpy as np
|
|
5
|
+
from pprint import pprint
|
|
6
|
+
|
|
7
|
+
from pokemon_env.emulator import EmeraldEmulator
|
|
8
|
+
from pokemon_env.enums import MetatileBehavior
|
|
9
|
+
|
|
10
|
+
def format_map_data(map_data, title="Map Data"):
|
|
11
|
+
"""Format the map data into a string using the agent's format"""
|
|
12
|
+
print(f"Map data: {map_data}")
|
|
13
|
+
lines = []
|
|
14
|
+
lines.append(f"=== {title} ===")
|
|
15
|
+
lines.append("Format: (MetatileID, Behavior, X, Y)")
|
|
16
|
+
lines.append(f"Map dimensions: {len(map_data)}x{len(map_data[0])}")
|
|
17
|
+
lines.append("")
|
|
18
|
+
lines.append("--- TRAVERSABILITY MAP ---")
|
|
19
|
+
|
|
20
|
+
# Column headers
|
|
21
|
+
header = " "
|
|
22
|
+
for i in range(len(map_data[0])):
|
|
23
|
+
header += f"{i:2} "
|
|
24
|
+
lines.append(header)
|
|
25
|
+
lines.append(" " + "--" * len(map_data[0]))
|
|
26
|
+
|
|
27
|
+
# Find player position (center of the grid)
|
|
28
|
+
center_y = len(map_data) // 2
|
|
29
|
+
center_x = len(map_data[0]) // 2
|
|
30
|
+
|
|
31
|
+
# Format each row
|
|
32
|
+
for y in range(len(map_data)):
|
|
33
|
+
row = f"{y:2}: "
|
|
34
|
+
for x in range(len(map_data[y])):
|
|
35
|
+
if y == center_y and x == center_x:
|
|
36
|
+
row += "P" # Player position
|
|
37
|
+
else:
|
|
38
|
+
tile = map_data[y][x]
|
|
39
|
+
behavior = tile[1]
|
|
40
|
+
# Convert behavior to map symbol using same logic as get_comprehensive_state
|
|
41
|
+
tile_id, behavior, collision, elevation = tile
|
|
42
|
+
|
|
43
|
+
# Handle both enum objects and raw integers from server API
|
|
44
|
+
if hasattr(behavior, 'name'):
|
|
45
|
+
behavior_name = behavior.name
|
|
46
|
+
elif isinstance(behavior, int):
|
|
47
|
+
try:
|
|
48
|
+
behavior_enum = MetatileBehavior(behavior)
|
|
49
|
+
behavior_name = behavior_enum.name
|
|
50
|
+
except ValueError:
|
|
51
|
+
behavior_name = "UNKNOWN"
|
|
52
|
+
else:
|
|
53
|
+
behavior_name = "UNKNOWN"
|
|
54
|
+
|
|
55
|
+
if behavior_name == "NORMAL":
|
|
56
|
+
# For normal tiles, use collision data to determine if passable
|
|
57
|
+
row += "." if collision == 0 else "#"
|
|
58
|
+
elif "TALL_GRASS" in behavior_name:
|
|
59
|
+
row += "~" # Tall grass
|
|
60
|
+
elif "WATER" in behavior_name:
|
|
61
|
+
row += "W" # Water
|
|
62
|
+
elif "JUMP" in behavior_name:
|
|
63
|
+
# Show jump direction
|
|
64
|
+
if "JUMP_EAST" in behavior_name:
|
|
65
|
+
row += "→"
|
|
66
|
+
elif "JUMP_WEST" in behavior_name:
|
|
67
|
+
row += "←"
|
|
68
|
+
elif "JUMP_NORTH" in behavior_name:
|
|
69
|
+
row += "↑"
|
|
70
|
+
elif "JUMP_SOUTH" in behavior_name:
|
|
71
|
+
row += "↓"
|
|
72
|
+
elif "JUMP_NORTHEAST" in behavior_name:
|
|
73
|
+
row += "↗"
|
|
74
|
+
elif "JUMP_NORTHWEST" in behavior_name:
|
|
75
|
+
row += "↖"
|
|
76
|
+
elif "JUMP_SOUTHEAST" in behavior_name:
|
|
77
|
+
row += "↘"
|
|
78
|
+
elif "JUMP_SOUTHWEST" in behavior_name:
|
|
79
|
+
row += "↙"
|
|
80
|
+
else:
|
|
81
|
+
row += "J"
|
|
82
|
+
elif "DOOR" in behavior_name:
|
|
83
|
+
row += "D" # Door
|
|
84
|
+
else:
|
|
85
|
+
# For other behaviors, show abbreviated name
|
|
86
|
+
short_name = behavior_name.replace("_", "")[:4]
|
|
87
|
+
row += f"{short_name[:2]}"
|
|
88
|
+
row += " " # Add space between tiles
|
|
89
|
+
lines.append(row.rstrip())
|
|
90
|
+
|
|
91
|
+
return "\n".join(lines)
|
|
92
|
+
|
|
93
|
+
def print_map_data(map_data, title="Map Data"):
|
|
94
|
+
"""Pretty print the map data and return the formatted string"""
|
|
95
|
+
formatted_map = format_map_data(map_data, title)
|
|
96
|
+
print("\n" + formatted_map)
|
|
97
|
+
print("\nLegend:")
|
|
98
|
+
print(" P = Player position")
|
|
99
|
+
print(" . = Normal walkable path")
|
|
100
|
+
print(" # = Blocked/wall")
|
|
101
|
+
print(" D = Door (can be entered)")
|
|
102
|
+
print(" ~ = Tall grass (wild Pokemon encounters)")
|
|
103
|
+
print(" W = Water (requires Surf)")
|
|
104
|
+
print(" IM = Impassable terrain")
|
|
105
|
+
print(" SE = Sealed area (cannot enter)")
|
|
106
|
+
print(" EA = East arrow (direction indicator)")
|
|
107
|
+
print(" SO = Sound mat (makes sound when stepped on)")
|
|
108
|
+
print(" TE = Television")
|
|
109
|
+
print(" →←↑↓↗↖↘↙ = Jump ledge directions (can jump in that direction)")
|
|
110
|
+
return formatted_map
|
|
111
|
+
|
|
112
|
+
# Get the absolute path to the test states directory
|
|
113
|
+
TEST_STATES_DIR = os.path.join(os.path.dirname(__file__), "states")
|
|
114
|
+
|
|
115
|
+
@pytest.fixture
|
|
116
|
+
def emulator():
|
|
117
|
+
"""Create and initialize an emulator instance"""
|
|
118
|
+
# Get path to ROM file
|
|
119
|
+
project_root = Path(__file__).parent.parent
|
|
120
|
+
rom_path = str(project_root / "Emerald-GBAdvance" / "rom.gba")
|
|
121
|
+
|
|
122
|
+
# Initialize emulator
|
|
123
|
+
emu = EmeraldEmulator(rom_path, headless=True, sound=False)
|
|
124
|
+
emu.initialize()
|
|
125
|
+
|
|
126
|
+
yield emu
|
|
127
|
+
|
|
128
|
+
# Cleanup
|
|
129
|
+
emu.stop()
|
|
130
|
+
|
|
131
|
+
def test_house_state_map_reading(emulator):
|
|
132
|
+
"""Test map reading functionality in the house state"""
|
|
133
|
+
# Load the house state
|
|
134
|
+
state_path = os.path.join(TEST_STATES_DIR, "house.state")
|
|
135
|
+
emulator.load_state(state_path)
|
|
136
|
+
|
|
137
|
+
# Get map data around player using the same function as the agent
|
|
138
|
+
map_data = emulator.memory_reader.read_map_around_player(radius=7)
|
|
139
|
+
|
|
140
|
+
# Format the map data
|
|
141
|
+
formatted_map = print_map_data(map_data, "House State Map")
|
|
142
|
+
|
|
143
|
+
# Load ground truth
|
|
144
|
+
truth_path = os.path.join(TEST_STATES_DIR, "house_map_truth.txt")
|
|
145
|
+
with open(truth_path, 'r') as f:
|
|
146
|
+
expected_map = f.read().strip()
|
|
147
|
+
|
|
148
|
+
# Compare with ground truth
|
|
149
|
+
assert formatted_map == expected_map, "Map output does not match ground truth"
|
|
150
|
+
|
|
151
|
+
# Basic structure tests
|
|
152
|
+
assert map_data is not None, "Map data should not be None"
|
|
153
|
+
assert isinstance(map_data, list), "Map data should be a list"
|
|
154
|
+
assert len(map_data) > 0, "Map data should not be empty"
|
|
155
|
+
assert isinstance(map_data[0], list), "Map data should be a 2D list"
|
|
156
|
+
|
|
157
|
+
# Test map dimensions
|
|
158
|
+
height = len(map_data)
|
|
159
|
+
width = len(map_data[0])
|
|
160
|
+
assert height == 15, f"Map height should be 15 (2*radius + 1), got {height}"
|
|
161
|
+
assert width == 15, f"Map width should be 15 (2*radius + 1), got {width}"
|
|
162
|
+
|
|
163
|
+
# Test tile data structure
|
|
164
|
+
center_tile = map_data[7][7] # Center tile where player is
|
|
165
|
+
assert isinstance(center_tile, tuple), "Tile data should be a tuple"
|
|
166
|
+
assert len(center_tile) == 4, "Tile data should contain metatile ID, behavior, x, and y coordinates"
|
|
167
|
+
|
|
168
|
+
# Test that we're indoors (should have indoor tiles)
|
|
169
|
+
center_behavior = center_tile[1]
|
|
170
|
+
assert isinstance(center_behavior, MetatileBehavior), "Tile behavior should be MetatileBehavior enum"
|
|
171
|
+
assert center_behavior in [MetatileBehavior.INDOOR_ENCOUNTER, MetatileBehavior.NORMAL], \
|
|
172
|
+
"Center tile should be an indoor tile"
|
|
173
|
+
|
|
174
|
+
def test_truck_state_map_reading(emulator):
|
|
175
|
+
"""Test map reading functionality in the truck state (game start)"""
|
|
176
|
+
# Load the truck state
|
|
177
|
+
state_path = os.path.join(TEST_STATES_DIR, "truck.state")
|
|
178
|
+
emulator.load_state(state_path)
|
|
179
|
+
|
|
180
|
+
# Get map data around player using the same function as the agent
|
|
181
|
+
map_data = emulator.memory_reader.read_map_around_player(radius=7)
|
|
182
|
+
|
|
183
|
+
# Format the map data
|
|
184
|
+
formatted_map = print_map_data(map_data, "Truck State Map")
|
|
185
|
+
|
|
186
|
+
# Load ground truth
|
|
187
|
+
truth_path = os.path.join(TEST_STATES_DIR, "truck_map_truth.txt")
|
|
188
|
+
with open(truth_path, 'r') as f:
|
|
189
|
+
expected_map = f.read().strip()
|
|
190
|
+
|
|
191
|
+
# Compare with ground truth
|
|
192
|
+
assert formatted_map == expected_map, "Map output does not match ground truth"
|
|
193
|
+
|
|
194
|
+
# Basic structure tests
|
|
195
|
+
assert map_data is not None, "Map data should not be None"
|
|
196
|
+
assert isinstance(map_data, list), "Map data should be a list"
|
|
197
|
+
assert len(map_data) > 0, "Map data should not be empty"
|
|
198
|
+
assert isinstance(map_data[0], list), "Map data should be a 2D list"
|
|
199
|
+
|
|
200
|
+
# Test map dimensions
|
|
201
|
+
height = len(map_data)
|
|
202
|
+
width = len(map_data[0])
|
|
203
|
+
assert height == 15, f"Map height should be 15 (2*radius + 1), got {height}"
|
|
204
|
+
assert width == 15, f"Map width should be 15 (2*radius + 1), got {width}"
|
|
205
|
+
|
|
206
|
+
# Test tile data structure
|
|
207
|
+
center_tile = map_data[7][7] # Center tile where player is
|
|
208
|
+
assert isinstance(center_tile, tuple), "Tile data should be a tuple"
|
|
209
|
+
assert len(center_tile) == 4, "Tile data should contain metatile ID, behavior, x, and y coordinates"
|
|
210
|
+
|
|
211
|
+
# Test that we're in the truck (should have indoor/special tiles)
|
|
212
|
+
center_behavior = center_tile[1]
|
|
213
|
+
assert isinstance(center_behavior, MetatileBehavior), "Tile behavior should be MetatileBehavior enum"
|
|
214
|
+
assert center_behavior in [MetatileBehavior.INDOOR_ENCOUNTER, MetatileBehavior.NORMAL], \
|
|
215
|
+
"Center tile should be an indoor tile"
|
|
216
|
+
|
|
217
|
+
def test_door_behavior(emulator):
|
|
218
|
+
"""Test that doors are properly identified and not marked as blocked"""
|
|
219
|
+
# Load the house state
|
|
220
|
+
state_path = os.path.join(TEST_STATES_DIR, "house.state")
|
|
221
|
+
emulator.load_state(state_path)
|
|
222
|
+
|
|
223
|
+
# Get map data around player using the same function as the agent
|
|
224
|
+
map_data = emulator.memory_reader.read_map_around_player(radius=7)
|
|
225
|
+
|
|
226
|
+
# Print the map data for visual inspection
|
|
227
|
+
print_map_data(map_data, "House State Map (Door Test)")
|
|
228
|
+
|
|
229
|
+
# Find any door tiles
|
|
230
|
+
door_found = False
|
|
231
|
+
door_behaviors = [MetatileBehavior.NON_ANIMATED_DOOR,
|
|
232
|
+
MetatileBehavior.ANIMATED_DOOR,
|
|
233
|
+
MetatileBehavior.WATER_DOOR]
|
|
234
|
+
|
|
235
|
+
for row in map_data:
|
|
236
|
+
for tile in row:
|
|
237
|
+
behavior = tile[1]
|
|
238
|
+
if behavior in door_behaviors:
|
|
239
|
+
door_found = True
|
|
240
|
+
# Verify door tile is not marked as blocked
|
|
241
|
+
assert behavior != MetatileBehavior.NORMAL, "Door should not be marked as normal path"
|
|
242
|
+
assert behavior in door_behaviors, "Door should have proper door behavior"
|
|
243
|
+
|
|
244
|
+
# We should find at least one door in the house state
|
|
245
|
+
assert door_found, "No doors found in house state"
|
|
246
|
+
|
|
247
|
+
def test_map_data_validation(emulator):
|
|
248
|
+
"""Test validation of map data structure and content"""
|
|
249
|
+
# Load any state (using truck state)
|
|
250
|
+
state_path = os.path.join(TEST_STATES_DIR, "truck.state")
|
|
251
|
+
emulator.load_state(state_path)
|
|
252
|
+
|
|
253
|
+
# Test different radius values
|
|
254
|
+
for radius in [3, 5, 7]:
|
|
255
|
+
map_data = emulator.memory_reader.read_map_around_player(radius=radius)
|
|
256
|
+
|
|
257
|
+
# Check dimensions
|
|
258
|
+
expected_size = 2 * radius + 1
|
|
259
|
+
assert len(map_data) == expected_size, f"Map height should be {expected_size} for radius {radius}"
|
|
260
|
+
assert all(len(row) == expected_size for row in map_data), \
|
|
261
|
+
f"All rows should have width {expected_size} for radius {radius}"
|
|
262
|
+
|
|
263
|
+
# Check tile data consistency
|
|
264
|
+
for row in map_data:
|
|
265
|
+
for tile in row:
|
|
266
|
+
# Check tile structure
|
|
267
|
+
assert isinstance(tile, tuple), "Tile data should be a tuple"
|
|
268
|
+
assert len(tile) == 4, "Tile data should contain metatile ID, behavior, x, and y coordinates"
|
|
269
|
+
|
|
270
|
+
# Check metatile ID
|
|
271
|
+
metatile_id = tile[0]
|
|
272
|
+
assert isinstance(metatile_id, int), "Metatile ID should be an integer"
|
|
273
|
+
assert metatile_id >= 0, "Metatile ID should be non-negative"
|
|
274
|
+
|
|
275
|
+
# Check behavior
|
|
276
|
+
behavior = tile[1]
|
|
277
|
+
assert isinstance(behavior, MetatileBehavior), "Behavior should be MetatileBehavior enum"
|
|
278
|
+
assert behavior.value >= 0, "Behavior value should be non-negative"
|
|
279
|
+
|
|
280
|
+
def test_upstairs_state_map_reading(emulator):
|
|
281
|
+
"""Test map reading functionality in the upstairs state"""
|
|
282
|
+
# Load the upstairs state
|
|
283
|
+
state_path = os.path.join(TEST_STATES_DIR, "upstairs.state")
|
|
284
|
+
emulator.load_state(state_path)
|
|
285
|
+
|
|
286
|
+
# Get map data around player using the same function as the agent
|
|
287
|
+
map_data = emulator.memory_reader.read_map_around_player(radius=7)
|
|
288
|
+
|
|
289
|
+
# Format the map data
|
|
290
|
+
formatted_map = print_map_data(map_data, "Upstairs State Map")
|
|
291
|
+
|
|
292
|
+
# Basic structure tests
|
|
293
|
+
assert map_data is not None, "Map data should not be None"
|
|
294
|
+
assert isinstance(map_data, list), "Map data should be a list"
|
|
295
|
+
assert len(map_data) > 0, "Map data should not be empty"
|
|
296
|
+
assert isinstance(map_data[0], list), "Map data should be a 2D list"
|
|
297
|
+
|
|
298
|
+
# Test map dimensions
|
|
299
|
+
height = len(map_data)
|
|
300
|
+
width = len(map_data[0])
|
|
301
|
+
assert height == 15, f"Map height should be 15 (2*radius + 1), got {height}"
|
|
302
|
+
assert width == 15, f"Map width should be 15 (2*radius + 1), got {width}"
|
|
303
|
+
|
|
304
|
+
# Test tile data structure
|
|
305
|
+
center_tile = map_data[7][7] # Center tile where player is
|
|
306
|
+
assert isinstance(center_tile, tuple), "Tile data should be a tuple"
|
|
307
|
+
assert len(center_tile) == 4, "Tile data should contain metatile ID, behavior, x, and y coordinates"
|
|
308
|
+
|
|
309
|
+
# Test that we're indoors (should have indoor tiles)
|
|
310
|
+
center_behavior = center_tile[1]
|
|
311
|
+
assert isinstance(center_behavior, MetatileBehavior), "Tile behavior should be MetatileBehavior enum"
|
|
312
|
+
|
|
313
|
+
# Print detailed information for debugging
|
|
314
|
+
# print(f"\n=== UPSTAIRS STATE DEBUG INFO ===")
|
|
315
|
+
print(f"Map dimensions: {width}x{height}")
|
|
316
|
+
print(f"Center tile: {center_tile}")
|
|
317
|
+
print(f"Center behavior: {center_behavior}")
|
|
318
|
+
|
|
319
|
+
# Check for specific upstairs characteristics
|
|
320
|
+
# Upstairs should have indoor tiles, possibly doors, and specific layout
|
|
321
|
+
indoor_tiles = 0
|
|
322
|
+
door_tiles = 0
|
|
323
|
+
wall_tiles = 0
|
|
324
|
+
impassable_tiles = 0
|
|
325
|
+
normal_tiles = 0
|
|
326
|
+
|
|
327
|
+
# Analyze the map data more thoroughly
|
|
328
|
+
for y, row in enumerate(map_data):
|
|
329
|
+
for x, tile in enumerate(row):
|
|
330
|
+
if len(tile) >= 2:
|
|
331
|
+
metatile_id, behavior, collision, elevation = tile
|
|
332
|
+
|
|
333
|
+
# Count different tile types
|
|
334
|
+
if behavior == MetatileBehavior.INDOOR_ENCOUNTER:
|
|
335
|
+
indoor_tiles += 1
|
|
336
|
+
elif behavior in [MetatileBehavior.NON_ANIMATED_DOOR, MetatileBehavior.ANIMATED_DOOR]:
|
|
337
|
+
door_tiles += 1
|
|
338
|
+
elif behavior == MetatileBehavior.NORMAL:
|
|
339
|
+
if collision > 0:
|
|
340
|
+
wall_tiles += 1
|
|
341
|
+
else:
|
|
342
|
+
normal_tiles += 1
|
|
343
|
+
else:
|
|
344
|
+
# Check if this is an impassable tile
|
|
345
|
+
if metatile_id == 0 or behavior.value == 0:
|
|
346
|
+
impassable_tiles += 1
|
|
347
|
+
|
|
348
|
+
print(f"Indoor tiles found: {indoor_tiles}")
|
|
349
|
+
print(f"Door tiles found: {door_tiles}")
|
|
350
|
+
print(f"Wall tiles found: {wall_tiles}")
|
|
351
|
+
print(f"Normal walkable tiles found: {normal_tiles}")
|
|
352
|
+
print(f"Impassable tiles found: {impassable_tiles}")
|
|
353
|
+
|
|
354
|
+
# Check map buffer addresses
|
|
355
|
+
print(f"\nMap buffer info:")
|
|
356
|
+
print(f"Map buffer address: 0x{emulator.memory_reader._map_buffer_addr:08X}")
|
|
357
|
+
print(f"Map width: {emulator.memory_reader._map_width}")
|
|
358
|
+
print(f"Map height: {emulator.memory_reader._map_height}")
|
|
359
|
+
|
|
360
|
+
# Check player coordinates
|
|
361
|
+
player_coords = emulator.memory_reader.read_coordinates()
|
|
362
|
+
print(f"Player coordinates: {player_coords}")
|
|
363
|
+
|
|
364
|
+
# Check if behaviors are being loaded correctly
|
|
365
|
+
all_behaviors = emulator.memory_reader.get_all_metatile_behaviors()
|
|
366
|
+
print(f"Total behaviors loaded: {len(all_behaviors) if all_behaviors else 0}")
|
|
367
|
+
|
|
368
|
+
# Check specific tiles around the player
|
|
369
|
+
print(f"\nTile analysis around player (center):")
|
|
370
|
+
for dy in range(-2, 3):
|
|
371
|
+
for dx in range(-2, 3):
|
|
372
|
+
y = 7 + dy
|
|
373
|
+
x = 7 + dx
|
|
374
|
+
if 0 <= y < len(map_data) and 0 <= x < len(map_data[0]):
|
|
375
|
+
tile = map_data[y][x]
|
|
376
|
+
if len(tile) >= 4:
|
|
377
|
+
metatile_id, behavior, collision, elevation = tile
|
|
378
|
+
symbol = "P" if dy == 0 and dx == 0 else f"{metatile_id:03X}"
|
|
379
|
+
print(f" ({dx:+2d},{dy:+2d}): {symbol} {behavior.name} c{collision} e{elevation}")
|
|
380
|
+
|
|
381
|
+
# Upstairs should have some indoor characteristics
|
|
382
|
+
assert indoor_tiles > 0 or door_tiles > 0, "Upstairs state should have indoor or door tiles"
|
|
383
|
+
|
|
384
|
+
# Check if we have too many impassable tiles (might indicate map reading issues)
|
|
385
|
+
total_tiles = width * height
|
|
386
|
+
impassable_ratio = impassable_tiles / total_tiles
|
|
387
|
+
print(f"Impassable tile ratio: {impassable_ratio:.2%}")
|
|
388
|
+
|
|
389
|
+
# If more than 50% of tiles are impassable, there might be an issue
|
|
390
|
+
if impassable_ratio > 0.5:
|
|
391
|
+
print("WARNING: High ratio of impassable tiles detected. This might indicate map reading issues.")
|
|
392
|
+
|
|
393
|
+
# Save the formatted map for future reference
|
|
394
|
+
truth_path = os.path.join(TEST_STATES_DIR, "upstairs_map_truth.txt")
|
|
395
|
+
with open(truth_path, 'w') as f:
|
|
396
|
+
f.write(formatted_map)
|
|
397
|
+
|
|
398
|
+
print(f"\nMap data saved to {truth_path}")
|
|
399
|
+
# print("=== END UPSTAIRS STATE DEBUG INFO ===\n")
|
|
400
|
+
|
|
401
|
+
def test_map_reading_area_transitions(emulator):
|
|
402
|
+
"""Test that map reading handles area transitions and new saves correctly"""
|
|
403
|
+
# Test 1: Load upstairs state and verify map reading
|
|
404
|
+
state_path = os.path.join(TEST_STATES_DIR, "upstairs.state")
|
|
405
|
+
emulator.load_state(state_path)
|
|
406
|
+
|
|
407
|
+
# Force invalidate map cache to simulate area transition
|
|
408
|
+
emulator.memory_reader.invalidate_map_cache()
|
|
409
|
+
|
|
410
|
+
# Try to read map - should work correctly
|
|
411
|
+
map_data = emulator.memory_reader.read_map_around_player(radius=7)
|
|
412
|
+
|
|
413
|
+
# Verify map data is valid
|
|
414
|
+
assert map_data is not None, "Map data should not be None after cache invalidation"
|
|
415
|
+
assert len(map_data) > 0, "Map data should not be empty after cache invalidation"
|
|
416
|
+
assert len(map_data) == 15, "Map height should be 15 after cache invalidation"
|
|
417
|
+
assert len(map_data[0]) == 15, "Map width should be 15 after cache invalidation"
|
|
418
|
+
|
|
419
|
+
# Test 2: Load house state and verify map reading
|
|
420
|
+
state_path = os.path.join(TEST_STATES_DIR, "house.state")
|
|
421
|
+
emulator.load_state(state_path)
|
|
422
|
+
|
|
423
|
+
# Map cache should be automatically invalidated by load_state
|
|
424
|
+
map_data = emulator.memory_reader.read_map_around_player(radius=7)
|
|
425
|
+
|
|
426
|
+
# Verify map data is valid
|
|
427
|
+
assert map_data is not None, "Map data should not be None after loading house state"
|
|
428
|
+
assert len(map_data) > 0, "Map data should not be empty after loading house state"
|
|
429
|
+
assert len(map_data) == 15, "Map height should be 15 after loading house state"
|
|
430
|
+
assert len(map_data[0]) == 15, "Map width should be 15 after loading house state"
|
|
431
|
+
|
|
432
|
+
# Test 3: Load truck state and verify map reading
|
|
433
|
+
state_path = os.path.join(TEST_STATES_DIR, "truck.state")
|
|
434
|
+
emulator.load_state(state_path)
|
|
435
|
+
|
|
436
|
+
map_data = emulator.memory_reader.read_map_around_player(radius=7)
|
|
437
|
+
|
|
438
|
+
# Verify map data is valid
|
|
439
|
+
assert map_data is not None, "Map data should not be None after loading truck state"
|
|
440
|
+
assert len(map_data) > 0, "Map data should not be empty after loading truck state"
|
|
441
|
+
assert len(map_data) == 15, "Map height should be 15 after loading truck state"
|
|
442
|
+
assert len(map_data[0]) == 15, "Map width should be 15 after loading truck state"
|
|
443
|
+
|
|
444
|
+
print("✅ All area transition tests passed - map reading handles transitions correctly")
|
|
445
|
+
|
|
446
|
+
def test_simple_test_state_map_reading(emulator):
|
|
447
|
+
"""Test map reading functionality in the simple_test state (rival's bedroom)"""
|
|
448
|
+
# Load the simple_test state
|
|
449
|
+
state_path = os.path.join(TEST_STATES_DIR, "simple_test.state")
|
|
450
|
+
emulator.load_state(state_path)
|
|
451
|
+
|
|
452
|
+
# Get map data around player using the same function as the agent
|
|
453
|
+
map_data = emulator.memory_reader.read_map_around_player(radius=7)
|
|
454
|
+
|
|
455
|
+
# Format the map data
|
|
456
|
+
formatted_map = print_map_data(map_data, "Simple Test State Map")
|
|
457
|
+
|
|
458
|
+
# Basic structure tests
|
|
459
|
+
assert map_data is not None, "Map data should not be None"
|
|
460
|
+
assert isinstance(map_data, list), "Map data should be a list"
|
|
461
|
+
assert len(map_data) > 0, "Map data should not be empty"
|
|
462
|
+
assert isinstance(map_data[0], list), "Map data should be a 2D list"
|
|
463
|
+
|
|
464
|
+
# Test map dimensions
|
|
465
|
+
height = len(map_data)
|
|
466
|
+
width = len(map_data[0])
|
|
467
|
+
assert height == 15, f"Map height should be 15 (2*radius + 1), got {height}"
|
|
468
|
+
assert width == 15, f"Map width should be 15 (2*radius + 1), got {width}"
|
|
469
|
+
|
|
470
|
+
# Test tile data structure
|
|
471
|
+
center_tile = map_data[7][7] # Center tile where player is
|
|
472
|
+
assert isinstance(center_tile, tuple), "Tile data should be a tuple"
|
|
473
|
+
assert len(center_tile) == 4, "Tile data should contain metatile ID, behavior, x, and y coordinates"
|
|
474
|
+
|
|
475
|
+
# Print detailed information for debugging
|
|
476
|
+
# print(f"\n=== SIMPLE TEST STATE DEBUG INFO ===")
|
|
477
|
+
print(f"Map dimensions: {width}x{height}")
|
|
478
|
+
print(f"Center tile: {center_tile}")
|
|
479
|
+
print(f"Center behavior: {center_tile[1]}")
|
|
480
|
+
|
|
481
|
+
# Check map buffer addresses
|
|
482
|
+
print(f"\nMap buffer info:")
|
|
483
|
+
print(f"Map buffer address: 0x{emulator.memory_reader._map_buffer_addr:08X}")
|
|
484
|
+
print(f"Map width: {emulator.memory_reader._map_width}")
|
|
485
|
+
print(f"Map height: {emulator.memory_reader._map_height}")
|
|
486
|
+
|
|
487
|
+
# Check player coordinates
|
|
488
|
+
player_coords = emulator.memory_reader.read_coordinates()
|
|
489
|
+
print(f"Player coordinates: {player_coords}")
|
|
490
|
+
|
|
491
|
+
# Check if behaviors are being loaded correctly
|
|
492
|
+
all_behaviors = emulator.memory_reader.get_all_metatile_behaviors()
|
|
493
|
+
print(f"Total behaviors loaded: {len(all_behaviors) if all_behaviors else 0}")
|
|
494
|
+
|
|
495
|
+
# Check specific tiles around the player
|
|
496
|
+
print(f"\nTile analysis around player (center):")
|
|
497
|
+
for dy in range(-2, 3):
|
|
498
|
+
for dx in range(-2, 3):
|
|
499
|
+
y = 7 + dy
|
|
500
|
+
x = 7 + dx
|
|
501
|
+
if 0 <= y < len(map_data) and 0 <= x < len(map_data[0]):
|
|
502
|
+
tile = map_data[y][x]
|
|
503
|
+
if len(tile) >= 4:
|
|
504
|
+
metatile_id, behavior, collision, elevation = tile
|
|
505
|
+
symbol = "P" if dy == 0 and dx == 0 else f"{metatile_id:03X}"
|
|
506
|
+
print(f" ({dx:+2d},{dy:+2d}): {symbol} {behavior.name} c{collision} e{elevation}")
|
|
507
|
+
|
|
508
|
+
# Count different tile types
|
|
509
|
+
indoor_tiles = 0
|
|
510
|
+
door_tiles = 0
|
|
511
|
+
wall_tiles = 0
|
|
512
|
+
impassable_tiles = 0
|
|
513
|
+
normal_tiles = 0
|
|
514
|
+
decoration_tiles = 0
|
|
515
|
+
|
|
516
|
+
for y, row in enumerate(map_data):
|
|
517
|
+
for x, tile in enumerate(row):
|
|
518
|
+
if len(tile) >= 2:
|
|
519
|
+
metatile_id, behavior, collision, elevation = tile
|
|
520
|
+
|
|
521
|
+
# Count different tile types
|
|
522
|
+
if behavior == MetatileBehavior.INDOOR_ENCOUNTER:
|
|
523
|
+
indoor_tiles += 1
|
|
524
|
+
elif behavior in [MetatileBehavior.NON_ANIMATED_DOOR, MetatileBehavior.ANIMATED_DOOR]:
|
|
525
|
+
door_tiles += 1
|
|
526
|
+
elif behavior == MetatileBehavior.NORMAL:
|
|
527
|
+
if collision > 0:
|
|
528
|
+
wall_tiles += 1
|
|
529
|
+
else:
|
|
530
|
+
normal_tiles += 1
|
|
531
|
+
elif "DECORATION" in behavior.name or "HOLDS" in behavior.name:
|
|
532
|
+
decoration_tiles += 1
|
|
533
|
+
else:
|
|
534
|
+
# Check if this is an impassable tile
|
|
535
|
+
if metatile_id == 0 or behavior.value == 0:
|
|
536
|
+
impassable_tiles += 1
|
|
537
|
+
|
|
538
|
+
print(f"\nTile counts:")
|
|
539
|
+
print(f"Indoor tiles found: {indoor_tiles}")
|
|
540
|
+
print(f"Door tiles found: {door_tiles}")
|
|
541
|
+
print(f"Wall tiles found: {wall_tiles}")
|
|
542
|
+
print(f"Normal walkable tiles found: {normal_tiles}")
|
|
543
|
+
print(f"Decoration tiles found: {decoration_tiles}")
|
|
544
|
+
print(f"Impassable tiles found: {impassable_tiles}")
|
|
545
|
+
|
|
546
|
+
# Check if we have too many impassable tiles (might indicate map reading issues)
|
|
547
|
+
total_tiles = width * height
|
|
548
|
+
impassable_ratio = impassable_tiles / total_tiles
|
|
549
|
+
print(f"Impassable tile ratio: {impassable_ratio:.2%}")
|
|
550
|
+
|
|
551
|
+
# If more than 50% of tiles are impassable, there might be an issue
|
|
552
|
+
if impassable_ratio > 0.5:
|
|
553
|
+
print("WARNING: High ratio of impassable tiles detected. This might indicate map reading issues.")
|
|
554
|
+
|
|
555
|
+
# Try to force re-find map buffer addresses
|
|
556
|
+
print("Attempting to force re-find map buffer addresses...")
|
|
557
|
+
emulator.memory_reader.invalidate_map_cache()
|
|
558
|
+
if emulator.memory_reader._find_map_buffer_addresses():
|
|
559
|
+
print("Successfully re-found map buffer addresses")
|
|
560
|
+
# Try reading map again
|
|
561
|
+
new_map_data = emulator.memory_reader.read_map_around_player(radius=7)
|
|
562
|
+
if new_map_data and len(new_map_data) > 0:
|
|
563
|
+
print("Map reading improved after cache invalidation")
|
|
564
|
+
formatted_map = print_map_data(new_map_data, "Simple Test State Map (After Cache Invalidation)")
|
|
565
|
+
|
|
566
|
+
# Save the formatted map for future reference
|
|
567
|
+
truth_path = os.path.join(TEST_STATES_DIR, "simple_test_map_truth.txt")
|
|
568
|
+
with open(truth_path, 'w') as f:
|
|
569
|
+
f.write(formatted_map)
|
|
570
|
+
|
|
571
|
+
print(f"\nMap data saved to {truth_path}")
|
|
572
|
+
# print("=== END SIMPLE TEST STATE DEBUG INFO ===\n")
|
|
573
|
+
|
|
574
|
+
# Simple test state is actually outdoors in tall grass, so it should have outdoor characteristics
|
|
575
|
+
assert normal_tiles > 0 or wall_tiles > 0, "Simple test state should have normal walkable tiles or walls"
|