From 346b15aae99e250b676b9d7a60b5d36a7ab94bae Mon Sep 17 00:00:00 2001 From: dayfine Date: Sun, 1 Nov 2026 16:03:14 -0800 Subject: [PATCH 1/7] BDSP MenuDetector and ShortDialogDetector: also detect the new game's menu and long first lines (behavior change) Both detectors keep their old check and accept one more case, so every frame they accepted before is still accepted; they only stop missing two real screens: - MenuDetector: a new game's menu has a "NEW" tag above the OPTIONS icon that breaks the top of the third white column. If the five columns are not white from the top, it now also accepts them white from below the tags (y = 0.170), still requiring the existing cross check. - ShortDialogDetector: a long first line (Rowan's "Now, go on, and leap into the world of Pokemon!") covers the right-end white box. If that box is not white, it now also accepts the box's right end below the first line. This changes what existing BDSP programs see for these screens. The screenshots (positives and negatives) are registered per file from CommandLineTests' PokemonBDSP/MenuDetector and PokemonBDSP/DialogDetector. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AKYzhrMnPK61XHWEch4a6E --- .../ComputerPrograms_UnitTestRunner.cpp | 4 ++ .../Inference/PokemonBDSP_DialogDetector.cpp | 33 +++++++++- .../Inference/PokemonBDSP_DialogDetector.h | 6 ++ .../Inference/PokemonBDSP_MenuDetector.cpp | 63 +++++++++++++------ .../Inference/PokemonBDSP_MenuDetector.h | 5 ++ 5 files changed, 92 insertions(+), 19 deletions(-) diff --git a/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp b/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp index 7b20b845bc..6833c7c134 100644 --- a/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp +++ b/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp @@ -32,6 +32,8 @@ #include "Pokemon/AutoStory/Pokemon_AutoStory_Tests.h" #include "Pokemon/Navigation/Pokemon_Navigation_Tests.h" +#include "PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h" +#include "PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h" #include "PokemonBDSP/PokemonBDSP_Tests.h" #include "VideoGames/PokemonHome/PokemonHome_Tests.h" #include "VideoGames/PokemonSwSh/PokemonSwSh_Tests.h" @@ -79,6 +81,8 @@ UnitTestDatabase make_UNIT_TESTS_ALL(){ NintendoSwitch::add_tests_UpdatePopupDetector(ret); NintendoSwitch::Typewriter::add_tests_TypewriterTextMapping(ret); NintendoSwitch::PokemonBDSP::add_tests(ret); + NintendoSwitch::PokemonBDSP::add_tests_DialogDetector(ret); + NintendoSwitch::PokemonBDSP::add_tests_MenuDetector(ret); NintendoSwitch::PokemonFRLG::add_tests(ret); NintendoSwitch::PokemonRSE::add_tests(ret); NintendoSwitch::PokemonHome::add_tests(ret); diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp index c8636d99cb..e0b9cd0abc 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp @@ -6,6 +6,8 @@ #include "CommonFramework/VideoPipeline/VideoOverlay.h" #include "CommonTools/Images/SolidColorTest.h" +#include "Common/Cpp/TestRunners/UnitTestDatabase.h" +#include "Tests/ImageUnitTest.h" #include "PokemonBDSP_DialogDetector.h" //#include @@ -23,6 +25,7 @@ ShortDialogDetector::ShortDialogDetector(Color color) , m_left_white(0.21, 0.835, 0.008, 0.12) , m_left(0.18, 0.835, 0.02, 0.12) , m_right_white(0.785, 0.835, 0.008, 0.12) + , m_right_white_low(0.785, 0.915, 0.008, 0.04) , m_right(0.822, 0.835, 0.02, 0.12) {} void ShortDialogDetector::make_overlays(VideoOverlaySet& items) const{ @@ -40,7 +43,9 @@ bool ShortDialogDetector::detect(const ImageViewRGB32& screen){ } ImageStats right_white = image_stats(extract_box_reference(screen, m_right_white)); // cout << right_white.average << right_white.stddev << endl; - if (!is_white(right_white)){ + // A long first line runs into m_right_white (Rowan's "Now, go on, and + // leap into the world of Pokemon!"): then the box's right end below it. + if (!is_white(right_white) && !is_white(image_stats(extract_box_reference(screen, m_right_white_low)))){ return false; } // ImageStats bottom = image_stats(extract_box_reference(screen, m_bottom)); @@ -125,6 +130,32 @@ bool ShortDialogPromptDetector::process_frame(const ImageViewRGB32& screen, Wall + + +class Test_ShortDialogDetector : public ImageUnitTest{ +public: + Test_ShortDialogDetector(const std::string& image, bool expected) + : ImageUnitTest("PokemonBDSP::ShortDialogDetector - " + image, {image}) + , m_expected(expected) + {} + + virtual UnitTestResult run_on_images( + Logger&, CancellableScope&, + const std::vector& images + ) const override{ + return ShortDialogDetector().detect(images[0]) == m_expected; + } + +private: + bool m_expected; +}; + +void add_tests_DialogDetector(UnitTestDatabase& database){ + database.add("PokemonBDSP/DialogDetector/IntroRowanNameTab_True.png", true); + database.add("PokemonBDSP/DialogDetector/IntroRowanNoArrow_True.png", true); +} + + } } } diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h index fe7c90cbb0..f1f98bcc3a 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h @@ -7,6 +7,7 @@ #ifndef PokemonAutomation_PokemonBDSP_BattleDialogDetector_H #define PokemonAutomation_PokemonBDSP_BattleDialogDetector_H +#include "Common/Cpp/TestRunners/UnitTestDatabase.h" #include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" #include "CommonTools/VisualDetector.h" #include "PokemonBDSP_SelectionArrow.h" @@ -31,6 +32,7 @@ class ShortDialogDetector : public StaticScreenDetector{ ImageFloatBox m_left_white; ImageFloatBox m_left; ImageFloatBox m_right_white; + ImageFloatBox m_right_white_low; ImageFloatBox m_right; }; class ShortDialogWatcher : public DetectorToFinder{ @@ -79,6 +81,10 @@ class ShortDialogPromptDetector : public VisualInferenceCallback{ + +// The screenshot tests in CommandLineTests/PokemonBDSP/DialogDetector/. +void add_tests_DialogDetector(UnitTestDatabase& database); + } } } diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp index 12da43966e..57814e7e7b 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp @@ -7,6 +7,8 @@ #include "CommonFramework/ImageTools/ImageStats.h" #include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" #include "CommonTools/Images/SolidColorTest.h" +#include "Common/Cpp/TestRunners/UnitTestDatabase.h" +#include "Tests/ImageUnitTest.h" #include "PokemonBDSP_MenuDetector.h" namespace PokemonAutomation{ @@ -41,25 +43,21 @@ void MenuDetector::make_overlays(VideoOverlaySet& items) const{ items.add(m_color, m_line4); items.add(m_color, m_cross); } -bool MenuDetector::detect(const ImageViewRGB32& screen){ - ImageStats stats0 = image_stats(extract_box_reference(screen, m_line0)); - if (!is_white(stats0)){ - return false; - } - ImageStats stats1 = image_stats(extract_box_reference(screen, m_line1)); - if (!is_white(stats1)){ - return false; - } - ImageStats stats2 = image_stats(extract_box_reference(screen, m_line2)); - if (!is_white(stats2)){ - return false; - } - ImageStats stats3 = image_stats(extract_box_reference(screen, m_line3)); - if (!is_white(stats3)){ - return false; +// Whether the five columns between the icons are white from `top` down. +static bool stripes_white(const ImageViewRGB32& screen, double top){ + for (size_t index = 0; index < 5; index++){ + const ImageFloatBox stripe(0.160 + 0.166 * (double)index, top, 0.015, STRIPE_BOTTOM - top); + if (!is_white(image_stats(extract_box_reference(screen, stripe)))){ + return false; + } } - ImageStats stats4 = image_stats(extract_box_reference(screen, m_line4)); - if (!is_white(stats4)){ + return true; +} +bool MenuDetector::detect(const ImageViewRGB32& screen){ + // A "NEW" tag above an icon (a new game's OPTIONS, next to the third + // column) breaks a column at the top; below the tags the columns are + // white either way. + if (!stripes_white(screen, m_line0.y) && !stripes_white(screen, STRIPE_TOP_BELOW_BANNER)){ return false; } ImageStats cross = image_stats(extract_box_reference(screen, m_cross)); @@ -84,6 +82,35 @@ bool MenuWatcher::process_frame(const ImageViewRGB32& frame, WallClock timestamp + +class Test_MenuDetector : public ImageUnitTest{ +public: + Test_MenuDetector(const std::string& image, bool expected) + : ImageUnitTest("PokemonBDSP::MenuDetector - " + image, {image}) + , m_expected(expected) + {} + + virtual UnitTestResult run_on_images( + Logger&, CancellableScope&, + const std::vector& images + ) const override{ + return MenuDetector().detect(images[0]) == m_expected; + } + +private: + bool m_expected; +}; + +void add_tests_MenuDetector(UnitTestDatabase& database){ + database.add("PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", true); + database.add("PokemonBDSP/MenuDetector/Home_False.png", false); + database.add("PokemonBDSP/MenuDetector/OverworldSandgemMart_False.png", false); + database.add("PokemonBDSP/MenuDetector/OverworldOreburghMine_False.png", false); + database.add("PokemonBDSP/MenuDetector/IntroDialog_False.png", false); + database.add("PokemonBDSP/MenuDetector/IntroBarry_False.png", false); +} + + } } } diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h index f7e5a0517f..46ca52470a 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h @@ -11,6 +11,7 @@ #include "CommonFramework/ImageTools/ImageBoxes.h" #include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" #include "CommonTools/VisualDetector.h" +#include "Common/Cpp/TestRunners/UnitTestDatabase.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -46,6 +47,10 @@ class MenuWatcher : public MenuDetector, public VisualInferenceCallback{ + +// The screenshot tests in CommandLineTests/PokemonBDSP/MenuDetector/. +void add_tests_MenuDetector(UnitTestDatabase& database); + } } } From b033840b63a794f69c337bd01e70f260ea321368 Mon Sep 17 00:00:00 2001 From: dayfine Date: Sun, 1 Nov 2026 16:25:07 -0800 Subject: [PATCH 2/7] BDSP MenuDetector: keep its check; test the new game's menu with skip_new_banner Review: MenuDetector(color, skip_new_banner = true) already checks the columns from below the "NEW" tags, which is the new game's menu. So MenuDetector goes back to its old code, and this PR changes only ShortDialogDetector's behavior. The screenshot tests now run both settings: the new game's menu is found with skip_new_banner and missed without it; the negatives are negative either way. ShortDialogDetector also gets an ordinary short-line box (Rowan's "Barry, is it?") as a positive next to the two long-first-line screens. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AKYzhrMnPK61XHWEch4a6E --- .../Inference/PokemonBDSP_DialogDetector.cpp | 2 + .../Inference/PokemonBDSP_MenuDetector.cpp | 73 +++++++++++++------ .../Inference/PokemonBDSP_MenuDetector.h | 1 - 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp index e0b9cd0abc..7fce0b563c 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp @@ -153,6 +153,8 @@ class Test_ShortDialogDetector : public ImageUnitTest{ void add_tests_DialogDetector(UnitTestDatabase& database){ database.add("PokemonBDSP/DialogDetector/IntroRowanNameTab_True.png", true); database.add("PokemonBDSP/DialogDetector/IntroRowanNoArrow_True.png", true); + // A short line: the box's right end is white where the old check looks. + database.add("PokemonBDSP/DialogDetector/IntroRowanShortLine_True.png", true); } diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp index 57814e7e7b..193d4f782f 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp @@ -43,21 +43,25 @@ void MenuDetector::make_overlays(VideoOverlaySet& items) const{ items.add(m_color, m_line4); items.add(m_color, m_cross); } -// Whether the five columns between the icons are white from `top` down. -static bool stripes_white(const ImageViewRGB32& screen, double top){ - for (size_t index = 0; index < 5; index++){ - const ImageFloatBox stripe(0.160 + 0.166 * (double)index, top, 0.015, STRIPE_BOTTOM - top); - if (!is_white(image_stats(extract_box_reference(screen, stripe)))){ - return false; - } - } - return true; -} bool MenuDetector::detect(const ImageViewRGB32& screen){ - // A "NEW" tag above an icon (a new game's OPTIONS, next to the third - // column) breaks a column at the top; below the tags the columns are - // white either way. - if (!stripes_white(screen, m_line0.y) && !stripes_white(screen, STRIPE_TOP_BELOW_BANNER)){ + ImageStats stats0 = image_stats(extract_box_reference(screen, m_line0)); + if (!is_white(stats0)){ + return false; + } + ImageStats stats1 = image_stats(extract_box_reference(screen, m_line1)); + if (!is_white(stats1)){ + return false; + } + ImageStats stats2 = image_stats(extract_box_reference(screen, m_line2)); + if (!is_white(stats2)){ + return false; + } + ImageStats stats3 = image_stats(extract_box_reference(screen, m_line3)); + if (!is_white(stats3)){ + return false; + } + ImageStats stats4 = image_stats(extract_box_reference(screen, m_line4)); + if (!is_white(stats4)){ return false; } ImageStats cross = image_stats(extract_box_reference(screen, m_cross)); @@ -82,11 +86,23 @@ bool MenuWatcher::process_frame(const ImageViewRGB32& frame, WallClock timestamp +namespace{ + +// Which columns the detector checks: the default ones from the top, or the +// ones below the "NEW" tags (a new game's menu, skip_new_banner = true). +enum class Columns{ + FromTop, + BelowNewTags, +}; class Test_MenuDetector : public ImageUnitTest{ public: - Test_MenuDetector(const std::string& image, bool expected) - : ImageUnitTest("PokemonBDSP::MenuDetector - " + image, {image}) + Test_MenuDetector(const std::string& image, Columns columns, bool expected) + : ImageUnitTest( + "PokemonBDSP::MenuDetector" + std::string(columns == Columns::BelowNewTags ? "(skip_new_banner)" : "") + " - " + image, + {image} + ) + , m_columns(columns) , m_expected(expected) {} @@ -94,20 +110,31 @@ class Test_MenuDetector : public ImageUnitTest{ Logger&, CancellableScope&, const std::vector& images ) const override{ - return MenuDetector().detect(images[0]) == m_expected; + MenuDetector detector(COLOR_RED, m_columns == Columns::BelowNewTags); + return detector.detect(images[0]) == m_expected; } private: + Columns m_columns; bool m_expected; }; +} + void add_tests_MenuDetector(UnitTestDatabase& database){ - database.add("PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", true); - database.add("PokemonBDSP/MenuDetector/Home_False.png", false); - database.add("PokemonBDSP/MenuDetector/OverworldSandgemMart_False.png", false); - database.add("PokemonBDSP/MenuDetector/OverworldOreburghMine_False.png", false); - database.add("PokemonBDSP/MenuDetector/IntroDialog_False.png", false); - database.add("PokemonBDSP/MenuDetector/IntroBarry_False.png", false); + // The "NEW" tag above OPTIONS breaks the third column from the top. + database.add("PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", Columns::BelowNewTags, true); + database.add("PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", Columns::FromTop, false); + for (const char* image : { + "PokemonBDSP/MenuDetector/Home_False.png", + "PokemonBDSP/MenuDetector/OverworldSandgemMart_False.png", + "PokemonBDSP/MenuDetector/OverworldOreburghMine_False.png", + "PokemonBDSP/MenuDetector/IntroDialog_False.png", + "PokemonBDSP/MenuDetector/IntroBarry_False.png", + }){ + database.add(image, Columns::FromTop, false); + database.add(image, Columns::BelowNewTags, false); + } } diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h index 46ca52470a..b605bfd3cf 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h @@ -47,7 +47,6 @@ class MenuWatcher : public MenuDetector, public VisualInferenceCallback{ - // The screenshot tests in CommandLineTests/PokemonBDSP/MenuDetector/. void add_tests_MenuDetector(UnitTestDatabase& database); From a69880ec0c468790079a8adb072e3cc8dc0ef6b9 Mon Sep 17 00:00:00 2001 From: dayfine Date: Mon, 2 Nov 2026 01:04:25 -0800 Subject: [PATCH 3/7] BDSP MenuDetector: columns clear of the NEW tags' edges In Jubilife after the Town Map, the second column took the edge of the NEW tag on TOWN MAP, so the menu went unseen and the checkpoint save fell back to blind presses (r65). Each column now starts a little further right, in the clear gap after an icon; the new game's menu and the Jubilife menu are found with either setting, and the negatives stay negative. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AKYzhrMnPK61XHWEch4a6E --- .../Inference/PokemonBDSP_MenuDetector.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp index 193d4f782f..9266926cd0 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp @@ -20,9 +20,14 @@ const double STRIPE_TOP = 0.110; const double STRIPE_TOP_BELOW_BANNER = 0.170; const double STRIPE_BOTTOM = 0.598; +// Each column sits in the gap right of an icon, clear of the "NEW" tag on +// its top right corner: from 0.160 the column took the edge of the tag on +// TOWN MAP and the menu went unseen (r65, Jubilife). +const double STRIPE_LEFT = 0.166; + static ImageFloatBox menu_stripe(size_t index, bool skip_new_banner){ double top = skip_new_banner ? STRIPE_TOP_BELOW_BANNER : STRIPE_TOP; - return ImageFloatBox(0.160 + 0.166 * (double)index, top, 0.015, STRIPE_BOTTOM - top); + return ImageFloatBox(STRIPE_LEFT + 0.166 * (double)index, top, 0.015, STRIPE_BOTTOM - top); } MenuDetector::MenuDetector(Color color, bool skip_new_banner) @@ -122,9 +127,15 @@ class Test_MenuDetector : public ImageUnitTest{ } void add_tests_MenuDetector(UnitTestDatabase& database){ - // The "NEW" tag above OPTIONS breaks the third column from the top. - database.add("PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", Columns::BelowNewTags, true); - database.add("PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", Columns::FromTop, false); + // The new game's menu: a "NEW" tag above OPTIONS. Jubilife after the + // Town Map: one on TOWN MAP, and the quest's text below. + for (const char* image : { + "PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", + "PokemonBDSP/MenuDetector/JubilifeTownMapNew_True.png", + }){ + database.add(image, Columns::FromTop, true); + database.add(image, Columns::BelowNewTags, true); + } for (const char* image : { "PokemonBDSP/MenuDetector/Home_False.png", "PokemonBDSP/MenuDetector/OverworldSandgemMart_False.png", From 67c6ca5a84624b6ce58823830895f022755a2feb Mon Sep 17 00:00:00 2001 From: dayfine Date: Mon, 2 Nov 2026 06:42:18 -0800 Subject: [PATCH 4/7] BDSP detectors: Floaroma and Jubilife menu and dialogue images A menu with three NEW tags, a "???" name tab in Jubilife's plaza, a one-line box with no tab, and cloud shadows over Floaroma as negatives. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01AKYzhrMnPK61XHWEch4a6E --- .../PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp | 5 +++++ .../PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp index 7fce0b563c..17194476f8 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp @@ -155,6 +155,11 @@ void add_tests_DialogDetector(UnitTestDatabase& database){ database.add("PokemonBDSP/DialogDetector/IntroRowanNoArrow_True.png", true); // A short line: the box's right end is white where the old check looks. database.add("PokemonBDSP/DialogDetector/IntroRowanShortLine_True.png", true); + // A "???" name tab, in Jubilife's yellow plaza after the tag battle. + database.add("PokemonBDSP/DialogDetector/JubilifeUnknownNameTab_True.png", true); + // One line, no name tab, over Floaroma's flowers. + database.add("PokemonBDSP/DialogDetector/FloaromaOldManOneLine_True.png", true); + database.add("PokemonBDSP/DialogDetector/FloaromaCloudShadows_False.png", false); } diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp index 9266926cd0..ef90664b86 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp @@ -132,6 +132,8 @@ void add_tests_MenuDetector(UnitTestDatabase& database){ for (const char* image : { "PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", "PokemonBDSP/MenuDetector/JubilifeTownMapNew_True.png", + // Three NEW tags (Trainer Card, Town Map, Ball Capsules), cursor on Bag. + "PokemonBDSP/MenuDetector/FloaromaMenuThreeNew_True.png", }){ database.add(image, Columns::FromTop, true); database.add(image, Columns::BelowNewTags, true); @@ -142,6 +144,9 @@ void add_tests_MenuDetector(UnitTestDatabase& database){ "PokemonBDSP/MenuDetector/OverworldOreburghMine_False.png", "PokemonBDSP/MenuDetector/IntroDialog_False.png", "PokemonBDSP/MenuDetector/IntroBarry_False.png", + "PokemonBDSP/MenuDetector/FloaromaOldManDialog_False.png", + // Cloud shadows over Floaroma's flower field. + "PokemonBDSP/MenuDetector/FloaromaCloudShadows_False.png", }){ database.add(image, Columns::FromTop, false); database.add(image, Columns::BelowNewTags, false); From 1b23b0c6a14b8ad4457b97df719595a9f8634cba Mon Sep 17 00:00:00 2001 From: dayfine Date: Thu, 5 Nov 2026 22:19:48 -0800 Subject: [PATCH 5/7] BDSP MenuDetector: label namespace closing braces --- .../PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp index ef90664b86..d27408f319 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp @@ -124,7 +124,7 @@ class Test_MenuDetector : public ImageUnitTest{ bool m_expected; }; -} +} // namespace void add_tests_MenuDetector(UnitTestDatabase& database){ // The new game's menu: a "NEW" tag above OPTIONS. Jubilife after the @@ -154,6 +154,6 @@ void add_tests_MenuDetector(UnitTestDatabase& database){ } -} -} -} +} // namespace PokemonBDSP +} // namespace NintendoSwitch +} // namespace PokemonAutomation From 176f793a9cbd1e45b01e24b408a56235f27febdb Mon Sep 17 00:00:00 2001 From: dayfine Date: Sat, 7 Nov 2026 11:51:38 -0800 Subject: [PATCH 6/7] BDSP detectors: use explicit image test cases and current style --- .../Inference/PokemonBDSP_DialogDetector.cpp | 68 ++++++--- .../Inference/PokemonBDSP_DialogDetector.h | 6 +- .../Inference/PokemonBDSP_MenuDetector.cpp | 131 ++++++++++++------ .../Inference/PokemonBDSP_MenuDetector.h | 8 +- 4 files changed, 143 insertions(+), 70 deletions(-) diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp index 17194476f8..d3cbe175b1 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.cpp @@ -4,9 +4,12 @@ * */ +#include +#include +#include +#include "Common/Cpp/TestRunners/UnitTestDatabase.h" #include "CommonFramework/VideoPipeline/VideoOverlay.h" #include "CommonTools/Images/SolidColorTest.h" -#include "Common/Cpp/TestRunners/UnitTestDatabase.h" #include "Tests/ImageUnitTest.h" #include "PokemonBDSP_DialogDetector.h" @@ -33,6 +36,7 @@ void ShortDialogDetector::make_overlays(VideoOverlaySet& items) const{ items.add(m_color, m_left_white); items.add(m_color, m_left); items.add(m_color, m_right_white); + items.add(m_color, m_right_white_low); items.add(m_color, m_right); } bool ShortDialogDetector::detect(const ImageViewRGB32& screen){ @@ -44,7 +48,7 @@ bool ShortDialogDetector::detect(const ImageViewRGB32& screen){ ImageStats right_white = image_stats(extract_box_reference(screen, m_right_white)); // cout << right_white.average << right_white.stddev << endl; // A long first line runs into m_right_white (Rowan's "Now, go on, and - // leap into the world of Pokemon!"): then the box's right end below it. + // leap into the world of Pokemon!"). Check below that line as well. if (!is_white(right_white) && !is_white(image_stats(extract_box_reference(screen, m_right_white_low)))){ return false; } @@ -132,37 +136,61 @@ bool ShortDialogPromptDetector::process_frame(const ImageViewRGB32& screen, Wall +namespace{ + +// The captured screen supplied to the short-dialog detector. +struct DialogInput{ + std::string_view image; +}; +// Whether a short dialog should be visible in that screen. +struct DialogExpectation{ + bool detected = false; +}; +// One image and its independently specified expected result. +struct DialogCase{ + DialogInput input; + DialogExpectation expectation; +}; + +// Runs the detector on the original image and the image-test color distortions. class Test_ShortDialogDetector : public ImageUnitTest{ public: - Test_ShortDialogDetector(const std::string& image, bool expected) - : ImageUnitTest("PokemonBDSP::ShortDialogDetector - " + image, {image}) - , m_expected(expected) + explicit Test_ShortDialogDetector(const DialogCase& test) + : ImageUnitTest(std::format("PokemonBDSP::ShortDialogDetector - {}", test.input.image), {std::string(test.input.image)}) + , m_expectation(test.expectation) {} virtual UnitTestResult run_on_images( Logger&, CancellableScope&, const std::vector& images ) const override{ - return ShortDialogDetector().detect(images[0]) == m_expected; + const bool detected = ShortDialogDetector().detect(images[0]); + if (detected != m_expectation.detected){ + return std::format("Expected short dialog detected={}, read {}.", m_expectation.detected, detected); + } + return true; } private: - bool m_expected; + DialogExpectation m_expectation; }; +} // namespace + void add_tests_DialogDetector(UnitTestDatabase& database){ - database.add("PokemonBDSP/DialogDetector/IntroRowanNameTab_True.png", true); - database.add("PokemonBDSP/DialogDetector/IntroRowanNoArrow_True.png", true); - // A short line: the box's right end is white where the old check looks. - database.add("PokemonBDSP/DialogDetector/IntroRowanShortLine_True.png", true); - // A "???" name tab, in Jubilife's yellow plaza after the tag battle. - database.add("PokemonBDSP/DialogDetector/JubilifeUnknownNameTab_True.png", true); - // One line, no name tab, over Floaroma's flowers. - database.add("PokemonBDSP/DialogDetector/FloaromaOldManOneLine_True.png", true); - database.add("PokemonBDSP/DialogDetector/FloaromaCloudShadows_False.png", false); + static constexpr std::array CASES{{ + {.input = {.image = "PokemonBDSP/DialogDetector/IntroRowanNameTab_True.png"}, .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/DialogDetector/IntroRowanNoArrow_True.png"}, .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/DialogDetector/IntroRowanShortLine_True.png"}, .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/DialogDetector/JubilifeUnknownNameTab_True.png"}, .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/DialogDetector/FloaromaOldManOneLine_True.png"}, .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/DialogDetector/FloaromaCloudShadows_False.png"}, .expectation = {.detected = false}}, + }}; + for (const DialogCase& test : CASES){ + database.add(test); + } } - -} -} -} +} // namespace PokemonBDSP +} // namespace NintendoSwitch +} // namespace PokemonAutomation diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h index f1f98bcc3a..00d4b4eb1f 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h @@ -85,7 +85,7 @@ class ShortDialogPromptDetector : public VisualInferenceCallback{ // The screenshot tests in CommandLineTests/PokemonBDSP/DialogDetector/. void add_tests_DialogDetector(UnitTestDatabase& database); -} -} -} +} // namespace PokemonBDSP +} // namespace NintendoSwitch +} // namespace PokemonAutomation #endif diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp index d27408f319..6d23f8fb83 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.cpp @@ -4,10 +4,13 @@ * */ +#include +#include +#include +#include "Common/Cpp/TestRunners/UnitTestDatabase.h" #include "CommonFramework/ImageTools/ImageStats.h" #include "CommonFramework/VideoPipeline/VideoOverlayScopes.h" #include "CommonTools/Images/SolidColorTest.h" -#include "Common/Cpp/TestRunners/UnitTestDatabase.h" #include "Tests/ImageUnitTest.h" #include "PokemonBDSP_MenuDetector.h" @@ -16,20 +19,23 @@ namespace NintendoSwitch{ namespace PokemonBDSP{ -const double STRIPE_TOP = 0.110; -const double STRIPE_TOP_BELOW_BANNER = 0.170; -const double STRIPE_BOTTOM = 0.598; +namespace{ + +constexpr double STRIPE_TOP = 0.110; +constexpr double STRIPE_TOP_BELOW_BANNER = 0.170; +constexpr double STRIPE_BOTTOM = 0.598; // Each column sits in the gap right of an icon, clear of the "NEW" tag on -// its top right corner: from 0.160 the column took the edge of the tag on -// TOWN MAP and the menu went unseen (r65, Jubilife). -const double STRIPE_LEFT = 0.166; +// its top right corner. The tag on TOWN MAP extends into a column at 0.160. +constexpr double STRIPE_LEFT = 0.166; -static ImageFloatBox menu_stripe(size_t index, bool skip_new_banner){ +ImageFloatBox menu_stripe(size_t index, bool skip_new_banner){ double top = skip_new_banner ? STRIPE_TOP_BELOW_BANNER : STRIPE_TOP; return ImageFloatBox(STRIPE_LEFT + 0.166 * (double)index, top, 0.015, STRIPE_BOTTOM - top); } +} // namespace + MenuDetector::MenuDetector(Color color, bool skip_new_banner) : m_color(color) , m_line0(menu_stripe(0, skip_new_banner)) @@ -93,67 +99,106 @@ bool MenuWatcher::process_frame(const ImageViewRGB32& frame, WallClock timestamp namespace{ -// Which columns the detector checks: the default ones from the top, or the -// ones below the "NEW" tags (a new game's menu, skip_new_banner = true). +// Selects full-height columns or columns starting below the NEW tags. enum class Columns{ FromTop, BelowNewTags, }; +// A screen and the existing detector mode used to inspect it. +struct MenuInput{ + std::string_view image; + Columns columns = Columns::FromTop; +}; +// Whether the screen should be recognized as the X menu. +struct MenuExpectation{ + bool detected = false; +}; +// One screen/mode pair and its independently specified expected result. +struct MenuCase{ + MenuInput input; + MenuExpectation expectation; +}; +// Verifies both detector modes, including the image-test color distortions. class Test_MenuDetector : public ImageUnitTest{ public: - Test_MenuDetector(const std::string& image, Columns columns, bool expected) + explicit Test_MenuDetector(const MenuCase& test) : ImageUnitTest( - "PokemonBDSP::MenuDetector" + std::string(columns == Columns::BelowNewTags ? "(skip_new_banner)" : "") + " - " + image, - {image} + std::format("PokemonBDSP::MenuDetector{} - {}", + test.input.columns == Columns::BelowNewTags ? "(skip_new_banner)" : "", test.input.image), + {std::string(test.input.image)} ) - , m_columns(columns) - , m_expected(expected) + , m_columns(test.input.columns) + , m_expectation(test.expectation) {} virtual UnitTestResult run_on_images( Logger&, CancellableScope&, const std::vector& images ) const override{ - MenuDetector detector(COLOR_RED, m_columns == Columns::BelowNewTags); - return detector.detect(images[0]) == m_expected; + MenuDetector detector(COLOR_RED, /*skip_new_banner=*/m_columns == Columns::BelowNewTags); + const bool detected = detector.detect(images[0]); + if (detected != m_expectation.detected){ + return std::format("Expected menu detected={}, read {}.", m_expectation.detected, detected); + } + return true; } private: - Columns m_columns; - bool m_expected; + Columns m_columns = Columns::FromTop; + MenuExpectation m_expectation; }; } // namespace void add_tests_MenuDetector(UnitTestDatabase& database){ - // The new game's menu: a "NEW" tag above OPTIONS. Jubilife after the - // Town Map: one on TOWN MAP, and the quest's text below. - for (const char* image : { - "PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", - "PokemonBDSP/MenuDetector/JubilifeTownMapNew_True.png", - // Three NEW tags (Trainer Card, Town Map, Ball Capsules), cursor on Bag. - "PokemonBDSP/MenuDetector/FloaromaMenuThreeNew_True.png", - }){ - database.add(image, Columns::FromTop, true); - database.add(image, Columns::BelowNewTags, true); - } - for (const char* image : { - "PokemonBDSP/MenuDetector/Home_False.png", - "PokemonBDSP/MenuDetector/OverworldSandgemMart_False.png", - "PokemonBDSP/MenuDetector/OverworldOreburghMine_False.png", - "PokemonBDSP/MenuDetector/IntroDialog_False.png", - "PokemonBDSP/MenuDetector/IntroBarry_False.png", - "PokemonBDSP/MenuDetector/FloaromaOldManDialog_False.png", - // Cloud shadows over Floaroma's flower field. - "PokemonBDSP/MenuDetector/FloaromaCloudShadows_False.png", - }){ - database.add(image, Columns::FromTop, false); - database.add(image, Columns::BelowNewTags, false); + static constexpr std::array CASES{{ + {.input = {.image = "PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", .columns = Columns::FromTop}, + .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/MenuDetector/NewGameBagOptions_True.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/MenuDetector/JubilifeTownMapNew_True.png", .columns = Columns::FromTop}, + .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/MenuDetector/JubilifeTownMapNew_True.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/MenuDetector/FloaromaMenuThreeNew_True.png", .columns = Columns::FromTop}, + .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/MenuDetector/FloaromaMenuThreeNew_True.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = true}}, + {.input = {.image = "PokemonBDSP/MenuDetector/Home_False.png", .columns = Columns::FromTop}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/Home_False.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/OverworldSandgemMart_False.png", .columns = Columns::FromTop}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/OverworldSandgemMart_False.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/OverworldOreburghMine_False.png", .columns = Columns::FromTop}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/OverworldOreburghMine_False.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/IntroDialog_False.png", .columns = Columns::FromTop}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/IntroDialog_False.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/IntroBarry_False.png", .columns = Columns::FromTop}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/IntroBarry_False.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/FloaromaOldManDialog_False.png", .columns = Columns::FromTop}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/FloaromaOldManDialog_False.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/FloaromaCloudShadows_False.png", .columns = Columns::FromTop}, + .expectation = {.detected = false}}, + {.input = {.image = "PokemonBDSP/MenuDetector/FloaromaCloudShadows_False.png", .columns = Columns::BelowNewTags}, + .expectation = {.detected = false}}, + }}; + for (const MenuCase& test : CASES){ + database.add(test); } } - } // namespace PokemonBDSP } // namespace NintendoSwitch } // namespace PokemonAutomation diff --git a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h index b605bfd3cf..e084f66d3e 100644 --- a/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h +++ b/SerialPrograms/Source/PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h @@ -7,11 +7,11 @@ #ifndef PokemonAutomation_PokemonBDSP_MenuDetector_H #define PokemonAutomation_PokemonBDSP_MenuDetector_H +#include "Common/Cpp/TestRunners/UnitTestDatabase.h" #include "Common/Cpp/Color.h" #include "CommonFramework/ImageTools/ImageBoxes.h" #include "CommonTools/InferenceCallbacks/VisualInferenceCallback.h" #include "CommonTools/VisualDetector.h" -#include "Common/Cpp/TestRunners/UnitTestDatabase.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -50,7 +50,7 @@ class MenuWatcher : public MenuDetector, public VisualInferenceCallback{ // The screenshot tests in CommandLineTests/PokemonBDSP/MenuDetector/. void add_tests_MenuDetector(UnitTestDatabase& database); -} -} -} +} // namespace PokemonBDSP +} // namespace NintendoSwitch +} // namespace PokemonAutomation #endif From cd4cf052b3ab207efde7f6bcd6eee9cfa7d50eb2 Mon Sep 17 00:00:00 2001 From: dayfine Date: Mon, 9 Nov 2026 03:01:14 -0800 Subject: [PATCH 7/7] BDSP: register the dialog and menu detector tests with the BDSP tests Keeps make_UNIT_TESTS_ALL() within code health's 50 lines now that the shared AutoStory tests add two registrations there. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_013VUubhHeYSMGSSDXZkKZss --- .../ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp | 4 ---- SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp b/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp index 539ca6dbbe..f59cae9afd 100644 --- a/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp +++ b/SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp @@ -35,8 +35,6 @@ #include "Pokemon/AutoStory/Pokemon_AutoStory_Tests.h" #include "Pokemon/Navigation/Pokemon_Navigation_Tests.h" #include "Pokemon/Navigation/Pokemon_RuntimeMap_Tests.h" -#include "PokemonBDSP/Inference/PokemonBDSP_DialogDetector.h" -#include "PokemonBDSP/Inference/PokemonBDSP_MenuDetector.h" #include "PokemonBDSP/PokemonBDSP_Tests.h" #include "VideoGames/PokemonHome/PokemonHome_Tests.h" #include "VideoGames/PokemonSwSh/PokemonSwSh_Tests.h" @@ -95,8 +93,6 @@ UnitTestDatabase make_UNIT_TESTS_ALL(){ NintendoSwitch::add_tests_UpdatePopupDetector(ret); NintendoSwitch::Typewriter::add_tests_TypewriterTextMapping(ret); NintendoSwitch::PokemonBDSP::add_tests(ret); - NintendoSwitch::PokemonBDSP::add_tests_DialogDetector(ret); - NintendoSwitch::PokemonBDSP::add_tests_MenuDetector(ret); NintendoSwitch::PokemonFRLG::add_tests(ret); NintendoSwitch::PokemonRSE::add_tests(ret); NintendoSwitch::PokemonHome::add_tests(ret); diff --git a/SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp b/SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp index 145096b969..7f5bca1843 100644 --- a/SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp +++ b/SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp @@ -5,6 +5,8 @@ */ #include "Common/Cpp/TestRunners/UnitTestDatabase.h" +#include "Inference/PokemonBDSP_DialogDetector.h" +#include "Inference/PokemonBDSP_MenuDetector.h" #include "Programs/AutoStory/PokemonBDSP_AutoStory_Maps_Tests.h" #include "Programs/AutoStory/PokemonBDSP_AutoStory_PlannedRoute_Tests.h" #include "Programs/AutoStory/PokemonBDSP_AutoStory_RoutePlanner_Tests.h" @@ -21,6 +23,8 @@ namespace PokemonBDSP{ void add_tests(UnitTestDatabase& database){ + add_tests_DialogDetector(database); + add_tests_MenuDetector(database); add_tests_AutoStory_Maps(database); add_tests_AutoStory_StoryWorld(database); add_tests_AutoStory_RoutePlanner(database);