diff --git a/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp b/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp index c40bee0870..ab59c5b97d 100644 --- a/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp +++ b/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp @@ -67,8 +67,10 @@ UnitTestDatabase make_UNIT_TESTS_ALL(){ Kernels::add_tests(ret); Pokemon::add_tests_LegPlanner(ret); Pokemon::add_tests_MapData(ret); + Pokemon::add_tests_RouteLog(ret); Pokemon::add_tests_StoryEvents(ret); Pokemon::add_tests_StorySegments(ret); + Pokemon::add_tests_StorySteps(ret); NintendoSwitch::add_tests_CheckOnlineDetector(ret); NintendoSwitch::add_tests_FailedToConnectDetector(ret); NintendoSwitch::add_tests_UpdatePopupDetector(ret); diff --git a/SerialPrograms/Source/Pokemon/Navigation/Pokemon_Navigation_Tests.h b/SerialPrograms/Source/Pokemon/Navigation/Pokemon_Navigation_Tests.h index 73eac9841a..2fe4d0e7bb 100644 --- a/SerialPrograms/Source/Pokemon/Navigation/Pokemon_Navigation_Tests.h +++ b/SerialPrograms/Source/Pokemon/Navigation/Pokemon_Navigation_Tests.h @@ -18,8 +18,10 @@ namespace Pokemon{ void add_tests_LegPlanner(UnitTestDatabase& database); void add_tests_MapData(UnitTestDatabase& database); +void add_tests_RouteLog(UnitTestDatabase& database); void add_tests_StoryEvents(UnitTestDatabase& database); void add_tests_StorySegments(UnitTestDatabase& database); +void add_tests_StorySteps(UnitTestDatabase& database); // (name, JSON text) of each map in Pokemon/Navigation/TestMaps/, built into // the program by pa_embed_text_files() (cmake/EmbedTextFiles.cmake). diff --git a/SerialPrograms/Source/Pokemon/Navigation/Pokemon_RouteLog.cpp b/SerialPrograms/Source/Pokemon/Navigation/Pokemon_RouteLog.cpp new file mode 100644 index 0000000000..3fbf5bb16f --- /dev/null +++ b/SerialPrograms/Source/Pokemon/Navigation/Pokemon_RouteLog.cpp @@ -0,0 +1,98 @@ +/* Route Log + * + * From: https://github.com/PokemonAutomation/ + * + */ + +#include +#include "Pokemon_RouteLog.h" + +namespace PokemonAutomation{ +namespace Pokemon{ + + +std::string to_string(const RouteTile& tile){ + return tile.map + " " + to_string(tile.pos); +} + +std::string describe(const std::vector& tiles){ + if (tiles.empty()){ + return "an unknown tile"; + } + std::string out; + for (const RouteTile& tile : tiles){ + out += (out.empty() ? "" : ", ") + to_string(tile); + } + return out; +} + +std::string to_string(const RouteLogLeg& leg){ + const std::string direction = direction_name(leg.direction); + switch (leg.action){ + case RouteLegAction::SURF: + return "surf " + direction; + case RouteLegAction::FACE: + return "face " + direction; + case RouteLegAction::WALK: + break; + } + const char* end = leg.end == RouteLegEnd::WALL ? " (to wall)" : leg.end == RouteLegEnd::WARP ? " (warp)" : " (open)"; + return direction + " " + std::to_string(leg.tiles) + end; +} + +const char* basis_name(EndBasis basis){ + switch (basis){ + case EndBasis::WALKED: + return "walked"; + case EndBasis::ASSUMED: + return "assumed"; + case EndBasis::UNKNOWN: + return "unknown"; + } + return "?"; +} + + +std::string route_plan_line(const std::string& label, const std::vector& from, const std::vector& legs){ + std::string text = label + ": plan from " + describe(from) + ":"; + for (const PlannedRouteLeg& leg : legs){ + text += "\n " + to_string(leg.leg) + " to " + describe(leg.after); + } + return text; +} + +std::string route_leg_line(size_t number, size_t count, const PlannedRouteLeg& leg){ + return std::format("Leg {}/{}: {} to {}", number, count, to_string(leg.leg), describe(leg.after)); +} + +std::string route_leg_end_line( + const std::string& label, const RouteLogLeg& leg, + const std::vector& at, EndBasis basis, const std::string& why +){ + const std::string how = why.empty() ? basis_name(basis) : std::string(basis_name(basis)) + ": " + why; + return std::format("{}: {}: ended at {} ({}).", label, to_string(leg), describe(at), how); +} + +std::string route_replan_line( + const std::string& label, const RouteLogLeg& leg, + const std::string& what, const std::vector& at +){ + return std::format("{}: {}: {}, at {}; planning again.", label, to_string(leg), what, describe(at)); +} + +std::string route_found_line(const std::string& label, const std::vector& at, size_t holds){ + return std::format("{}: found again at {}, after {} hold(s).", label, describe(at), holds); +} + +std::string route_trainer_line(const std::string& label, const std::string& who, const std::vector& at){ + return label + ": " + who + " stopped us about " + describe(at) + "."; +} + +std::string route_arrived_line(const std::string& label, const std::vector& at){ + return label + ": arrived at " + describe(at) + "."; +} + + + +} +} diff --git a/SerialPrograms/Source/Pokemon/Navigation/Pokemon_RouteLog.h b/SerialPrograms/Source/Pokemon/Navigation/Pokemon_RouteLog.h new file mode 100644 index 0000000000..5f5d4f09a4 --- /dev/null +++ b/SerialPrograms/Source/Pokemon/Navigation/Pokemon_RouteLog.h @@ -0,0 +1,120 @@ +/* Route Log + * + * From: https://github.com/PokemonAutomation/ + * + * The log lines a route walk writes, shared by the games' walkers so that + * every game's log reads the same and devtools/maps can draw any run's + * routes from it. These are BDSP AutoStory's route lines, lifted from + * PokemonBDSP_AutoStory_RouteWalk.cpp, with what the other games need: + * + * [Route] To the grass: plan from Route 202 (15, 25), Route 202 (16, 25): + * east 15 (to wall) to Route 202 (23, 25), Route 202 (30, 26) + * north 5 (to wall) to Route 202 (23, 24) + * [Route] Leg 1/2: east 15 (to wall) to Route 202 (23, 25), Route 202 (30, 26) + * [Route] To the grass: east 15 (to wall): ended at Route 202 (23, 25) (walked). + * [Route] To the grass: north 5 (to wall): stopped early after about 2 tiles, at Route 202 (23, 23); planning again. + * [Route] To the grass: found again at Route 202 (22, 24), after 2 holds. + * [Route] To the grass: arrived at Route 202 (23, 24). + * + * A position is a set of tiles: BDSP plans from every tile the player may + * be on, FRLG from the one it is on. A leg's end says how it is known: + * walked (as planned, or seen), assumed (taken to have ended as planned + * after something interrupted it), or unknown. + * + * The functions return a line without the "[Route] " tag, which + * ROUTE_LOG_TAG holds; the caller logs it. + * + */ + +#ifndef PokemonAutomation_Pokemon_RouteLog_H +#define PokemonAutomation_Pokemon_RouteLog_H + +#include +#include +#include +#include "Pokemon_Grid.h" + +namespace PokemonAutomation{ +namespace Pokemon{ + + +inline constexpr const char* ROUTE_LOG_TAG = "[Route] "; + +// A tile of a named map. +struct RouteTile{ + std::string map; + TilePos pos; +}; +// "Route 202 (23, 25)". +std::string to_string(const RouteTile& tile); +// The tiles, comma-separated; "an unknown tile" for none. +std::string describe(const std::vector& tiles); + + +enum class RouteLegAction{ + WALK, + SURF, + FACE, +}; + +// How a leg ends: at a wall, on a warp, or where it is timed to. +enum class RouteLegEnd{ + OPEN, + WALL, + WARP, +}; + +struct RouteLogLeg{ + RouteLegAction action = RouteLegAction::WALK; + Direction direction = Direction::NORTH; + size_t tiles = 0; + RouteLegEnd end = RouteLegEnd::OPEN; +}; +// "east 15 (to wall)", "south 4 (open)", "north 1 (warp)", "surf east", +// "face west". +std::string to_string(const RouteLogLeg& leg); + +// A planned leg and the tiles it may end on. +struct PlannedRouteLeg{ + RouteLogLeg leg; + std::vector after; +}; + +// How the end of a walked leg is known. +enum class EndBasis{ + WALKED, // walked as planned, or seen + ASSUMED, // taken to have ended as planned after an interrupt + UNKNOWN, // stopped somewhere on the way +}; +const char* basis_name(EndBasis basis); + + +// "