From 504bb704f7be6839e2f553a6d796c23d5c21ed00 Mon Sep 17 00:00:00 2001 From: dayfine Date: Sun, 8 Nov 2026 14:53:13 -0800 Subject: [PATCH 1/3] Make existing BDSP recovery tests compile across GCC and Clang-cl --- .../AutoStory/PokemonBDSP_AutoStory_RecoveryWalk_Tests.cpp | 5 +++-- .../Simulation/PokemonBDSP_AutoStory_GridSim_Tests.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_RecoveryWalk_Tests.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_RecoveryWalk_Tests.cpp index 794a08bc30..1654a8cfe1 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_RecoveryWalk_Tests.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_RecoveryWalk_Tests.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "Common/Cpp/Exceptions.h" #include "Pokemon/AutoStory/Pokemon_TilePose.h" @@ -217,7 +218,7 @@ class WallStepIO : public RecoveryStepIO{ GridSim& m_sim; }; -const Pokemon::StoryStep& story_step(const std::string& row, const std::string& name){ +const Pokemon::StoryStep& story_step(std::string_view row, std::string_view name){ for (const Pokemon::StoryCheckpoint& checkpoint : story_checkpoints()){ for (const Pokemon::StoryStep& step : checkpoint.steps){ if (checkpoint.id == row && step.name == name){ @@ -225,7 +226,7 @@ const Pokemon::StoryStep& story_step(const std::string& row, const std::string& } } } - throw GridSimFailure{"no step " + name + " in " + row}; + throw GridSimFailure{std::format("no step {} in {}", name, row)}; } // lab-B 2026-11-08 11:55: a wild battle on 01.03's counted taps across diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_GridSim_Tests.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_GridSim_Tests.cpp index 190f7409da..d565eb096e 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_GridSim_Tests.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_GridSim_Tests.cpp @@ -484,7 +484,7 @@ StepsWalk walk_steps(GridSim& sim, const TableSteps& table){ WalkStepsIO io(sim, route_io); StoryWalkSteps walk(io); walk.begin_attempt("01.03"); - Pokemon::RunState state{.pose = table.start}; + Pokemon::RunState state{.pose = table.start, .flags = {}}; try{ for (const StoryStep& step : table.steps){ const Pokemon::Outcome outcome = step.kind == StoryStepKind::ROUTE ? walk.route(state, step) From bd5e3592c8e6b6941c13e38989e05d166e7da1b8 Mon Sep 17 00:00:00 2001 From: Di Fan <26423504+dayfine@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:50:04 -0800 Subject: [PATCH 2/3] BDSP: load mine/Roark checkpoint from shared authored JSON (#263) * Load BDSP mine checkpoint from shared JSON without changing its executor * Pin the BDSP replay schema correction for paired CI * Compare normalized checkpoint semantics instead of optional JSON defaults * Share authored checkpoint envelopes and embedded resource loading * Use shared authored checkpoint loading for BDSP * Make the native mine plan authoritative and address data review * Space BDSP data declarations and test mutation helpers * Resolve simulator staging root across Python versions * Keep existing Git normalization rules for authored story data * Report actual BDSP story data values in test failures --- .../workflows/cpp-ci-serial-programs-base.yml | 40 ++++ SerialPrograms/CMakeLists.txt | 14 ++ .../Source/PokemonBDSP/PokemonBDSP_Tests.cpp | 2 + .../PokemonBDSP_AutoStory_StoryData.cpp | 56 +++++ .../PokemonBDSP_AutoStory_StoryData.h | 34 +++ .../PokemonBDSP_AutoStory_StoryData_Tests.cpp | 195 +++++++++++++++++ .../PokemonBDSP_AutoStory_StoryData_Tests.h | 22 ++ .../PokemonBDSP_AutoStory_StoryTable.cpp | 108 +--------- ...onBDSP_AutoStory_StorySimulation_Tests.cpp | 25 +++ .../Programs/AutoStory/StoryData/README.md | 16 ++ .../AutoStory/StoryData/bdsp-mine-01.03.md | 41 ++++ .../StoryData/bdsp-mine-01.03.plan.json | 197 ++++++++++++++++++ SerialPrograms/cmake/SourceFiles.cmake | 4 + 13 files changed, 649 insertions(+), 105 deletions(-) create mode 100644 SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.cpp create mode 100644 SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.h create mode 100644 SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.cpp create mode 100644 SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.h create mode 100644 SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/README.md create mode 100644 SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.md create mode 100644 SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.plan.json diff --git a/.github/workflows/cpp-ci-serial-programs-base.yml b/.github/workflows/cpp-ci-serial-programs-base.yml index a87f526da4..40452d1067 100644 --- a/.github/workflows/cpp-ci-serial-programs-base.yml +++ b/.github/workflows/cpp-ci-serial-programs-base.yml @@ -96,6 +96,46 @@ jobs: operation: prepare consumer-directory: Arduino-Source + - name: Checkout authored BDSP story simulator + if: startsWith(inputs.os, 'ubuntu') && inputs.run-tests + uses: actions/checkout@v7 + with: + repository: dayfine/PokemonSimulation + ref: cf365e1722dea03fcf40c322b910332fcc864ea4 + path: AuthoredStorySource + + - name: Replay the authoritative Arduino mine plan + if: startsWith(inputs.os, 'ubuntu') && inputs.run-tests + env: + PYTHONPATH: AuthoredStorySource/src + run: | + python3 - <<'PYTHON' + from pathlib import Path + import hashlib, json, tempfile + from pokesim.story.replay_binding import load_binding + from pokesim.story.replay_cases import read_cases, check_case + from pokesim.story.checkpoint_replay import run + source = Path('AuthoredStorySource').resolve() + folder = source / 'data/story/bdsp-mine-pilot' + native = Path('Arduino-Source/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData') + # The Arduino plan is authoritative; bind these exact bytes explicitly. + # The simulator's checked-in plan is only a frozen regression snapshot. + with tempfile.TemporaryDirectory(prefix='.native-story-', dir=source) as temporary: + stage = Path(temporary) + raw = (native / 'bdsp-mine-01.03.plan.json').read_bytes() + (stage / 'plan.json').write_bytes(raw) + profile = json.loads((folder / 'binding.json').read_text()) + profile['plan'] = {'file': str((stage / 'plan.json').relative_to(source)), + 'sha256': hashlib.sha256(raw).hexdigest()} + (stage / 'binding.json').write_text(json.dumps(profile)) + binding = load_binding(stage / 'binding.json', source) + cases = read_cases(folder / 'cases.json') + for case in cases: + check_case(case, run(binding, case['input'])) + print(case['id'] + ': current Arduino plan replay passed') + assert len(cases) == 9 + PYTHON + - name: Install Qt uses: jurplel/install-qt-action@v4 with: diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index 0fe561e736..bc51b4daf2 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -240,6 +240,20 @@ pa_pokesim_map_directory(BDSP_MAP_DIRECTORY bdsp "${POKESIM_CONSUMER}" "${POKESI file(RELATIVE_PATH BDSP_MAP_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}" "${BDSP_MAP_DIRECTORY}") pa_embed_text_files(SerialProgramsLib "${BDSP_MAP_RESOURCES}" PokemonBDSP/Programs/AutoStory/Data/PokemonBDSP_AutoStory_Maps.h PokemonAutomation::NintendoSwitch::PokemonBDSP bdsp_map_json) +# TODO: Move JSON syntax preflight into pa_embed_text_files so map and story +# resources share it. Keep game/schema/semantic checks in their native tests. +# Catch JSON syntax errors during a lab build, before static table initialization. +# Native tests still validate the schema, game policy and execution semantics. +file(GLOB BDSP_STORY_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/Source/PokemonBDSP/Programs/AutoStory/StoryData/*.json") +foreach(BDSP_STORY_FILE IN LISTS BDSP_STORY_FILES) + file(READ "${BDSP_STORY_FILE}" BDSP_STORY_TEXT) + string(JSON BDSP_STORY_TYPE ERROR_VARIABLE BDSP_STORY_ERROR TYPE "${BDSP_STORY_TEXT}") + if(BDSP_STORY_ERROR OR NOT BDSP_STORY_TYPE STREQUAL "OBJECT") + message(FATAL_ERROR "Invalid authored checkpoint ${BDSP_STORY_FILE}: ${BDSP_STORY_ERROR}") + endif() +endforeach() +pa_embed_text_files(SerialProgramsLib Source/PokemonBDSP/Programs/AutoStory/StoryData + PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.h PokemonAutomation::NintendoSwitch::PokemonBDSP bdsp_checkpoint_json) # Create binary target diff --git a/SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp b/SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp index 72c29a2fa1..40be88c76f 100644 --- a/SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp +++ b/SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp @@ -16,6 +16,7 @@ #include "Programs/AutoStory/PokemonBDSP_AutoStory_StoryWorld_Tests.h" #include "Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_StorySim_Tests.h" #include "Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_StorySimulation_Tests.h" +#include "Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.h" #include "Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable_Tests.h" #include "Programs/AutoStory/PokemonBDSP_AutoStory_Walk_Tests.h" #include "PokemonBDSP_Tests.h" @@ -34,6 +35,7 @@ void add_tests(UnitTestDatabase& database){ add_tests_AutoStory_StoryMaps(database); add_tests_AutoStory_Segment00(database); add_tests_AutoStory_StorySimulation(database); + add_tests_AutoStory_StoryData(database); add_tests_AutoStory_StoryTable(database); add_tests_AutoStory_Walk(database); add_tests_AutoStory_PlannedRoute(database); diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.cpp new file mode 100644 index 0000000000..8fecf57098 --- /dev/null +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.cpp @@ -0,0 +1,56 @@ +/* BDSP Authored Checkpoint Data + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include +#include "Common/Cpp/Exceptions.h" +#include "Common/Cpp/Json/JsonObject.h" +#include "Pokemon/AutoStory/Pokemon_AuthoredCheckpointJson.h" +#include "PokemonBDSP_AutoStory_StoryData.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonBDSP{ + + +namespace{ + +StoryFailure read_failure(std::string_view value){ + if (value == "reload_and_retry"){ + return StoryFailure::RELOAD_AND_RETRY; + } + if (value == "stop"){ + return StoryFailure::STOP; + } + throw ParseException("Unsupported BDSP failure policy: " + std::string(value)); +} + +StoryCheckpoint apply_execution(Pokemon::AuthoredCheckpoint parsed, std::string_view source){ + const JsonObject& execution = parsed.execution; + Pokemon::require_story_json_fields(execution, {"on_failure", "saves_in"}, source); + StoryCheckpoint result; + static_cast(result) = std::move(parsed.checkpoint); + result.on_failure = read_failure(execution.get_string_throw("on_failure")); + result.saves_in = execution.get_string_throw("saves_in"); + if ((result.on_failure == StoryFailure::RELOAD_AND_RETRY) == result.saves_in.empty()){ + throw ParseException("BDSP save-card location is required exactly when the checkpoint saves/retries."); + } + return result; +} + +} // namespace + +StoryCheckpoint read_authored_checkpoint(const JsonValue& value, std::string_view source){ + return apply_execution(Pokemon::read_authored_checkpoint(value, "bdsp", source), source); +} + +StoryCheckpoint load_authored_checkpoint(std::string_view file){ + return apply_execution(Pokemon::load_authored_checkpoint(bdsp_checkpoint_json(), file, "bdsp"), file); +} + + +} // namespace PokemonBDSP +} // namespace NintendoSwitch +} // namespace PokemonAutomation diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.h b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.h new file mode 100644 index 0000000000..ef56b77bca --- /dev/null +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.h @@ -0,0 +1,34 @@ +/* BDSP Authored Checkpoint Data + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonBDSP_AutoStory_StoryData_H +#define PokemonAutomation_PokemonBDSP_AutoStory_StoryData_H + +#include +#include +#include +#include +#include "Common/Cpp/Json/JsonValue.h" +#include "PokemonBDSP_AutoStory_StoryExecutor.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonBDSP{ + + +// Strict authored envelope and BDSP save/failure configuration; no controller IO. +// Unknown fields/values or inconsistent save policy throw ParseException. +StoryCheckpoint read_authored_checkpoint(const JsonValue& value, std::string_view source); + +// The embedded authored JSON files, keyed by filename without .json. +const std::vector>& bdsp_checkpoint_json(); + +// Load an embedded authored checkpoint; a missing file throws InternalProgramError. +StoryCheckpoint load_authored_checkpoint(std::string_view file); +} // namespace PokemonBDSP +} // namespace NintendoSwitch +} // namespace PokemonAutomation +#endif diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.cpp new file mode 100644 index 0000000000..98c70e6f7a --- /dev/null +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.cpp @@ -0,0 +1,195 @@ +/* BDSP Authored Checkpoint Tests + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include +#include +#include "Common/Cpp/Exceptions.h" +#include "Common/Cpp/Json/JsonArray.h" +#include "Common/Cpp/Json/JsonObject.h" +#include "Pokemon/AutoStory/Pokemon_StoryStepJson.h" +#include "Pokemon/AutoStory/Pokemon_TilePose.h" +#include "Data/PokemonBDSP_AutoStory_Maps.h" +#include "PokemonBDSP_AutoStory_StoryData.h" +#include "PokemonBDSP_AutoStory_StoryTable.h" +#include "PokemonBDSP_AutoStory_StoryData_Tests.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonBDSP{ + + +namespace{ + +struct Input{ + std::string_view name; + void (*mutate)(JsonObject&) = nullptr; +}; + +struct Expectation{ + bool rejected = false; +}; +struct Case{ + Input input; + Expectation expectation; +}; +void extra(JsonObject& root){ + root["unknown"] = true; +} + +void wrong_game(JsonObject& root){ + root["game"] = "frlg"; +} + +void wrong_policy(JsonObject& root){ + root.get_object_throw("execution")["on_failure"] = "guess"; +} + +void missing_card(JsonObject& root){ + root.get_object_throw("execution")["saves_in"] = ""; +} + +void stop_with_save(JsonObject& root){ + root.get_object_throw("execution")["on_failure"] = "stop"; +} + +const Case CASES[] = { + {.input = {.name = "mine resource is registered with ordered events and save policy"}, .expectation = {}}, + {.input = {.name = "extra envelope field", .mutate = extra}, .expectation = {.rejected = true}}, + {.input = {.name = "wrong game", .mutate = wrong_game}, .expectation = {.rejected = true}}, + {.input = {.name = "unknown failure policy", .mutate = wrong_policy}, .expectation = {.rejected = true}}, + {.input = {.name = "missing save-card location", .mutate = missing_card}, .expectation = {.rejected = true}}, + {.input = {.name = "stop policy must not claim a start save", .mutate = stop_with_save}, .expectation = {.rejected = true}}, +}; + +std::string string_list(const std::vector& values){ + JsonArray result; + for (const std::string& value : values){ + result.push_back(value); + } + return result.dump(-1); +} + +std::string tile_list(const std::vector& tiles){ + std::vector values; + for (const auto& tile : tiles){ + values.push_back(Pokemon::to_string(tile)); + } + return string_list(values); +} + +std::string policy(const StoryCheckpoint& row){ + return std::format("{}; saves_in=\"{}\"", + row.on_failure == StoryFailure::RELOAD_AND_RETRY ? "reload_and_retry" : "stop", row.saves_in); +} + +JsonValue plan(){ + std::vector names; + for (const auto& [name, text] : bdsp_checkpoint_json()){ + names.push_back(name); + if (name == "bdsp-mine-01.03.plan"){ + return parse_json(text); + } + } + throw ParseException("Expected embedded bdsp-mine-01.03.plan.json; read resource names " + string_list(names)); +} + +// Literal authored poses must stay aligned with the map-derived anchors +// used by adjacent checkpoints; a map correction must trigger plan review. +std::string check_map_anchors(const StoryCheckpoint& loaded){ + const std::vector doorstep{ + {47, 21}, map_point("OreburghCity", "pokemon_center_doorstep") + }; + if (!loaded.start || Pokemon::tiles_of(*loaded.start) != doorstep){ + return "Expected Center exit tiles " + tile_list(doorstep) + "; read " + + (loaded.start ? Pokemon::story_pose_json(*loaded.start).dump(-1) : "absent start"); + } + const std::vector below_roark{ + map_point("OreburghMine", "roark").next(Pokemon::Direction::SOUTH) + }; + for (const auto& step : loaded.steps){ + const bool roark = step.name == "below_roark" || step.name == "roark_smashes_the_rock"; + if (roark || step.name == "heal_oreburgh"){ + const auto& expected = roark ? below_roark : doorstep; + if (!step.pose_after || Pokemon::tiles_of(*step.pose_after) != expected){ + return "Expected " + step.name + " arrival tiles " + tile_list(expected) + "; read " + + (step.pose_after ? Pokemon::story_pose_json(*step.pose_after).dump(-1) : "absent arrival"); + } + } + } + return ""; +} + +std::string check_registration(const StoryCheckpoint& loaded){ + const auto& rows = story_checkpoints(); + const auto row = std::find_if(rows.begin(), rows.end(), [](const StoryCheckpoint& x){ return x.id == "01.03"; }); + // Wiring check only: both values originate in the authored resource. + // Route correctness is covered by separate scenario expectations. + if (row == rows.end()){ + std::vector ids; + for (const auto& entry : rows){ + ids.push_back(entry.id); + } + return "Expected registered checkpoint 01.03; read checkpoint IDs " + string_list(ids); + } + if (Pokemon::story_checkpoint_json(*row) != Pokemon::story_checkpoint_json(loaded)){ + return "Expected registered resource " + Pokemon::story_checkpoint_json(loaded).dump(-1) + + "; read table record " + Pokemon::story_checkpoint_json(*row).dump(-1); + } + if (loaded.on_failure != StoryFailure::RELOAD_AND_RETRY || loaded.saves_in != "Oreburgh City" + || row->on_failure != loaded.on_failure || row->saves_in != loaded.saves_in){ + return "Expected reload_and_retry; saves_in=\"Oreburgh City\" for resource and table; read resource " + + policy(loaded) + "; table " + policy(*row); + } + const std::vector expected{ + "above_the_mine", "into_the_mine", "into_the_coal_room", "south_to_the_rock", + "to_roarks_corner", "beside_roarks_rock", "below_roark", "roark", "roark_smashes_the_rock", + "below_the_coal_rooms_warp", "up_to_the_hall", "out_of_the_mine", + "north_to_the_centers_wall", "east_below_the_centers_door", "heal_oreburgh" + }; + std::vector actual; + for (const auto& step : loaded.steps){ + actual.push_back(step.name); + } + if (actual != expected){ + return "Expected event IDs " + string_list(expected) + "; read " + string_list(actual); + } + return check_map_anchors(loaded); +} + +class DataTest : public UnitTest{ +public: + explicit DataTest(Case test) : UnitTest("PokemonBDSP::StoryData - " + std::string(test.input.name)), m_test(test){} + virtual UnitTestResult run(Logger&, CancellableScope&) const override{ + JsonValue input = plan(); + if (m_test.input.mutate){ + m_test.input.mutate(input.to_object_throw()); + } + try{ + const StoryCheckpoint loaded = read_authored_checkpoint(input, m_test.input.name); + if (m_test.expectation.rejected){ + return "Expected authored data rejection; read accepted record " + Pokemon::story_checkpoint_json(loaded).dump(-1); + } + const std::string error = check_registration(loaded); + return error.empty() ? UnitTestResult(true) : UnitTestResult(error); + }catch (const Exception& error){ + return m_test.expectation.rejected ? UnitTestResult(true) : UnitTestResult(error.to_str()); + } + } +private: + Case m_test; +}; + +} // namespace + +void add_tests_AutoStory_StoryData(UnitTestDatabase& database){ + for (const Case& test : CASES){ + database.add(test); + } +} + +} // namespace PokemonBDSP +} // namespace NintendoSwitch +} // namespace PokemonAutomation diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.h b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.h new file mode 100644 index 0000000000..2b81e594e7 --- /dev/null +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.h @@ -0,0 +1,22 @@ +/* BDSP Authored Checkpoint Tests + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#ifndef PokemonAutomation_PokemonBDSP_AutoStory_StoryData_Tests_H +#define PokemonAutomation_PokemonBDSP_AutoStory_StoryData_Tests_H + +#include "Common/Cpp/TestRunners/UnitTestDatabase.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonBDSP{ + + +// Register strict authored-data validation and table-wiring cases. +void add_tests_AutoStory_StoryData(UnitTestDatabase& database); +} // namespace PokemonBDSP +} // namespace NintendoSwitch +} // namespace PokemonAutomation +#endif diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp index a6e5a30c63..aeaf1a8fc2 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp @@ -23,6 +23,7 @@ #include #include #include "Data/PokemonBDSP_AutoStory_Maps.h" +#include "PokemonBDSP_AutoStory_StoryData.h" #include "PokemonBDSP_AutoStory_StoryTable.h" namespace PokemonAutomation{ @@ -59,10 +60,6 @@ const char* const JUBILIFE = "JubilifeCity"; const char* const TRAINERS_SCHOOL = "TrainersSchool"; const char* const OREBURGH = "OreburghCity"; const char* const GYM = "OreburghGym"; -// The mine's entrance hall (Diamond's MAP_OREBURGH_MINE_B1F) and the coal -// room below it, Roark's. -const char* const MINE_HALL = "OreburghMineB1F"; -const char* const MINE = "OreburghMine"; const char* const GATE = "OreburghGate"; const char* const ROUTE_203 = "Route203"; const char* const ROUTE_204_SOUTH = "Route204South"; @@ -685,68 +682,6 @@ StoryCheckpoint route_203_to_oreburgh(){ }); } -// 01.03, planned on Oreburgh's map and the coal room's (Diamond's zone -// events narc_0044 and narc_0197): Barry in the Center's door says Roark -// went to the mine (Roark ignored A until then, r101 and r104); down the -// door's column to the mine's warp tiles (44..48, 59); straight down the -// entrance hall to its warps (11..13, 21); in the coal room down to -// Roark, whose scene ends with him gone (Platinum's -// scripts_oreburgh_mine_b2f.s: he smashes the rock east of him and walks -// off east); back up and out; and the Center's nurse, so 01.04 starts on -// the doorstep. The Center's door is the decomp's (47, 20): out of -// Sandgem's and Jubilife's Centers the console stood in their doors' -// columns. The map's doorstep (46, 21) is the Gym's door column plus the -// 20 taps of 01.04 (r217d, r218d), which do not say where the taps began; -// the heal lines up on the lit doorway if north from (47, 21) misses. -// -// The hall is walked on counted taps, not held: it is a cave, and a wild -// battle on a hold toward a fade would read as the fade. Its warp tiles -// are only entered on the step after, which meets no wild Pokemon. The -// warps are Platinum's (pret/pokeplatinum res/field/maps/*/events.json; -// BDSP keeps DP's layout): the city's (44..48, 59) lead to the hall's -// (11, 1), (11, 1), (12, 1), (13, 1), (13, 1), and the hall's (11..13, 1) -// back to the city's (45..47, 59), a column each. Down, from the city's -// (46, 59) we land on the hall's (12, 1); up, the coal room's warp (15, 1) -// lands us on the hall's (12, 21), not (11, 21), so the walk north up -// column 12 comes out on the city's (46, 59), east of the mine's sign -// (45, 56): the walk north to it never stopped there (lab-B 2026-11-08, -// 09:46, 10:39 and 12:03). -// -// Across the coal room to Roark's east corner (20, 28): the north wall above -// it, his rock (19, 28) to its west. A wild battle on a counted tap re-plans -// to the end of the step, and a step that ends in a corner ends that re-plan -// on wall clips. Lab-B 2026-11-08 11:55: the re-plan went only to the end of -// the interrupted leg, the open tile (15, 16); its last leg, south 5 across -// the open room, overshot a row, and the taps after ran under Roark's rocks. -std::vector legs_to_roarks_corner(){ - return { - {.direction = EAST, .tiles = 1}, - {.direction = SOUTH, .tiles = 2}, - {.direction = WEST, .tiles = 1}, - {.direction = SOUTH, .tiles = 5}, - {.direction = EAST, .tiles = 14}, - {.direction = SOUTH, .tiles = 10}, - {.direction = WEST, .tiles = 2}, - {.direction = SOUTH, .tiles = 2}, - {.direction = WEST, .tiles = 7}, - }; -} - -std::vector legs_out_of_coal_room(){ - return { - {.direction = EAST, .tiles = 9}, - {.direction = NORTH, .tiles = 3}, - {.direction = EAST, .tiles = 2}, - {.direction = NORTH, .tiles = 10}, - {.direction = WEST, .tiles = 14}, - {.direction = NORTH, .tiles = 5}, - {.direction = EAST, .tiles = 1}, - {.direction = NORTH, .tiles = 2}, - {.direction = WEST, .tiles = 1}, - {.direction = NORTH, .tiles = 7}, - }; -} - // 01.02b: the welcome leaves the party as Route 203 left it (2026-11-07 runs: // the lead paralysed, its water move out of PP, and every reload of 01.03 // replayed that). Barry's lines first, as before the split (he blocks the @@ -763,45 +698,9 @@ StoryCheckpoint oreburgh_center_heal(){ }); } +// The authored file carries the mine route and its save-card/failure policy. StoryCheckpoint oreburgh_mine_to_roark(){ - const Pose below_roark = next_to(MINE, "roark", SOUTH, NORTH); - return row("01.03", "Oreburgh Mine to Roark", out_of_oreburgh_center(), { - route("above_the_mine", at(OREBURGH, 46, 58, SOUTH), "down the door's column to above the mine's warp tiles", "town"), - step_into("into_the_mine", SOUTH, "the warp tile (46, 59)", at(MINE_HALL, 12, 1, SOUTH)), - step_into("into_the_coal_room", SOUTH, "down the entrance hall through the cave mouth", at(MINE, 15, 2, SOUTH)), - anchored(to_walls("south_to_the_rock", {{.direction = SOUTH, .tiles = 8}}, "south against the corridor rock (15, 11)"), - at(MINE, 15, 9, SOUTH)), - anchored(legs("to_roarks_corner", legs_to_roarks_corner(), "east around the rock, across the room, down column 29, west to Roark's rock"), - at(MINE, 20, 28, WEST)), - // Counted taps along the open row 29 drifted (lab-B 2026-11-07: south 3 - // moved 2): south stops at the wall under row 29, two tiles from below him. - anchored(to_walls("beside_roarks_rock", {{.direction = SOUTH, .tiles = 1}}, "south against the room's wall"), - at(MINE, 20, 29, SOUTH)), - anchored(legs("below_roark", {{.direction = WEST, .tiles = 2}}, "west 2 to below Roark"), below_roark), - talk("roark", "Roark, north of us between his rocks"), - scene("roark_smashes_the_rock", below_roark, "his lines, Rock Smash on the rock east of him, and he walks off"), - anchored(legs("below_the_coal_rooms_warp", legs_out_of_coal_room(), "east around crate, up column 29, west across row 17, and up corridor to warp doorstep"), - at(MINE, 15, 2, NORTH)), - step_into("up_to_the_hall", NORTH, "the warp tile (15, 1)", at(MINE_HALL, 12, 21, NORTH)), - // Out on the city's (46, 59), the warp tile of the hall's column 12 - // (the decomp's warp: the hall's (12, 1) to the city's (46, 59)), or - // a tile north should the walk go on past the fade. On the console - // (lab-B 2026-11-08 13:50) a tap north and a hold east to the wall - // matched the map only from column 46. - step_into("out_of_the_mine", NORTH, "up the entrance hall and out to the city", - Pokemon::tile_pose(OREBURGH, {TilePos{46, 59}, TilePos{46, 58}}, NORTH)), - // Straight north up column 46 to the wall west of the Center's door, - // (46, 20): 37 tiles from (46, 58), 38 from (46, 59). Oreburgh's - // Hiker wanders (46..48, 25..27) across it, and the walk waits for - // him. The column was the one the console stopped on at 10:39, and - // it keeps off the street east of the mine, where at 13:50 a hold - // east from (48, 57) moved 0 though the map has (49, 57) open, and - // the walk after went astray. - anchored(legs("north_to_the_centers_wall", {{.direction = NORTH, .tiles = 37}}, - "north up column 46 to the wall west of the Center's door", "counted_wall"), at(OREBURGH, 46, 21, NORTH)), - anchored(legs("east_below_the_centers_door", {{.direction = EAST, .tiles = 1}}, "a tap east below the door"), below_oreburghs_center()), - heal("heal_oreburgh", "the nurse; out onto the doorstep, where 01.04 starts", out_of_oreburgh_center()), - }); + return load_authored_checkpoint("bdsp-mine-01.03.plan"); } // Below Rowan's trigger in Jubilife: (47, 8), the east tile under the @@ -952,7 +851,6 @@ const std::map& start_save_cards(){ {"01.01", "Jubilife City"}, {"01.02", "Route 203"}, {"01.02b", "Oreburgh City"}, - {"01.03", "Oreburgh City"}, {"01.04", "Oreburgh City"}, {"01.05", "Oreburgh City"}, {"01.06", "Route 203"}, diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_StorySimulation_Tests.cpp b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_StorySimulation_Tests.cpp index 27c100db13..16de02bb7c 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_StorySimulation_Tests.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_StorySimulation_Tests.cpp @@ -510,7 +510,32 @@ class Test_StorySimulation : public UnitTest{ } // namespace +// Execute the authored mine resource through the actual table and shared engine. +UnitTestResult test_authored_mine(Logger&){ + Simulation sim; + const auto report = sim.run({.from = "01.03", .to = "01.03"}); + if (!report.completed || sim.begun() != std::vector{"01.03"} + || sim.count("save in 01.03") != 1 || sim.count("card for 01.03: Oreburgh City") != 1 + || sim.count("01.03/heal_oreburgh") != 1 || sim.count("01.03/roark") != 1){ + return "Expected the JSON-authored mine checkpoint to save once, talk to Roark, heal and finish:" + joined(sim.trace) + joined(sim.lines); + } + return true; +} + +UnitTestResult test_authored_mine_stops(Logger&){ + Simulation sim; + sim.game.failures["01.03/out_of_the_mine"] = {.failure = "unconfirmed_transition", .times = 3}; + const auto report = sim.run({.from = "01.03", .to = "01.03"}); + if (report.completed || report.reloads != 2 || report.failed_event != "out_of_the_mine" + || sim.count("01.03/heal_oreburgh") != 0){ + return "Expected failed mine transition to exhaust retries before healing:" + joined(sim.trace) + joined(sim.lines); + } + return true; +} + const AutoStoryStorySimulationCase AUTO_STORY_STORY_SIMULATION_CASES[] = { + {.input = {.name = "01.03 authored mine executes through the real table", .body = test_authored_mine}, .expectation = {}}, + {.input = {.name = "01.03 unconfirmed mine exit stops before healing", .body = test_authored_mine_stops}, .expectation = {}}, { .input = {"00.00 to Floaroma, in order, each row saved first, English and Japanese", test_whole_table}, .expectation = {}, diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/README.md b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/README.md new file mode 100644 index 0000000000..cf77edffe0 --- /dev/null +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/README.md @@ -0,0 +1,16 @@ +# BDSP authored checkpoint pilot + +`bdsp-mine-01.03.plan.json` is the authoritative definition of the mine/Roark checkpoint. `oreburgh_mine_to_roark()` loads it into the existing story table. Edit this file here when changing the route; PokemonSimulation consumes an explicitly pinned Arduino revision for independent analysis. Its checked-in pilot plan is a frozen regression snapshot, not a second authoring source. + +The shared `pokesim-authored-checkpoint/1` envelope contains `checkpoint.id`, `title`, `start`, `events` in that order. BDSP validates `execution.on_failure` and `execution.saves_in`. The common envelope and resource lookup live in `Pokemon/AutoStory/Pokemon_AuthoredCheckpointJson.*`. Bookkeeping defaults and game executors are unchanged. + +## Validation and iteration + +1. Native registered tests run the actual JSON-backed table through the existing engine/fake game, including retry exhaustion before healing. Data tests check parsing, table wiring, event IDs/order, policy/card values, and map-derived Center/Roark anchors. The table-wiring comparison is not an independent check against the removed C++ authoring list. +2. Linux CI pins a simulator revision reachable from its main branch and replays **this checkout's JSON** against nine independent scenario expectations. There is no byte-equality requirement against a second plan. A route change may still require revising a scenario expectation when its expected behavior intentionally changes; such failures are meaningful regression feedback, not a requirement to mirror JSON or repin simulator code for every edit. + +A lab route edit therefore touches the Arduino plan and applicable tests/evidence. The next build re-embeds it automatically. CMake rejects malformed JSON syntax before launching the app; native tests perform full reader/policy validation. Run tests before deploying: CMake's syntax preflight alone cannot validate all semantics. The shared formatter writes authored JSON; Git normalization rules are unchanged. Whitespace and line-ending changes do not affect replay results. C++ retains the repository CRLF convention. + +Coordinates remain literal data tied to the pinned map. The map-anchor test catches drift in the Center exit and Roark approach; it does not validate every tile or replace map review. See [the checkpoint evidence](bdsp-mine-01.03.md) for the warp columns, counted hall movement, wall choices and unresolved observations. + +The native fake uses declared scene/door fallbacks and is not a detector/timing oracle. Python uses scripted transition/dialogue/scene/heal evidence and NPC-clearance windows. Missing warp links, native planner-cost parity, queued movement and live recovery remain gaps. Python's diagnostic policy is stop/no-save; the native program retains its start-save/reload policy. No map correction, console, profile or save is part of this authoring migration. diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.md b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.md new file mode 100644 index 0000000000..efe2adc205 --- /dev/null +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.md @@ -0,0 +1,41 @@ +# Mine/Roark checkpoint: route evidence + +This sidecar preserves the reasoning that previously accompanied the C++ row. The JSON defines current actions; these observations explain why they were chosen. They are historical evidence, not new console verification. Entry assumes the preceding Barry conversation and Center heal have completed. + +## Entry and Center alignment + +The Center door is `(47,20)`, so its approach is `(47,21)` facing north. The published `pokemon_center_doorstep` is `(46,21)`: a route there hit the wall west of the door and healing found no door (lab-B, 2026-11-07 20:18). Both `(47,21)` and `(46,21)` facing south remain exit alternatives. Earlier Gym alignment runs r217d/r218d inferred column 46 from twenty west taps; they did not independently establish where those taps began. + +`above_the_mine` goes to `(46,58)` facing south. The city's five warp tiles `(44..48,59)` map into the hall at `(11,1),(11,1),(12,1),(13,1),(13,1)`. Thus column 46 enters B1F at `(12,1)`. The mine hall is Diamond's B1F; the coal room below is the room with Roark. Diamond zone-event sources were narc_0044/narc_0197; Platinum's event files provide the corresponding warp layout, used as evidence for BDSP's retained DP layout. + +## Into the coal room + +`into_the_mine` and `into_the_coal_room` cross the cave mouths. Hall movement uses counted taps rather than an open hold toward a fade: a wild battle on a hold can be mistaken for the transition. Warp entry itself is expected on the subsequent step. These are executor assumptions that still need detector/interruption coverage. + +The hall's coal-room mouth is `(11..13,21)`; column 12 enters the coal room at `(15,2)`. `south_to_the_rock` clips the corridor rock before the grouped inbound legs. The script's anchor is `(15,9)` facing south; the rock was described at `(15,11)`. + +## Roark approach and talk + +`to_roarks_corner` groups all nine inbound legs through `(20,28)` facing west, rather than ending recovery at an open intermediate tile. Lab-B, 2026-11-08 11:55: recovery replanned only to `(15,16)`, a subsequent south-five leg overshot a row, and later taps ran under Roark's rocks. Grouping through a corner allows a replanned path to finish with wall clips. The offline interrupted-leg case deliberately stops with uncertainty; it does not prove live recovery/rejoin. + +`beside_roarks_rock` clips the room's south wall to `(20,29)`, then `below_roark` moves west two to `(18,29)` facing north. Lab-B, 2026-11-07: an open counted south-three leg moved only two, motivating the wall anchor. Roark's map point is `(18,28)`; native tests check the authored below-Roark pose against that map point. + +`roark` initiates the conversation. Barry's earlier message that Roark went to the mine is a prerequisite: Roark ignored A without it in r101/r104. `roark_smashes_the_rock` represents the lines, Rock Smash east of him, and his departure east, consistent with Platinum's mine B2F script. The declared return pose is an executor fallback, not an observed receipt proving the scene completed. + +## Back through the hall + +`below_the_coal_rooms_warp` contains the ten outbound legs: east around the crate, north along column 29, west across row 17, then up the corridor to `(15,2)` facing north. Entering `(15,1)` returns to the hall at `(12,21)`, not `(11,21)`. + +`up_to_the_hall` and `out_of_the_mine` continue up column 12. The hall's `(12,1)` exit leads to city `(46,59)`. The city belief also permits `(46,58)` if movement continued past the fade. Lab-B, 2026-11-08 09:46, 10:39 and 12:03: the exit was east of the sign `(45,56)` and northward movement did not stop there. At 13:50, a north tap and east wall hold matched the map only from column 46. + +## Return to the nurse + +`north_to_the_centers_wall` clips the wall at `(46,20)` from either exit tile: 37 tiles from row 58 or 38 from row 59. The Hiker wanders through columns 46..48 and rows 25..27; the live walker must wait or recover safely, and the offline model uses an explicit synthetic clearance window. + +This column avoids the street east of the mine. At 13:50, an east hold from `(48,57)` moved zero although the map showed `(49,57)` open; later movement went astray. That discrepancy remains unresolved and is not grounds to change terrain silently. + +`east_below_the_centers_door` moves one tile east to `(47,21)` facing north. `heal_oreburgh` enters, heals and exits to the two-tile doorstep belief. The next Gym checkpoint starts there. The checkpoint keeps save-at-start/reload-and-retry guarded by the Oreburgh City save card. + +## Updating this route + +Edit the authoritative JSON, update relevant native/scenario expectations, and preserve new run evidence here. A changed map pin requires anchor review. A claimed transition or recovery fix needs an independent observation/regression case; do not turn `declared_after` into proof of success. The initial migration was checked against the pre-migration row and the reported successful lab-B sequence of 2026-11-08 14:17–14:23; later changes must record their own evidence. diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.plan.json b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.plan.json new file mode 100644 index 0000000000..7a8261607e --- /dev/null +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.plan.json @@ -0,0 +1,197 @@ +{ + "format": "pokesim-authored-checkpoint/1", + "game": "bdsp", + "checkpoint": { + "id": "01.03", + "title": "Oreburgh Mine to Roark", + "start": { + "map": "OreburghCity", + "certainty": "set", + "where": {"kind": "tiles", "tiles": [[47, 21], [46, 21]], "facing": "south"} + }, + "events": [ + { + "id": "above_the_mine", + "kind": "route", + "target": { + "map": "OreburghCity", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[46, 58]], "facing": "south"} + }, + "variant": "town", + "why": "down the door's column to above the mine's warp tiles" + }, + { + "id": "into_the_mine", + "kind": "step", + "direction": "south", + "declared_after": { + "map": "OreburghMineB1F", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[12, 1]], "facing": "south"} + }, + "why": "the warp tile (46, 59)" + }, + { + "id": "into_the_coal_room", + "kind": "step", + "direction": "south", + "declared_after": { + "map": "OreburghMine", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[15, 2]], "facing": "south"} + }, + "why": "down the entrance hall through the cave mouth" + }, + { + "id": "south_to_the_rock", + "kind": "legs", + "legs": [{"direction": "south", "tiles": 8}], + "declared_after": { + "map": "OreburghMine", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[15, 9]], "facing": "south"} + }, + "why": "south against the corridor rock (15, 11)", + "variant": "to_wall" + }, + { + "id": "to_roarks_corner", + "kind": "legs", + "legs": [ + {"direction": "east", "tiles": 1}, + {"direction": "south", "tiles": 2}, + {"direction": "west", "tiles": 1}, + {"direction": "south", "tiles": 5}, + {"direction": "east", "tiles": 14}, + {"direction": "south", "tiles": 10}, + {"direction": "west", "tiles": 2}, + {"direction": "south", "tiles": 2}, + {"direction": "west", "tiles": 7} + ], + "declared_after": { + "map": "OreburghMine", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[20, 28]], "facing": "west"} + }, + "why": "east around the rock, across the room, down column 29, west to Roark's rock" + }, + { + "id": "beside_roarks_rock", + "kind": "legs", + "legs": [{"direction": "south", "tiles": 1}], + "declared_after": { + "map": "OreburghMine", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[20, 29]], "facing": "south"} + }, + "why": "south against the room's wall", + "variant": "to_wall" + }, + { + "id": "below_roark", + "kind": "legs", + "legs": [{"direction": "west", "tiles": 2}], + "declared_after": { + "map": "OreburghMine", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[18, 29]], "facing": "north"} + }, + "why": "west 2 to below Roark" + }, + { + "id": "roark", + "kind": "talk", + "why": "Roark, north of us between his rocks" + }, + { + "id": "roark_smashes_the_rock", + "kind": "scene", + "declared_after": { + "map": "OreburghMine", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[18, 29]], "facing": "north"} + }, + "why": "his lines, Rock Smash on the rock east of him, and he walks off" + }, + { + "id": "below_the_coal_rooms_warp", + "kind": "legs", + "legs": [ + {"direction": "east", "tiles": 9}, + {"direction": "north", "tiles": 3}, + {"direction": "east", "tiles": 2}, + {"direction": "north", "tiles": 10}, + {"direction": "west", "tiles": 14}, + {"direction": "north", "tiles": 5}, + {"direction": "east", "tiles": 1}, + {"direction": "north", "tiles": 2}, + {"direction": "west", "tiles": 1}, + {"direction": "north", "tiles": 7} + ], + "declared_after": { + "map": "OreburghMine", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[15, 2]], "facing": "north"} + }, + "why": "east around crate, up column 29, west across row 17, and up corridor to warp doorstep" + }, + { + "id": "up_to_the_hall", + "kind": "step", + "direction": "north", + "declared_after": { + "map": "OreburghMineB1F", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[12, 21]], "facing": "north"} + }, + "why": "the warp tile (15, 1)" + }, + { + "id": "out_of_the_mine", + "kind": "step", + "direction": "north", + "declared_after": { + "map": "OreburghCity", + "certainty": "set", + "where": {"kind": "tiles", "tiles": [[46, 59], [46, 58]], "facing": "north"} + }, + "why": "up the entrance hall and out to the city" + }, + { + "id": "north_to_the_centers_wall", + "kind": "legs", + "legs": [{"direction": "north", "tiles": 37}], + "declared_after": { + "map": "OreburghCity", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[46, 21]], "facing": "north"} + }, + "why": "north up column 46 to the wall west of the Center's door", + "variant": "counted_wall" + }, + { + "id": "east_below_the_centers_door", + "kind": "legs", + "legs": [{"direction": "east", "tiles": 1}], + "declared_after": { + "map": "OreburghCity", + "certainty": "tile", + "where": {"kind": "tiles", "tiles": [[47, 21]], "facing": "north"} + }, + "why": "a tap east below the door" + }, + { + "id": "heal_oreburgh", + "kind": "heal", + "declared_after": { + "map": "OreburghCity", + "certainty": "set", + "where": {"kind": "tiles", "tiles": [[47, 21], [46, 21]], "facing": "south"} + }, + "why": "the nurse; out onto the doorstep, where 01.04 starts" + } + ] + }, + "execution": {"on_failure": "reload_and_retry", "saves_in": "Oreburgh City"} +} diff --git a/SerialPrograms/cmake/SourceFiles.cmake b/SerialPrograms/cmake/SourceFiles.cmake index 579109c51f..804d80b04e 100644 --- a/SerialPrograms/cmake/SourceFiles.cmake +++ b/SerialPrograms/cmake/SourceFiles.cmake @@ -1575,6 +1575,10 @@ file(GLOB LIBRARY_SOURCES Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Walk_Tests.h Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Segment00_Tests.cpp Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Segment00_Tests.h + Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.cpp + Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData_Tests.h + Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.cpp + Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryData.h Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.cpp Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTable.h Source/PokemonBDSP/Programs/AutoStory/Simulation/PokemonBDSP_AutoStory_StorySimulation_Tests.cpp From 36849eeaa735814675d5285dc514daf39b4c3f39 Mon Sep 17 00:00:00 2001 From: dayfine Date: Sun, 8 Nov 2026 17:06:28 -0800 Subject: [PATCH 3/3] BDSP AutoStory: the mine doc's above_the_mine stops two short of the warps #269 moved the route's target to (46, 57); the authored plan follows it in the merge, and its doc now says so too. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss --- .../PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.md b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.md index efe2adc205..9237f7a965 100644 --- a/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.md +++ b/SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/StoryData/bdsp-mine-01.03.md @@ -6,7 +6,7 @@ This sidecar preserves the reasoning that previously accompanied the C++ row. Th The Center door is `(47,20)`, so its approach is `(47,21)` facing north. The published `pokemon_center_doorstep` is `(46,21)`: a route there hit the wall west of the door and healing found no door (lab-B, 2026-11-07 20:18). Both `(47,21)` and `(46,21)` facing south remain exit alternatives. Earlier Gym alignment runs r217d/r218d inferred column 46 from twenty west taps; they did not independently establish where those taps began. -`above_the_mine` goes to `(46,58)` facing south. The city's five warp tiles `(44..48,59)` map into the hall at `(11,1),(11,1),(12,1),(13,1),(13,1)`. Thus column 46 enters B1F at `(12,1)`. The mine hall is Diamond's B1F; the coal room below is the room with Roark. Diamond zone-event sources were narc_0044/narc_0197; Platinum's event files provide the corresponding warp layout, used as evidence for BDSP's retained DP layout. +`above_the_mine` goes to `(46,57)` facing south, two tiles short of the warp row so an open walk cannot slide onto it (01.05, lab-B 2026-11-08). The city's five warp tiles `(44..48,59)` map into the hall at `(11,1),(11,1),(12,1),(13,1),(13,1)`. Thus column 46 enters B1F at `(12,1)`. The mine hall is Diamond's B1F; the coal room below is the room with Roark. Diamond zone-event sources were narc_0044/narc_0197; Platinum's event files provide the corresponding warp layout, used as evidence for BDSP's retained DP layout. ## Into the coal room