From 004b6a1323a81dbdd704375965099bb48e6db2d7 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Thu, 25 Mar 2021 00:21:41 -0500 Subject: [PATCH 01/13] Source dump for latest beta: - Add all the autonomous shiny hunting programs. - Feedback SR'ing for several programs. - Numerous bug fixes. --- ClientSource/Programs/BeamReset.cpp | 2 +- Common/Clientside/SpinLock.h | 10 +- Common/Clientside/Unicode.cpp | 3 +- Common/MessageProtocol.h | 4 +- Common/PokemonSwSh/PokemonSwShAutoHosts.cpp | 7 +- Common/PokemonSwSh/PokemonSwShAutoHosts.h | 25 +- Common/PokemonSwSh/PokemonSwShDateSpam.cpp | 8 +- Common/PokemonSwSh/PokemonSwShDateSpam.h | 5 +- Common/PokemonSwSh/PokemonSwShGameEntry.cpp | 31 +- Common/PokemonSwSh/PokemonSwShGameEntry.h | 4 +- GeneratorSource/Source/Main.cpp | 19 +- .../Source/Tools/CommandRunner.cpp | 10 +- .../Source/Tools/PersistentSettings.cpp | 2 +- SerialPrograms/SerialPrograms.pro | 49 +++ .../Source/CommonFramework/Globals.cpp | 2 +- .../Inference/AnomalyDetector.cpp | 124 ++++++ .../Inference/AnomalyDetector.h | 70 ++++ .../Inference/BlackScreenDetector.cpp | 13 +- .../Inference/ColorClustering.cpp | 168 ++++++++ .../Inference/ColorClustering.h | 50 +++ .../Inference/FillGeometry.cpp | 170 ++++++++ .../CommonFramework/Inference/FillGeometry.h | 52 +++ .../CommonFramework/Inference/FillMatrix.cpp | 63 +++ .../CommonFramework/Inference/FillMatrix.h | 101 +++++ .../Inference/FloatStatAccumulator.h | 49 +++ .../CommonFramework/Inference/ImageTools.cpp | 36 +- .../CommonFramework/Inference/ImageTools.h | 5 + .../Inference/TimeWindowStatTracker.h | 135 +++++++ .../Programs/FriendCodeAdder.cpp | 9 +- SerialPrograms/Source/PanelList.cpp | 16 + .../PokemonSwSh_BattleMenuDetector.cpp | 210 ++++++++++ .../PokemonSwSh_BattleMenuDetector.h | 41 ++ .../Inference/PokemonSwSh_BeamSetter.cpp | 14 +- .../Inference/PokemonSwSh_BeamSetter.h | 10 +- .../Inference/PokemonSwSh_FishingDetector.cpp | 197 +++++++++ .../Inference/PokemonSwSh_FishingDetector.h | 54 +++ .../PokemonSwSh_RaidCatchDetector.cpp | 74 ++++ .../Inference/PokemonSwSh_RaidCatchDetector.h | 43 ++ .../PokemonSwSh_StartBattleDetector.cpp | 52 +++ .../PokemonSwSh_StartBattleDetector.h | 40 ++ ...PokemonSwSh_SummaryShinySymbolDetector.cpp | 124 ++++++ .../PokemonSwSh_SummaryShinySymbolDetector.h | 54 +++ .../PokemonSwSh_ShinyEncounterDetector.cpp | 146 +++++++ .../PokemonSwSh_ShinyEncounterDetector.h | 57 +++ .../ShinyDetection/PokemonSwSh_ShinyFilters.h | 36 ++ .../PokemonSwSh_ShinyTrigger.cpp | 131 ++++++ .../ShinyDetection/PokemonSwSh_ShinyTrigger.h | 44 +++ .../PokemonSwSh_SparkleTrigger.cpp | 252 ++++++++++++ .../PokemonSwSh_SparkleTrigger.h | 51 +++ .../PokemonSwSh_SquareTrigger.cpp | 374 ++++++++++++++++++ .../PokemonSwSh_SquareTrigger.h | 34 ++ .../DenHunting/PokemonSwSh_BeamReset.cpp | 2 +- .../PokemonSwSh_PurpleBeamFinder.cpp | 62 ++- .../PokemonSwSh_AutoHost-MultiGame.cpp | 4 +- .../Hosting/PokemonSwSh_AutoHost-Rolling.cpp | 5 +- .../Hosting/PokemonSwSh_DenRoller.cpp | 9 +- .../Programs/Hosting/PokemonSwSh_DenTools.h | 4 +- .../Programs/PokemonSwSh_StartGame.cpp | 203 ++++++++++ .../Programs/PokemonSwSh_StartGame.h | 45 +++ .../PokemonSwSh_FriendSearchDisconnect.cpp | 5 +- .../PokemonSwSh_EncounterStats.cpp | 71 ++++ .../ShinyHunting/PokemonSwSh_EncounterStats.h | 48 +++ .../PokemonSwSh_EncounterTracker.cpp | 63 +++ .../PokemonSwSh_EncounterTracker.h | 44 +++ ...emonSwSh_ShinyHuntAutonomous-BerryTree.cpp | 138 +++++++ ...okemonSwSh_ShinyHuntAutonomous-BerryTree.h | 41 ++ ...okemonSwSh_ShinyHuntAutonomous-Fishing.cpp | 156 ++++++++ .../PokemonSwSh_ShinyHuntAutonomous-Fishing.h | 46 +++ ...kemonSwSh_ShinyHuntAutonomous-IoATrade.cpp | 129 ++++++ ...PokemonSwSh_ShinyHuntAutonomous-IoATrade.h | 41 ++ .../PokemonSwSh_ShinyHuntAutonomous-Regi.cpp | 139 +++++++ .../PokemonSwSh_ShinyHuntAutonomous-Regi.h | 46 +++ ...monSwSh_ShinyHuntAutonomous-Regigigas2.cpp | 175 ++++++++ ...kemonSwSh_ShinyHuntAutonomous-Regigigas2.h | 62 +++ ...onSwSh_ShinyHuntAutonomous-StrongSpawn.cpp | 122 ++++++ ...emonSwSh_ShinyHuntAutonomous-StrongSpawn.h | 47 +++ ...Sh_ShinyHuntAutonomous-SwordsOfJustice.cpp | 134 +++++++ ...SwSh_ShinyHuntAutonomous-SwordsOfJustice.h | 44 +++ .../PokemonSwSh/Programs/TestProgram.cpp | 133 ++++++- 79 files changed, 5193 insertions(+), 105 deletions(-) create mode 100644 SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.cpp create mode 100644 SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.h create mode 100644 SerialPrograms/Source/CommonFramework/Inference/ColorClustering.cpp create mode 100644 SerialPrograms/Source/CommonFramework/Inference/ColorClustering.h create mode 100644 SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp create mode 100644 SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h create mode 100644 SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp create mode 100644 SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h create mode 100644 SerialPrograms/Source/CommonFramework/Inference/FloatStatAccumulator.h create mode 100644 SerialPrograms/Source/CommonFramework/Inference/TimeWindowStatTracker.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h diff --git a/ClientSource/Programs/BeamReset.cpp b/ClientSource/Programs/BeamReset.cpp index d6b78a2103..91de28232c 100644 --- a/ClientSource/Programs/BeamReset.cpp +++ b/ClientSource/Programs/BeamReset.cpp @@ -64,7 +64,7 @@ void program_BeamReset(const std::string& device_name){ } pbf_wait(DELAY_BEFORE_RESET); - reset_game_from_home(TOLERATE_SYSTEM_UPDATE_MENU_SLOW, 0, false); + reset_game_from_home(TOLERATE_SYSTEM_UPDATE_MENU_SLOW); } end_program_callback(); diff --git a/Common/Clientside/SpinLock.h b/Common/Clientside/SpinLock.h index a93e7bbbe9..edc8035288 100644 --- a/Common/Clientside/SpinLock.h +++ b/Common/Clientside/SpinLock.h @@ -9,7 +9,9 @@ #include #include +#if _WIN32 #include +#endif #include using std::cout; @@ -32,6 +34,7 @@ class SpinLock{ } } void spin_acquire(const char* label){ +#if _WIN32 uint64_t start = __rdtsc(); while (true){ bool state = false; @@ -45,6 +48,9 @@ class SpinLock{ start = __rdtsc(); } } +#else + spin_acquire(); +#endif } void unlock(){ @@ -63,7 +69,7 @@ class SpinLockGuard{ SpinLockGuard(SpinLock& lock, const char* label) : m_lock(lock) - , m_label(label) +// , m_label(label) { lock.spin_acquire(label); } @@ -73,7 +79,7 @@ class SpinLockGuard{ private: SpinLock& m_lock; - const char* m_label; +// const char* m_label; }; diff --git a/Common/Clientside/Unicode.cpp b/Common/Clientside/Unicode.cpp index ff33241d04..fbe728a003 100644 --- a/Common/Clientside/Unicode.cpp +++ b/Common/Clientside/Unicode.cpp @@ -94,7 +94,8 @@ std::u16string utf8_to_utf16(const std::string& str){ #ifdef _WIN32 std::wstring utf8_to_wstr(const std::string& str){ - return std::wstring(str.begin(), str.end()); + std::u16string tmp(utf8_to_utf16(str)); + return std::wstring(tmp.begin(), tmp.end()); } #endif diff --git a/Common/MessageProtocol.h b/Common/MessageProtocol.h index 511478c5a1..b4b3acb054 100644 --- a/Common/MessageProtocol.h +++ b/Common/MessageProtocol.h @@ -153,10 +153,10 @@ // (version / 100) must be the same on both server and client. // (version % 100) can be higher on server than client. // -#define PABB_PROTOCOL_VERSION 2021030200 +#define PABB_PROTOCOL_VERSION 2021032200 // Program versioning doesn't matter. It's just for informational purposes. -#define PABB_PROGRAM_VERSION 2021031900 +#define PABB_PROGRAM_VERSION 2021032200 #define PABB_BAUD_RATE 115200 #define PABB_RETRANSMIT_DELAY_MILLIS 80 diff --git a/Common/PokemonSwSh/PokemonSwShAutoHosts.cpp b/Common/PokemonSwSh/PokemonSwShAutoHosts.cpp index 79d66f2e30..b24f91329b 100644 --- a/Common/PokemonSwSh/PokemonSwShAutoHosts.cpp +++ b/Common/PokemonSwSh/PokemonSwShAutoHosts.cpp @@ -25,12 +25,13 @@ void connect_to_internet( params.connect_to_internet_delay = connect_to_internet_delay; device.issue_request(params); } -void home_to_add_friends(uint8_t user_slot, bool fix_cursor){ - home_to_add_friends(*global_connection, user_slot, fix_cursor); +void home_to_add_friends(uint8_t user_slot, uint8_t scroll_down, bool fix_cursor){ + home_to_add_friends(*global_connection, user_slot, scroll_down, fix_cursor); } -void home_to_add_friends(BotBase& device, uint8_t user_slot, bool fix_cursor){ +void home_to_add_friends(BotBase& device, uint8_t user_slot, uint8_t scroll_down, bool fix_cursor){ pabb_home_to_add_friends params; params.user_slot = user_slot; + params.scroll_down = scroll_down; params.fix_cursor = fix_cursor; device.issue_request(params); } diff --git a/Common/PokemonSwSh/PokemonSwShAutoHosts.h b/Common/PokemonSwSh/PokemonSwShAutoHosts.h index 27140014e2..2ef7e7d71d 100644 --- a/Common/PokemonSwSh/PokemonSwShAutoHosts.h +++ b/Common/PokemonSwSh/PokemonSwShAutoHosts.h @@ -19,8 +19,15 @@ //////////////////////////////////////////////////////////////////////////////// // Universal #ifdef __cplusplus -void connect_to_internet(uint16_t open_ycomm_delay, uint16_t connect_to_internet_delay); -void home_to_add_friends(uint8_t user_slot, bool fix_cursor); +void connect_to_internet( + uint16_t open_ycomm_delay, + uint16_t connect_to_internet_delay +); +void home_to_add_friends( + uint8_t user_slot, + uint8_t scroll_down, + bool fix_cursor +); uint16_t accept_FRs( uint8_t slot, bool fix_cursor, uint16_t game_to_home_delay_safe, @@ -43,8 +50,17 @@ void accept_FRs_while_waiting( namespace PokemonAutomation{ class BotBase; } -void connect_to_internet(PokemonAutomation::BotBase& device, uint16_t open_ycomm_delay, uint16_t connect_to_internet_delay); -void home_to_add_friends(PokemonAutomation::BotBase& device, uint8_t user_slot, bool fix_cursor); +void connect_to_internet( + PokemonAutomation::BotBase& device, + uint16_t open_ycomm_delay, + uint16_t connect_to_internet_delay +); +void home_to_add_friends( + PokemonAutomation::BotBase& device, + uint8_t user_slot, + uint8_t scroll_down, + bool fix_cursor +); uint16_t accept_FRs( PokemonAutomation::BotBase& device, uint8_t slot, bool fix_cursor, @@ -84,6 +100,7 @@ typedef struct{ typedef struct{ seqnum_t seqnum; uint8_t user_slot; + uint8_t scroll_down; bool fix_cursor; } PABB_PACK pabb_home_to_add_friends; diff --git a/Common/PokemonSwSh/PokemonSwShDateSpam.cpp b/Common/PokemonSwSh/PokemonSwShDateSpam.cpp index 13712ce5b1..4859b03d19 100644 --- a/Common/PokemonSwSh/PokemonSwShDateSpam.cpp +++ b/Common/PokemonSwSh/PokemonSwShDateSpam.cpp @@ -12,11 +12,12 @@ using namespace PokemonAutomation; -void home_to_date_time(bool fast){ - home_to_date_time(*global_connection, fast); +void home_to_date_time(bool to_date_change, bool fast){ + home_to_date_time(*global_connection, to_date_change, fast); } -void home_to_date_time(BotBase& device, bool fast){ +void home_to_date_time(BotBase& device, bool to_date_change, bool fast){ pabb_home_to_date_time params; + params.to_date_change = to_date_change; params.fast = fast; device.issue_request(params); } @@ -93,6 +94,7 @@ int register_message_converters_pokemon_date_spam(){ if (body.size() != sizeof(pabb_home_to_date_time)){ ss << "(invalid size)" << std::endl; return ss.str(); } const auto* params = (const pabb_home_to_date_time*)body.c_str(); ss << "seqnum = " << (uint64_t)params->seqnum; + ss << ", to_date_change = " << params->to_date_change; ss << ", fast = " << params->fast; return ss.str(); } diff --git a/Common/PokemonSwSh/PokemonSwShDateSpam.h b/Common/PokemonSwSh/PokemonSwShDateSpam.h index ca6c5bedcb..70af1baf3f 100644 --- a/Common/PokemonSwSh/PokemonSwShDateSpam.h +++ b/Common/PokemonSwSh/PokemonSwShDateSpam.h @@ -26,7 +26,7 @@ // date change button. // If (fast = true), it will run slightly faster, but has a higher chance of // not making all way in. Do this only if the program is able to self-recover. -void home_to_date_time(bool fast); +void home_to_date_time(bool to_date_change, bool fast); // Call this immediately after calling "home_to_date_time()". // This function will roll the 1st and 3rd slots forward by one. @@ -79,7 +79,7 @@ void rollback_hours_from_home(uint8_t hours, uint16_t settings_to_home_delay); namespace PokemonAutomation{ class BotBase; } -void home_to_date_time (PokemonAutomation::BotBase& device, bool fast); +void home_to_date_time (PokemonAutomation::BotBase& device, bool to_date_change, bool fast); void roll_date_forward_1 (PokemonAutomation::BotBase& device, bool fast); void roll_date_backward_N (PokemonAutomation::BotBase& device, uint8_t skips, bool fast); void home_roll_date_enter_game (PokemonAutomation::BotBase& device, bool rollback_year); @@ -103,6 +103,7 @@ void rollback_hours_from_home (PokemonAutomation::BotBase& device, #define PABB_MSG_COMMAND_HOME_TO_DATE_TIME 0xb4 typedef struct{ seqnum_t seqnum; + bool to_date_change; bool fast; } PABB_PACK pabb_home_to_date_time; diff --git a/Common/PokemonSwSh/PokemonSwShGameEntry.cpp b/Common/PokemonSwSh/PokemonSwShGameEntry.cpp index ff09240986..663e790f5c 100644 --- a/Common/PokemonSwSh/PokemonSwShGameEntry.cpp +++ b/Common/PokemonSwSh/PokemonSwShGameEntry.cpp @@ -73,24 +73,11 @@ void fast_reset_game( device.issue_request(params); } -void reset_game_from_home(bool tolerate_update_menu, uint8_t user_slot, bool game_slot2){ - reset_game_from_home(*global_connection, tolerate_update_menu, user_slot, game_slot2); +void reset_game_from_home(bool tolerate_update_menu){ + reset_game_from_home(*global_connection, tolerate_update_menu); } -void reset_game_from_home(BotBase& device, bool tolerate_update_menu, uint8_t user_slot, bool game_slot2){ -#if 0 - pabb_reset_game_from_home params; - params.tolerate_update_menu = tolerate_update_menu; - params.user_slot = user_slot; - params.game_slot2 = game_slot2; - device.issue_request(params); -#else - // If "user_slot" is 0, start whatever the current user is. - // If "game_slot2" is true, start the game in the 2nd slot instead of the 1st slot. - - if (!START_GAME_REQUIRES_INTERNET && - !tolerate_update_menu && - user_slot == 0 && !game_slot2 - ){ +void reset_game_from_home(BotBase& device, bool tolerate_update_menu){ + if (!START_GAME_REQUIRES_INTERNET && !tolerate_update_menu){ fast_reset_game( device, START_GAME_MASH, START_GAME_WAIT, @@ -100,15 +87,7 @@ void reset_game_from_home(BotBase& device, bool tolerate_update_menu, uint8_t us } close_game(device); - - start_game_from_home( - device, - tolerate_update_menu, - game_slot2 ? 2 : 0, - user_slot, - false - ); -#endif + start_game_from_home(device, tolerate_update_menu, 0, 0, false); } void settings_to_enter_game(bool fast){ diff --git a/Common/PokemonSwSh/PokemonSwShGameEntry.h b/Common/PokemonSwSh/PokemonSwShGameEntry.h index 2967f20424..f085d4d02c 100644 --- a/Common/PokemonSwSh/PokemonSwShGameEntry.h +++ b/Common/PokemonSwSh/PokemonSwShGameEntry.h @@ -73,7 +73,7 @@ void fast_reset_game( // From the Switch home menu, close the game and open another game. // If "user_slot" is 0, start whatever the current user is. // If "game_slot2" is true, start the game in the 2nd slot instead of the 1st slot. -void reset_game_from_home(bool tolerate_update_menu, uint8_t user_slot, bool game_slot2); +void reset_game_from_home(bool tolerate_update_menu); #endif //////////////////////////////////////////////////////////////////////////////// @@ -98,7 +98,7 @@ void fast_reset_game( uint16_t start_game_mash, uint16_t start_game_wait, uint16_t enter_game_mash, uint16_t enter_game_wait ); -void reset_game_from_home (PokemonAutomation::BotBase& device, bool tolerate_update_menu, uint8_t user_slot, bool game_slot2); +void reset_game_from_home (PokemonAutomation::BotBase& device, bool tolerate_update_menu); #endif //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// diff --git a/GeneratorSource/Source/Main.cpp b/GeneratorSource/Source/Main.cpp index bff7f37141..dae2398eb9 100644 --- a/GeneratorSource/Source/Main.cpp +++ b/GeneratorSource/Source/Main.cpp @@ -31,7 +31,24 @@ int main(int argc, char *argv[]) if (!QDir(settings.path + CONFIG_FOLDER_NAME).exists()){ QMessageBox box; - box.critical(nullptr, "Error", "Unable to find source directory.\r\nPlease unzip the package if you haven't already."); + box.critical( + nullptr, "Error", + "Unable to find source directory.\r\nPlease unzip the package if you haven't already." + ); + } + + for (QChar ch : settings.path){ + if (ch.unicode() >= 128){ + QMessageBox box; + box.critical( + nullptr, "Error", + "Unicode characters found in the path name. Please move the folder to a place with only English characters.\r\n" + "GNU Make does not work with filepaths containing non-ASCII characters.\r\n" + "\r\n" + "Path: " + settings.path + ); + break; + } } MainWindow w; diff --git a/GeneratorSource/Source/Tools/CommandRunner.cpp b/GeneratorSource/Source/Tools/CommandRunner.cpp index 23cf971949..941ac64c5f 100644 --- a/GeneratorSource/Source/Tools/CommandRunner.cpp +++ b/GeneratorSource/Source/Tools/CommandRunner.cpp @@ -37,9 +37,10 @@ int build_hexfile( si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); QString command = module + " " + board.c_str() + " " + program_name + " > \"" + log_file + "\" 2>&1"; + std::wstring wpath = utf8_to_wstr(command.toUtf8().data()); bool ret = CreateProcessW( nullptr, - &utf8_to_wstr(command.toUtf8().data())[0], + &wpath[0], nullptr, nullptr, false, @@ -50,11 +51,12 @@ int build_hexfile( &pi ); if (!ret){ - ret = GetLastError(); - std::cout << "error = " << ret << std::endl; + DWORD code = GetLastError(); + std::cout << "error = " << code << std::endl; run_on_main_thread([=]{ QMessageBox box; - box.critical(nullptr, "Error", "Unable to open: " + module); + box.critical(nullptr, "Error", "Unable to open: " + QString::fromWCharArray(wpath.data()) + "\r\nError Code: " + QString::number(code)); +// box.critical(nullptr, "Error", "Unable to open: " + module + "\r\nError Code: " + QString::number(code)); }); return 1; } diff --git a/GeneratorSource/Source/Tools/PersistentSettings.cpp b/GeneratorSource/Source/Tools/PersistentSettings.cpp index 1b98480a4c..637aea98d1 100644 --- a/GeneratorSource/Source/Tools/PersistentSettings.cpp +++ b/GeneratorSource/Source/Tools/PersistentSettings.cpp @@ -20,7 +20,7 @@ using std::endl; namespace PokemonAutomation{ -const QString VERSION = "v0.3.9"; +const QString VERSION = "v0.4.0"; const QString DISCORD = "https://discord.gg/cQ4gWxN"; const QString GITHUB_REPO = "https://github.com/PokemonAutomation/SwSh-Arduino"; diff --git a/SerialPrograms/SerialPrograms.pro b/SerialPrograms/SerialPrograms.pro index bccdc1df21..d5e9ba4f1d 100644 --- a/SerialPrograms/SerialPrograms.pro +++ b/SerialPrograms/SerialPrograms.pro @@ -59,7 +59,11 @@ SOURCES += \ ../Common/SwitchFramework/Switch_PushButtons.cpp \ ../Common/SwitchRoutines/SwitchDigitEntry.cpp \ Source/CommonFramework/Globals.cpp \ + Source/CommonFramework/Inference/AnomalyDetector.cpp \ Source/CommonFramework/Inference/BlackScreenDetector.cpp \ + Source/CommonFramework/Inference/ColorClustering.cpp \ + Source/CommonFramework/Inference/FillGeometry.cpp \ + Source/CommonFramework/Inference/FillMatrix.cpp \ Source/CommonFramework/Inference/ImageTools.cpp \ Source/CommonFramework/Main.cpp \ Source/CommonFramework/Options/FixedCode.cpp \ @@ -92,8 +96,17 @@ SOURCES += \ Source/NintendoSwitch/Programs/PreventSleep.cpp \ Source/NintendoSwitch/Programs/SwitchViewer.cpp \ Source/PanelList.cpp \ + Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.cpp \ Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp \ + Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp \ + Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.cpp \ Source/PokemonSwSh/Inference/PokemonSwSh_RaidLobbyReader.cpp \ + Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp \ + Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.cpp \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp \ Source/PokemonSwSh/Options/Catchability.cpp \ Source/PokemonSwSh/Options/EggStepCount.cpp \ Source/PokemonSwSh/Options/RegiSelector.cpp \ @@ -104,8 +117,18 @@ SOURCES += \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_SurpriseTrade.cpp \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_TradeBot.cpp \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_TurboA.cpp \ + Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_MultiGameFossil.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHunt-Regi.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp \ @@ -180,10 +203,16 @@ HEADERS += \ ../Common/SwitchFramework/Switch_PushButtons.h \ ../Common/SwitchRoutines/SwitchDigitEntry.h \ Source/CommonFramework/Globals.h \ + Source/CommonFramework/Inference/AnomalyDetector.h \ Source/CommonFramework/Inference/BlackScreenDetector.h \ + Source/CommonFramework/Inference/ColorClustering.h \ + Source/CommonFramework/Inference/FillGeometry.h \ + Source/CommonFramework/Inference/FillMatrix.h \ Source/CommonFramework/Inference/FloatPixel.h \ + Source/CommonFramework/Inference/FloatStatAccumulator.h \ Source/CommonFramework/Inference/ImageTools.h \ Source/CommonFramework/Inference/InferenceTypes.h \ + Source/CommonFramework/Inference/TimeWindowStatTracker.h \ Source/CommonFramework/Options/BooleanCheckBox.h \ Source/CommonFramework/Options/ConfigOption.h \ Source/CommonFramework/Options/FixedCode.h \ @@ -227,8 +256,18 @@ HEADERS += \ Source/NintendoSwitch/Programs/SwitchViewer.h \ Source/NintendoSwitch/Programs/VirtualConsole.h \ Source/PanelList.h \ + Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h \ Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.h \ + Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h \ + Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h \ Source/PokemonSwSh/Inference/PokemonSwSh_RaidLobbyReader.h \ + Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h \ + Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h \ Source/PokemonSwSh/Options/Catchability.h \ Source/PokemonSwSh/Options/EggStepCount.h \ Source/PokemonSwSh/Options/FossilTable.h \ @@ -241,8 +280,18 @@ HEADERS += \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_SurpriseTrade.h \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_TradeBot.h \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_TurboA.h \ + Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_MultiGameFossil.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHunt-Regi.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntTools.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.h \ diff --git a/SerialPrograms/Source/CommonFramework/Globals.cpp b/SerialPrograms/Source/CommonFramework/Globals.cpp index 7c7a29e383..10259a830b 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.cpp +++ b/SerialPrograms/Source/CommonFramework/Globals.cpp @@ -9,7 +9,7 @@ namespace PokemonAutomation{ -const QString VERSION = "v0.4.3"; +const QString VERSION = "v0.4.6"; const QString DISCORD = "https://discord.gg/cQ4gWxN"; const QString GITHUB_REPO = "https://github.com/PokemonAutomation/SwSh-Arduino"; diff --git a/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.cpp b/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.cpp new file mode 100644 index 0000000000..a766aa26e5 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.cpp @@ -0,0 +1,124 @@ +/* Differential Anomaly Detector_H + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include "AnomalyDetector.h" + +namespace PokemonAutomation{ + + +AnomalyDetector::AnomalyDetector(size_t window_size, double min_value, double max_value) + : m_window_size(window_size) + , m_min_value(min_value) + , m_max_value(max_value) + , m_sum(0) + , m_sum_sqr(0) +{ + double range = max_value - min_value; + double max_size = range * range * window_size; + m_scale_to_fixedpoint = std::sqrt(((uint64_t)1 << 63) / max_size); + m_scale_to_double = 1. / m_scale_to_fixedpoint; +} +double AnomalyDetector::push(double x){ + if (m_window.size() >= m_window_size){ + uint32_t fixed = m_window.front(); + m_window.pop_front(); + m_sum -= fixed; + m_sum_sqr -= (uint64_t)fixed * fixed; + } + + x -= m_min_value; + x = std::max(x, 0.); + x = std::min(x, m_max_value - m_min_value); + + uint32_t fixed = (uint32_t)(x * m_scale_to_fixedpoint); + m_window.emplace_back(fixed); + m_sum += fixed; + m_sum_sqr += (uint64_t)fixed * fixed; + + return sigma(x); +} +double AnomalyDetector::mean() const{ + size_t count = m_window.size(); + return m_sum / (double)count * m_scale_to_double + m_min_value; +} +double AnomalyDetector::stddev() const{ + size_t count = m_window.size(); + return std::sqrt((m_sum_sqr - m_sum*m_sum / (double)count) / (count - 1)) * m_scale_to_double; +} +double AnomalyDetector::sigma(double x) const{ + return std::abs((x - mean()) / stddev()); +} + + + +TimeNormalizedDeltaAnomalyDetector::TimeNormalizedDeltaAnomalyDetector(size_t window_size, double max_value) + : m_window_size(window_size) + , m_max_value(max_value) + , m_sum(0) + , m_sum_sqr(0) +{ + double max_size = max_value * max_value * window_size; + m_scale_to_fixedpoint = std::sqrt(((uint64_t)1 << 63) / max_size); + m_scale_to_double = 1. / m_scale_to_fixedpoint; +} +double TimeNormalizedDeltaAnomalyDetector::push( + double x, + std::chrono::time_point timestamp +){ + if (m_window.size() >= m_window_size){ + uint32_t fixed = m_window.front().fixed_point_value; + m_window.pop_front(); + m_sum -= fixed; + m_sum_sqr -= (uint64_t)fixed * fixed; + } + + if (m_window.empty()){ + m_window.emplace_back(); + Frame& frame = m_window.back(); + frame.timestamp = timestamp; + frame.fixed_point_value = 0; + return 0; + } + + auto time_diff = timestamp - m_window.back().timestamp; + uint32_t millis = std::chrono::duration_cast(time_diff).count(); + x /= millis; + x = std::min(x, m_max_value); + + uint32_t fixed = (uint32_t)(x * m_scale_to_fixedpoint); + + m_window.emplace_back(); + Frame& frame = m_window.back(); + frame.timestamp = timestamp; + frame.fixed_point_value = fixed; + m_sum += fixed; + m_sum_sqr += (uint64_t)fixed * fixed; + + +// cout << x << " / " << stddev() << " = " << sigma(x) << endl; +// cout << "count = " << m_window.size() << endl; +// cout << "sum = " << m_sum << endl; +// cout << "m_sum_sqr = " << m_sum_sqr << endl; + return sigma(x); +} + +double TimeNormalizedDeltaAnomalyDetector::mean() const{ + size_t count = m_window.size(); + return m_sum / (double)count * m_scale_to_double; +} +double TimeNormalizedDeltaAnomalyDetector::stddev() const{ + size_t count = m_window.size(); + return std::sqrt((m_sum_sqr - m_sum*m_sum / (double)count) / (count - 1)) * m_scale_to_double; +} +double TimeNormalizedDeltaAnomalyDetector::sigma(double x) const{ + return std::abs((x - mean()) / stddev()); +} + + + +} + diff --git a/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.h b/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.h new file mode 100644 index 0000000000..60afbfe119 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.h @@ -0,0 +1,70 @@ +/* Differential Anomaly Detector_H + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_CommonFramework_DifferentialAnomalyDetector_H +#define PokemonAutomation_CommonFramework_DifferentialAnomalyDetector_H + +#include +#include + +namespace PokemonAutomation{ + + +class AnomalyDetector{ +public: + AnomalyDetector(size_t window_size, double min_value, double max_value); + double push(double x); + + double mean() const; + double stddev() const; + double sigma(double x) const; + +private: + size_t m_window_size; + double m_min_value; + double m_max_value; + double m_scale_to_fixedpoint; + double m_scale_to_double; + uint64_t m_sum; + uint64_t m_sum_sqr; + std::deque m_window; +}; + + +class TimeNormalizedDeltaAnomalyDetector{ +public: + TimeNormalizedDeltaAnomalyDetector(size_t window_size, double max_value); + double push( + double x, + std::chrono::time_point timestamp = std::chrono::system_clock::now() + ); + + double mean() const; + double stddev() const; + double sigma(double x) const; + +private: + struct Frame{ + std::chrono::time_point timestamp; + uint32_t fixed_point_value; + }; + + size_t m_window_size; + double m_max_value; + double m_scale_to_fixedpoint; + double m_scale_to_double; + uint64_t m_sum; + uint64_t m_sum_sqr; + std::deque m_window; +}; + + + + + +} + +#endif diff --git a/SerialPrograms/Source/CommonFramework/Inference/BlackScreenDetector.cpp b/SerialPrograms/Source/CommonFramework/Inference/BlackScreenDetector.cpp index 086bbb283c..c758f4888b 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/BlackScreenDetector.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/BlackScreenDetector.cpp @@ -37,19 +37,19 @@ BlackScreenDetector::BlackScreenDetector( bool BlackScreenDetector::black_is_over(){ - // Grab baseline image. QImage image = m_feed.snapshot(); if (image.isNull()){ m_logger.log("BlackScreenDetector(): Screenshot failed.", "purple"); return false; } - ImageStats stats = pixel_stats(image); - double average = stats.average.sum(); - double stddev = stats.stddev.sum(); +// ImageStats stats = pixel_stats(image); +// double average = stats.average.sum(); +// double stddev = stats.stddev.sum(); // cout << stats.average << endl; - m_logger.log("BlackScreenDetector(): a = " + QString::number(average) + ", s = " + QString::number(stddev), "purple"); - if (average < 100 && stddev < 10){ +// m_logger.log("BlackScreenDetector(): a = " + QString::number(average) + ", s = " + QString::number(stddev), "purple"); +// if (average < 100 && stddev < 10){ + if (is_black(image)){ m_has_been_black = true; return false; } @@ -58,5 +58,4 @@ bool BlackScreenDetector::black_is_over(){ - } diff --git a/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.cpp b/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.cpp new file mode 100644 index 0000000000..a1b6592173 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.cpp @@ -0,0 +1,168 @@ +/* Color Clustering + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include "ColorClustering.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ + +void PixelEuclideanStatAccumulator::clear(){ + m_count = 0; + m_sum_y = 0; + m_sum_z = 0; + m_sqr_x = 0; + m_sqr_y = 0; + m_sqr_z = 0; +} +void PixelEuclideanStatAccumulator::operator+=(FloatPixel pixel){ + m_count++; + m_sum_x += pixel.r; + m_sum_y += pixel.g; + m_sum_z += pixel.b; + m_sqr_x += pixel.r * pixel.r; + m_sqr_y += pixel.g * pixel.g; + m_sqr_z += pixel.b * pixel.b; +} +uint64_t PixelEuclideanStatAccumulator::count() const{ + return m_count; +} +FloatPixel PixelEuclideanStatAccumulator::center() const{ + return FloatPixel(m_sum_x / m_count, m_sum_y / m_count, m_sum_z / m_count); +} +double PixelEuclideanStatAccumulator::deviation() const{ + if (m_count < 1){ + return 0; + } + double variance = m_sqr_x + m_sqr_y + m_sqr_z; + variance -= (m_sum_x*m_sum_x + m_sum_y*m_sum_y + m_sum_z*m_sum_z) / m_count; + return std::sqrt(variance / (m_count - 1)); +} + + +double cluster_fit_2( + const QImage& image, + QRgb color0, PixelEuclideanStatAccumulator& cluster0, + QRgb color1, PixelEuclideanStatAccumulator& cluster1 +){ + int width = image.width(); + int height = image.height(); + + FloatPixel f0(color0); + FloatPixel f1(color1); + + PixelEuclideanStatAccumulator stats0; + PixelEuclideanStatAccumulator stats1; + +// image.save("points.png"); +// std::fstream ss("points.txt", std::fstream::out); + + for (int r = 0; r < height; r++){ + for (int c = 0; c < width; c++){ + QRgb pixel = image.pixel(c, r); +// ss << "{" << qRed(pixel) << ", " << qGreen(pixel) << ", " << qBlue(pixel) << "}," << endl; + FloatPixel p0 = f0 - pixel; + FloatPixel p1 = f1 - pixel; + double distance0 = (p0 * p0).sum(); + double distance1 = (p1 * p1).sum(); + if (distance0 < distance1){ + stats0 += pixel; +// cout << qRed(pixel) << ", " << qGreen(pixel) << ", " << qBlue(pixel) << ": " << std::sqrt(distance0) << endl; + }else{ + stats1 += pixel; +// cout << qRed(pixel) << ", " << qGreen(pixel) << ", " << qBlue(pixel) << ": " << std::sqrt(distance1) << endl; + } + } + } + +#if 0 + cout << "color0 = " << stats0.count() << ": " << stats0.center() << ", " << stats0.deviation() << endl; + cout << "color1 = " << stats1.count() << ": " << stats1.center() << ", " << stats1.deviation() << endl; +#endif + + cluster0 = stats0; + cluster1 = stats1; + + return (stats0.deviation() * stats0.count() + stats1.deviation() * stats1.count()) / (stats0.count() + stats1.count()); +} + +bool cluster_fit_2( + const QImage& image, + QRgb color0, double ratio0, + QRgb color1, double ratio1, + double ratio_threshold, + double deviation_threshold, + double distance_threshold +){ + const size_t NUM_CLUSTERS = 2; + + if (image.isNull()){ + return false; + } + size_t total = (size_t)image.width() * image.height(); + + FloatPixel center_desired[NUM_CLUSTERS] = {color0, color1}; + double count_ratio_desired[NUM_CLUSTERS] = {ratio0, ratio1}; + + PixelEuclideanStatAccumulator cluster[NUM_CLUSTERS]; + double deviation = cluster_fit_2(image, color0, cluster[0], color1, cluster[1]); + color0 = cluster[0].center().round(); + color1 = cluster[1].center().round(); + deviation = cluster_fit_2(image, color0, cluster[0], color1, cluster[1]); +// cout << "deviation = " << deviation << endl; + if (deviation > deviation_threshold){ + return false; + } + + FloatPixel center_actual[NUM_CLUSTERS]; + double count_ratio_actual[NUM_CLUSTERS]; + + double ratio_sum = 0; + for (size_t c = 0; c < NUM_CLUSTERS; c++){ + ratio_sum += count_ratio_desired[c]; + center_actual[c] = cluster[c].center(); + } + for (size_t c = 0; c < NUM_CLUSTERS; c++){ +// cout << cluster[c].count() << ": " << cluster[c].center() << ", " << cluster[c].deviation() << endl; + + count_ratio_desired[c] /= ratio_sum; + count_ratio_actual[c] = (double)cluster[c].count() / total; + double diff = std::abs(count_ratio_actual[c] - count_ratio_desired[c]); +// cout << "Color Ratio Diff: " << diff << endl; + if (diff > ratio_threshold){ +// cout << "Color Ratio Diff is Too Large: " << diff << endl; + return false; + } + + if (center_desired[c].sum() < 50){ + double distance = euclidean_distance(center_desired[c], center_actual[c]) * count_ratio_desired[c]; + if (distance > 40){ +// cout << "Black Distance is Too Large: " << distance << endl; + return false; + } + }else{ + FloatPixel ratio_desired = center_desired[c] / center_desired[c].sum(); + FloatPixel ratio_actual = center_actual[c] / center_actual[c].sum(); + double distance = euclidean_distance(ratio_desired, ratio_actual); +// cout << ratio_desired << " / " << ratio_actual << " = " << distance << endl; + if (distance > distance_threshold){ + return false; + } + } + } + +// cout << "match" << endl; + return true; +} + + + + +} + diff --git a/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.h b/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.h new file mode 100644 index 0000000000..f04bb8b4a6 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.h @@ -0,0 +1,50 @@ +/* Color Clustering + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_CommonFramework_ColorClustering_H +#define PokemonAutomation_CommonFramework_ColorClustering_H + +#include "ImageTools.h" + +namespace PokemonAutomation{ + +class PixelEuclideanStatAccumulator{ +public: + void clear(); + void operator+=(FloatPixel pixel); + + uint64_t count() const; + FloatPixel center() const; + double deviation() const; + +private: + uint64_t m_count = 0; + double m_sum_x = 0; + double m_sum_y = 0; + double m_sum_z = 0; + double m_sqr_x = 0; + double m_sqr_y = 0; + double m_sqr_z = 0; +}; + +double cluster_fit_2( + const QImage& image, + QRgb color0, PixelEuclideanStatAccumulator& cluster0, + QRgb color1, PixelEuclideanStatAccumulator& cluster1 +); + +bool cluster_fit_2( + const QImage& image, + QRgb color0, double ratio0, + QRgb color1, double ratio1, + double ratio_threshold = 0.2, + double deviation_threshold = 50, + double distance_threshold = 0.2 +); + + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp new file mode 100644 index 0000000000..efa579f7f7 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp @@ -0,0 +1,170 @@ +/* Fill Geometry + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + * + * Return a set of connected clusters. + * + */ + +#include "FillGeometry.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ + +bool fill_geometry( + FillGeometry& geometry, + FillMatrix& matrix, + int x, int y, bool allow_diagonal, + FillMatrix::ObjectID id +){ + if (matrix[y][x] != 1){ + return false; + } + + int width = matrix.width(); + int height = matrix.height(); + + int min_x = x; + int max_x = x; + int min_y = y; + int max_y = y; + + size_t area = 0; + uint64_t sum_x = 0; + uint64_t sum_y = 0; + +// int startx = x; +// int starty = y; + + std::vector> queue; + queue.emplace_back(x, y); + matrix[y][x] = id; +// cout << " (|" << starty << "," << startx << " : " << id << " = " << matrix[starty][startx] << ")" << endl; + + while (!queue.empty()){ + x = queue.back().first; + y = queue.back().second; + queue.pop_back(); + + area++; + sum_x += x; + sum_y += y; + + // Load the verticals. + if (y - 1 >= 0){ + FillMatrix::ObjectID& neighbor = matrix[y - 1][x]; + if (neighbor == 1){ + neighbor = id; + queue.emplace_back(x, y - 1); + min_y = std::min(min_y, y - 1); + } + } + if (y + 1 < height){ + FillMatrix::ObjectID& neighbor = matrix[y + 1][x]; + if (neighbor == 1){ + neighbor = id; + queue.emplace_back(x, y + 1); + max_y = std::max(max_y, y + 1); + } + } + if (x - 1 >= 0){ + FillMatrix::ObjectID& neighbor = matrix[y][x - 1]; + if (neighbor == 1){ + neighbor = id; + queue.emplace_back(x - 1, y); + min_x = std::min(min_x, x - 1); + } + } + if (x + 1 < width){ + FillMatrix::ObjectID& neighbor = matrix[y][x + 1]; + if (neighbor == 1){ + neighbor = id; + queue.emplace_back(x + 1, y); + max_x = std::max(max_x, x + 1); + } + } + + if (!allow_diagonal){ + continue; + } + + // If all verticals are blank, try the diagonals. + if (y - 1 >= 0 && x - 1 >= 0){ + FillMatrix::ObjectID& neighbor = matrix[y - 1][x - 1]; + if (neighbor == 1){ + neighbor = id; + queue.emplace_back(x - 1, y - 1); + min_x = std::min(min_x, x - 1); + min_y = std::min(min_y, y - 1); + } + } + if (y - 1 >= 0 && x + 1 < width){ + FillMatrix::ObjectID& neighbor = matrix[y - 1][x + 1]; + if (neighbor == 1){ + neighbor = id; + queue.emplace_back(x + 1, y - 1); + max_x = std::max(max_x, x + 1); + min_y = std::min(min_y, y - 1); + } + } + if (y + 1 < height && x - 1 >= 0){ + FillMatrix::ObjectID& neighbor = matrix[y + 1][x - 1]; + if (neighbor == 1){ + neighbor = id; + queue.emplace_back(x - 1, y + 1); + min_x = std::min(min_x, x - 1); + max_y = std::max(max_y, y + 1); + } + } + if (y + 1 < height && x + 1 < width){ + FillMatrix::ObjectID& neighbor = matrix[y + 1][x + 1]; + if (neighbor == 1){ + neighbor = id; + queue.emplace_back(x + 1, y + 1); + max_x = std::max(max_x, x + 1); + max_y = std::max(max_y, y + 1); + } + } + } +// cout << " (|" << starty << "," << startx << " : " << id << " = " << matrix[starty][startx] << ")" << endl; + + geometry.box.min_x = min_x; + geometry.box.min_y = min_y; + geometry.box.max_x = max_x + 1; + geometry.box.max_y = max_y + 1; + geometry.id = id; + geometry.area = area; + geometry.center_x = (int)(sum_x / area); + geometry.center_y = (int)(sum_y / area); + return true; +} + +std::vector find_all_objects(FillMatrix& matrix, bool allow_diagonal, size_t min_area){ + size_t width = matrix.width(); + size_t height = matrix.height(); + std::vector objects; + FillMatrix::ObjectID id = 2; + for (size_t r = 0; r < height; r++){ + for (size_t c = 0; c < width; c++){ + FillGeometry region; + if (fill_geometry(region, matrix, c, r, allow_diagonal, id)){ + id++; + if (region.area >= min_area){ + objects.emplace_back(region); + } + } + } + } +// cout << "objects = " << objects.size() << endl; + return objects; +} + + + + + +} diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h new file mode 100644 index 0000000000..953bfe699d --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h @@ -0,0 +1,52 @@ +/* Fill Geometry + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + * + * Return a set of connected clusters. + * + */ + +#ifndef PokemonAutomation_CommonFramework_FillGeometry_H +#define PokemonAutomation_CommonFramework_FillGeometry_H + +#include +#include +#include "InferenceTypes.h" +#include "FillMatrix.h" + +namespace PokemonAutomation{ + +struct FillGeometry{ + PixelBox box; + FillMatrix::ObjectID id = 0; + size_t area = 0; + int center_x = 0; + int center_y = 0; +}; + +enum class MapState : uint8_t{ + WALL, + EMPTY, + QUEUED, + DONE, +}; + + +bool fill_geometry( + FillGeometry& geometry, + FillMatrix& matrix, + int x, int y, bool allow_diagonal, + FillMatrix::ObjectID id +); +std::vector find_all_objects( + FillMatrix& matrix, + bool allow_diagonal, + size_t min_area = 10 +); + + + + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp new file mode 100644 index 0000000000..2c69c09353 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp @@ -0,0 +1,63 @@ +/* Flag Matrix + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "FillMatrix.h" + +namespace PokemonAutomation{ + + +FillMatrix::FillMatrix(FillMatrix&& x) + : m_width(x.m_width) + , m_height(x.m_height) + , m_cells(std::move(x.m_cells)) +{ + x.m_width = 0; + x.m_height = 0; +} +FillMatrix& FillMatrix::operator=(FillMatrix&& x){ + m_width = x.m_width; + m_height = x.m_height; + m_cells = std::move(x.m_cells); + x.m_width = 0; + x.m_height = 0; + return *this; +} + +FillMatrix::FillMatrix() + : m_width(0) + , m_height(0) +{} +FillMatrix::FillMatrix(size_t width, size_t height) + : m_width(width) + , m_height(height) + , m_cells(width * height) +{} +FillMatrix::FillMatrix(const QImage& image) + : FillMatrix( + image.isNull() ? 0 : image.width(), + image.isNull() ? 0 : image.height() + ) +{} + +FillMatrix FillMatrix::extract(PixelBox box, ObjectID id) const{ + int width = box.width(); + int height = box.height(); + FillMatrix matrix(width, height); + for (int r = 0; r < height; r++){ + for (int c = 0; c < width; c++){ + ObjectID cell = (*this)[r + box.min_y][c + box.min_x]; + matrix[r][c] = cell == id ? 1 : 0; + } + } + return matrix; +} + + + + + + +} diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h new file mode 100644 index 0000000000..9cf289020e --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h @@ -0,0 +1,101 @@ +/* Fill Matrix + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_CommonFramework_FillMatrix_H +#define PokemonAutomation_CommonFramework_FillMatrix_H + +#include +#include +#include "InferenceTypes.h" + +namespace PokemonAutomation{ + +class FillMatrix{ +public: + using ObjectID = uint32_t; + +public: + FillMatrix(FillMatrix&& x); + FillMatrix(const FillMatrix& x) = default; + FillMatrix& operator=(FillMatrix&& x); + FillMatrix& operator=(const FillMatrix& x) = default; + +public: + FillMatrix(); + FillMatrix(size_t width, size_t height); + FillMatrix(const QImage& image); + + size_t width () const{ return m_width; } + size_t height() const{ return m_height; } + const ObjectID* operator[](size_t row) const{ return m_cells.data() + row * m_width; } + ObjectID* operator[](size_t row) { return m_cells.data() + row * m_width; } + + FillMatrix extract(PixelBox box, ObjectID id) const; + +public: + template + bool apply_filter(Image&& image, Filter& filter); + + template + bool apply_filter( + Image&& image, Filter& filter, + size_t y_start, size_t y_end, + size_t x_start, size_t x_end + ); + + +private: + size_t m_width; + size_t m_height; + std::vector m_cells; +}; + + + + +// Templates + +template +bool FillMatrix::apply_filter(Image&& image, Filter& filter){ + return apply_filter(image, filter, 0, m_height, 0, m_width); +} + + +template +bool FillMatrix::apply_filter( + Image&& image, Filter& filter, + size_t y_start, size_t y_end, + size_t x_start, size_t x_end +){ + if (image.isNull() || (size_t)image.width() != m_width || (size_t)image.height() != m_height){ + return false; + } + + ObjectID* data = m_cells.data(); + for (size_t r = y_start; r < y_end; r++){ + for (size_t c = x_start; c < x_end; c++){ + filter(data[c], image, c, r); + } + data += m_width; + } + + return true; +} + + + + + + + + + + + + + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/Inference/FloatStatAccumulator.h b/SerialPrograms/Source/CommonFramework/Inference/FloatStatAccumulator.h new file mode 100644 index 0000000000..5c63b6f44e --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/FloatStatAccumulator.h @@ -0,0 +1,49 @@ +/* Float Stat Accumulator + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_CommonFramework_FloatStatAccumulator_H +#define PokemonAutomation_CommonFramework_FloatStatAccumulator_H + +#include +#include +#include + +class FloatStatAccumulator{ +public: + using StatObject = double; + +public: + void operator+=(double x){ + m_count++; + m_sum += x; + m_sqr += x*x; + m_min = std::min(m_min, x); + m_max = std::max(m_max, x); + } + + uint64_t count() const{ return m_count; }; + double min() const{ return m_min; } + double max() const{ return m_max; } + double mean() const{ + return m_sum / m_count; + } + double stddev() const{ + return std::sqrt((m_sqr - m_sum*m_sum / m_count) / (m_count - 1)); + } + double diff_metric(double reference) const{ + return std::sqrt((m_sqr + reference*(reference * m_count - 2 * m_sum)) / m_count); + } + +private: + uint64_t m_count = 0; + double m_sum = 0; + double m_sqr = 0; + double m_min = INFINITY; + double m_max = -INFINITY; +}; + + +#endif diff --git a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp index e06cf01523..9e2231df5e 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp @@ -161,9 +161,43 @@ ImageStats pixel_stats(const QImage& image){ std::sqrt(variance.b) ) }; - } +ImageStats object_stats(const QImage& image, const FillMatrix& matrix, const FillGeometry& object){ + int w = image.width(); + int h = image.height(); + if (w * h <= 1){ + return ImageStats(); + } + FloatPixel sum; + FloatPixel sqr_sum; + for (int r = 0; r < h; r++){ + for (int c = 0; c < w; c++){ + if (matrix[r][c] == object.id){ + FloatPixel p(image.pixel(c, r)); + sum += p; + sqr_sum += p * p; + } + } + } + size_t total = object.area; + FloatPixel variance = (sqr_sum - sum*sum / total) / (total - 1); + return ImageStats{ + sum / total, + FloatPixel( + std::sqrt(variance.r), + std::sqrt(variance.g), + std::sqrt(variance.b) + ) + }; +} + +bool is_black(const QImage& image, double max_rgb_sum, double max_stddev_sum){ + ImageStats stats = pixel_stats(image); + double average = stats.average.sum(); + double stddev = stats.stddev.sum(); + return average < 100 && stddev < 10; +} diff --git a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.h b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.h index 004d649a90..40d45df0be 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.h +++ b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.h @@ -11,6 +11,7 @@ #include #include "InferenceTypes.h" #include "FloatPixel.h" +#include "FillGeometry.h" namespace PokemonAutomation{ @@ -36,6 +37,10 @@ struct ImageStats{ FloatPixel stddev; }; ImageStats pixel_stats(const QImage& image); +ImageStats object_stats(const QImage& image, const FillMatrix& matrix, const FillGeometry& object); + + +bool is_black(const QImage& image, double max_rgb_sum = 100, double max_stddev_sum = 10); diff --git a/SerialPrograms/Source/CommonFramework/Inference/TimeWindowStatTracker.h b/SerialPrograms/Source/CommonFramework/Inference/TimeWindowStatTracker.h new file mode 100644 index 0000000000..dbf29b38b9 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/TimeWindowStatTracker.h @@ -0,0 +1,135 @@ +/* Time Window Stat Tracker + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_CommonFramework_TimeWindowStatTracker_H +#define PokemonAutomation_CommonFramework_TimeWindowStatTracker_H + +#include +#include + +namespace PokemonAutomation{ + + +template +class TimeWindowStatTracker{ + using milliseconds = std::chrono::milliseconds; + using system_clock = std::chrono::system_clock; + using StatObject = typename StatAccumulator::StatObject; + +public: + TimeWindowStatTracker(milliseconds window) + : m_window(window) + {} + + milliseconds window() const{ + return m_window; + } + + system_clock::time_point push( + const StatObject& stats, + system_clock::time_point timestamp = system_clock::now() + ){ + m_history.emplace( + std::piecewise_construct, + std::forward_as_tuple(timestamp), + std::forward_as_tuple(stats) + ); + + system_clock::time_point tail = timestamp - m_window; + while (!m_history.empty() && m_history.begin()->first < tail){ + m_history.erase(m_history.begin()); + } + + return timestamp; + } + system_clock::time_point push( + StatObject&& stats, + system_clock::time_point timestamp = system_clock::now() + ){ + m_history.emplace( + std::piecewise_construct, + std::forward_as_tuple(timestamp), + std::forward_as_tuple(std::move(stats)) + ); + + system_clock::time_point tail = timestamp - m_window; + while (!m_history.empty() && m_history.begin()->first < tail){ + m_history.erase(m_history.begin()); + } + + return timestamp; + } + + // Accumulate entire history. + StatAccumulator accumulate_all() const{ + return accumulate(m_history.begin(), m_history.end()); + } + + // Accumulate [latest - duration, latest]. + StatAccumulator accumulate_last( + milliseconds duration + ) const{ + return accumulate( + m_history.empty() + ? m_history.end() + : m_history.lower_bound(m_history.rbegin()->first - duration), + m_history.end() + ); + } + + // Accumulate [oldest, point]. + StatAccumulator accumulate_start_to_point( + system_clock::time_point point + ) const{ + return accumulate(m_history.begin, m_history.upper_bound(point)); + } + + // Accumulate [oldest, latest - time_behind_latest]. + StatAccumulator accumulate_start_to_point( + milliseconds time_behind_latest + ) const{ + return accumulate( + m_history.begin(), + m_history.empty() + ? m_history.end() + : m_history.upper_bound(m_history.rbegin()->first - time_behind_latest) + ); + } + + // Accumulate [start, end]. + StatAccumulator accumulate( + system_clock::time_point start, + system_clock::time_point end + ) const{ + return accumulate(m_history.lower_bound(start), m_history.upper_bound(end)); + } + + +private: + using const_iterator_type = typename std::map::const_iterator; + + StatAccumulator accumulate( + const_iterator_type start, + const_iterator_type end + ) const{ + StatAccumulator accumulator; + for (; start != end; ++start){ + accumulator += start->second; + } + return accumulator; + } + + +private: + milliseconds m_window; + std::map m_history; + +}; + + + +} +#endif diff --git a/SerialPrograms/Source/NintendoSwitch/Programs/FriendCodeAdder.cpp b/SerialPrograms/Source/NintendoSwitch/Programs/FriendCodeAdder.cpp index 4e6712eca0..a7e5a6ee4e 100644 --- a/SerialPrograms/Source/NintendoSwitch/Programs/FriendCodeAdder.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Programs/FriendCodeAdder.cpp @@ -60,15 +60,8 @@ void FriendCodeAdder::program(SingleSwitchProgramEnvironment& env) const{ continue; } - home_to_add_friends(USER_SLOT - 1, first); + home_to_add_friends(USER_SLOT - 1, 3, first); first = false; - pbf_wait(50); - - // Enter friend search. - pbf_press_dpad(DPAD_RIGHT, 5, 0); - pbf_move_right_joystick(128, 255, 5, 0); - pbf_press_dpad(DPAD_DOWN, 5, 0); - pbf_move_right_joystick(128, 255, 5, 0); ssf_press_button1(BUTTON_A, OPEN_CODE_PAD_DELAY); enter_digits(12, &code[0]); diff --git a/SerialPrograms/Source/PanelList.cpp b/SerialPrograms/Source/PanelList.cpp index 6a163fe1d5..4f580319e6 100644 --- a/SerialPrograms/Source/PanelList.cpp +++ b/SerialPrograms/Source/PanelList.cpp @@ -43,6 +43,14 @@ #include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.h" #include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.h" +#include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h" +#include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h" +#include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h" +#include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h" +#include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h" +#include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h" +#include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h" + #include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_BeamReset.h" #include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.h" #include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_EventBeamFinder.h" @@ -137,6 +145,14 @@ const std::vector>& PROGRAM_LIST(){ list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.cpp new file mode 100644 index 0000000000..4ea83a668f --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.cpp @@ -0,0 +1,210 @@ +/* Battle Menu Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + * + * Drop a wishing piece and determine if it is red or purple. + * + */ + +#include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/ColorClustering.h" +#include "PokemonSwSh_BattleMenuDetector.h" + +#include +using std::cout; +using std::endl; + +//#define DEBUG_StandardBattleMenuDetector + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +StandardBattleMenuDetector::StandardBattleMenuDetector(VideoFeed& feed) + : m_icon_fight (feed, 0.923, 0.576 + 0 * 0.1075, 0.05, 0.080) + , m_icon_pokemon (feed, 0.923, 0.576 + 1 * 0.1075, 0.05, 0.080) + , m_icon_bag (feed, 0.923, 0.576 + 2 * 0.1075, 0.05, 0.080) + , m_icon_run (feed, 0.923, 0.576 + 3 * 0.1075, 0.05, 0.080) + , m_text_fight (feed, 0.830, 0.576 + 0 * 0.1075, 0.08, 0.080) + , m_text_pokemon (feed, 0.830, 0.576 + 1 * 0.1075, 0.08, 0.080) + , m_text_bag (feed, 0.830, 0.576 + 2 * 0.1075, 0.08, 0.080) + , m_text_run (feed, 0.830, 0.576 + 3 * 0.1075, 0.08, 0.080) +{} +bool StandardBattleMenuDetector::detect(const QImage& image) const{ + bool fight; + +#ifdef DEBUG_StandardBattleMenuDetector + cout << "=============> Fight Text 0" << endl; +#endif + fight = false; + fight |= !fight && cluster_fit_2( + extract_box(image, m_text_fight), + qRgb(0, 0, 0), 0.9, + qRgb(255, 255, 255), 0.1, + 0.2, 50, 0.1 + ); + fight |= !fight && cluster_fit_2( + extract_box(image, m_text_fight), + qRgb(0, 0, 0), 0.1, + qRgb(255, 255, 255), 0.9, + 0.2, 50, 0.1 + ); + if (!fight){ + return false; + } + +#ifdef DEBUG_StandardBattleMenuDetector + cout << "=============> Pokemon Text" << endl; +#endif + fight = false; + fight |= !fight && cluster_fit_2( + extract_box(image, m_text_pokemon), + qRgb(0, 0, 0), 0.1, + qRgb(255, 255, 255), 0.9, + 0.2, 50, 0.1 + ); + fight |= !fight && cluster_fit_2( + extract_box(image, m_text_pokemon), + qRgb(0, 0, 0), 0.9, + qRgb(255, 255, 255), 0.1, + 0.2, 50, 0.1 + ); + if (!fight){ + return false; + } + +#ifdef DEBUG_StandardBattleMenuDetector + cout << "=============> Bag Text" << endl; +#endif + fight = false; + fight |= !fight && cluster_fit_2( + extract_box(image, m_text_bag), + qRgb(0, 0, 0), 0.1, + qRgb(255, 255, 255), 0.9, + 0.2, 50, 0.1 + ); + fight |= !fight && cluster_fit_2( + extract_box(image, m_text_bag), + qRgb(0, 0, 0), 0.9, + qRgb(255, 255, 255), 0.1, + 0.2, 50, 0.1 + ); + if (!fight){ + return false; + } + +#ifdef DEBUG_StandardBattleMenuDetector + cout << "=============> Run Text" << endl; +#endif + fight = false; + fight |= !fight && cluster_fit_2( + extract_box(image, m_text_run), + qRgb(0, 0, 0), 0.1, + qRgb(255, 255, 255), 0.9, + 0.2, 50, 0.1 + ); + fight |= !fight && cluster_fit_2( + extract_box(image, m_text_run), + qRgb(0, 0, 0), 0.9, + qRgb(255, 255, 255), 0.1, + 0.2, 50, 0.1 + ); + if (!fight){ + return false; + } + + +#if 1 +#ifdef DEBUG_StandardBattleMenuDetector + cout << "=============> Fight Symbol 0" << endl; +#endif + fight = false; + fight |= !fight && cluster_fit_2( + extract_box(image, m_icon_fight), + qRgb(255, 255, 255), 1.7, + qRgb(153, 75, 112), 1.0 + ); +#ifdef DEBUG_StandardBattleMenuDetector + cout << "=============> Fight Symbol 1" << endl; +#endif + fight |= !fight && cluster_fit_2( + extract_box(image, m_icon_fight), + qRgb(0, 0, 0), 1.4, + qRgb(185, 6, 40), 1.0 + ); + fight |= !fight && cluster_fit_2( // Max raid Fight button is a bit different. + extract_box(image, m_icon_fight), + qRgb(0, 0, 0), 1.7, + qRgb(182, 33, 82), 1.0 + ); + if (!fight){ + return false; + } + +#ifdef DEBUG_StandardBattleMenuDetector + cout << "=============> Pokemon Symbol" << endl; +#endif + bool pokemon = false; + pokemon |= !pokemon && cluster_fit_2( + extract_box(image, m_icon_pokemon), + qRgb(255, 255, 255), 3.1, + qRgb(126, 224, 142), 1.0 + ); + pokemon |= !pokemon && cluster_fit_2( + extract_box(image, m_icon_pokemon), + qRgb(0, 0, 0), 2.7, + qRgb(8, 158, 18), 1.0 + ); + if (!pokemon){ + return false; + } + +#ifdef DEBUG_StandardBattleMenuDetector + cout << "=============> Bag Symbol" << endl; +#endif + bool bag = false; + bag |= !bag && cluster_fit_2( + extract_box(image, m_icon_bag), + qRgb(255, 255, 255), 2.4, + qRgb(236, 192, 124), 1.0 + ); + bag |= !bag && cluster_fit_2( + extract_box(image, m_icon_bag), + qRgb(0, 0, 0), 1.9, + qRgb(215, 120, 11), 1.0 + ); + if (!bag){ + return false; + } + +#ifdef DEBUG_StandardBattleMenuDetector + cout << "=============> Run Symbol" << endl; +#endif + bool run = false; + run |= !run && cluster_fit_2( + extract_box(image, m_icon_run), + qRgb(255, 255, 255), 2.3, + qRgb(216, 150, 230), 1.0 + ); + run |= !run && cluster_fit_2( + extract_box(image, m_icon_run), + qRgb(0, 0, 0), 1.9, + qRgb(179, 15, 195), 1.0 + ); + if (!run){ + return false; + } +#endif + +// image.save("battle-menu.png"); + return true; +} + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h new file mode 100644 index 0000000000..f987fb867d --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h @@ -0,0 +1,41 @@ +/* Battle Menu Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + * + * Drop a wishing piece and determine if it is red or purple. + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_BattleMenuDetector_H +#define PokemonAutomation_PokemonSwSh_BattleMenuDetector_H + +#include "CommonFramework/Tools/VideoFeed.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +class StandardBattleMenuDetector{ +public: + StandardBattleMenuDetector(VideoFeed& feed); + + bool detect(const QImage& image) const; + + +private: + InferenceBoxScope m_icon_fight; + InferenceBoxScope m_icon_pokemon; + InferenceBoxScope m_icon_bag; + InferenceBoxScope m_icon_run; + InferenceBoxScope m_text_fight; + InferenceBoxScope m_text_pokemon; + InferenceBoxScope m_text_bag; + InferenceBoxScope m_text_run; +}; + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp index 71cce729cf..174e168b28 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp @@ -31,7 +31,7 @@ BeamSetter::BeamSetter(VideoFeed& feed, Logger& logger) } } -bool BeamSetter::run( +BeamSetter::Detection BeamSetter::run( ProgramEnvironment& env, BotBase& botbase, double detection_threshold, uint16_t timeout_ticks ){ @@ -39,7 +39,7 @@ bool BeamSetter::run( QImage baseline_image = m_feed.snapshot(); if (baseline_image.isNull()){ m_logger.log("BeamSetter(): Screenshot failed.", "purple"); - return false; + return Detection::NO_DETECTION; } // baseline_image.save("f:/test0.jpg"); // cout << "=======================" << endl; @@ -67,7 +67,7 @@ bool BeamSetter::run( QImage current = m_feed.snapshot(); if (current.isNull()){ m_logger.log("BeamSetter(): Screenshot failed.", "purple"); - return false; + return Detection::NO_DETECTION; } // current.save("f:/test1.jpg"); @@ -134,7 +134,7 @@ bool BeamSetter::run( m_logger.log(str, "purple"); if (count >= 5){ m_logger.log("BeamReader(): 5 positive red reads. Red beam found.", "blue"); - return false; + return Detection::RED_DETECTED; } } if (!purple_detections.empty()){ @@ -149,18 +149,18 @@ bool BeamSetter::run( m_logger.log(str, "purple"); if (count >= 1){ m_logger.log("BeamReader(): Purple beam found!", "blue"); - return true; + return Detection::PURPLE; } } if (low_stddev_flag && text_stddev > 100){ m_logger.log("BeamReader(): No beam detected with text. Resetting.", "blue"); - return false; + return Detection::RED_ASSUMED; } env.wait(std::chrono::milliseconds(50)); now = system_clock(botbase); } - return false; + return Detection::NO_DETECTION; } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.h index 8b5d7a5718..c1904b9d9f 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.h @@ -21,10 +21,18 @@ namespace PokemonSwSh{ class BeamSetter{ +public: + enum Detection{ + NO_DETECTION, + RED_DETECTED, + RED_ASSUMED, + PURPLE, + }; + public: BeamSetter(VideoFeed& feed, Logger& logger); - bool run( + Detection run( ProgramEnvironment& env, BotBase& botbase, double detection_threshold, uint16_t timeout_ticks ); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp new file mode 100644 index 0000000000..7d00ae3e04 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp @@ -0,0 +1,197 @@ +/* Fishing Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/FillMatrix.h" +#include "PokemonSwSh_FishingDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +struct PinkFilter{ + size_t count = 0; + + void operator()(FillMatrix::ObjectID& cell, const QImage& image, size_t x, size_t y){ + QRgb pixel = image.pixel(x, y); + int red = qRed(pixel); + int green = qGreen(pixel); +// int blue = qBlue(pixel); + int set = red > 128 && green < 128; + cell = set; + count += set; + } +}; +struct PinkFilter2{ + size_t count = 0; + + void operator()(FillMatrix::ObjectID& cell, QImage& image, size_t x, size_t y){ + QRgb pixel = image.pixel(x, y); + int red = qRed(pixel); + int green = qGreen(pixel); +// int blue = qBlue(pixel); + int set = red > 128 && green < 128; + if (!set){ + image.setPixel(x, y, 0); + } + cell = set; + count += set; + } +}; + + +FishingDetector::FishingDetector( + VideoFeed& feed +) + : m_feed(feed) + , m_hook_box(feed, 0.4, 0.15, 0.2, 0.4) + , m_miss_box(feed, 0.3, 0.9, 0.4, 0.05) + , m_battle_menu(feed) +{} +FishingDetector::Detection FishingDetector::detect_now(){ + QImage screen = m_feed.snapshot(); + + if (m_battle_menu.detect(screen)){ + return Detection::BATTLE_MENU; + } + + QImage hook_image = extract_box(screen, m_hook_box); + { + QImage image = extract_box(screen, m_miss_box); + ImageStats stats = pixel_stats(image); + if (stats.stddev.sum() < 10 && stats.average.sum() > 500 && pixel_stddev(hook_image).sum() > 50){ + return Detection::MISSED; + } + } + +// return Detection::NO_DETECTION; + + + FillMatrix matrix(hook_image); + PinkFilter filter; + matrix.apply_filter(hook_image, filter); + + std::vector objects = find_all_objects(matrix, false, 10); + std::multimap candidate_top; + std::multimap candidate_bot; + for (const FillGeometry& object : objects){ + ImageStats stats = object_stats(hook_image, matrix, object); + double aspect_ratio = (double)object.box.width() / object.box.height(); + FloatPixel color_ratio = stats.average / stats.average.sum(); + double stddev = stats.stddev.sum(); + +#if 0 + cout << object.area << " : [" << object.center_x << "," << object.center_y + << "][" << object.box.width() << " x " << object.box.height() + << "], mean = " << stats.average / stats.average.sum() + << ", stddev = " << stats.stddev << endl; +#endif + + if (0.4 < aspect_ratio && aspect_ratio < 0.6 && + stddev < 100 && + euclidean_distance(color_ratio, FloatPixel(0.56, 0.14, 0.30)) < 0.2 + ){ +// cout << "top" << endl; + candidate_top.emplace(object.area, object); + } + if (1.0 < aspect_ratio && aspect_ratio < 1.5 && + stddev < 100 && + euclidean_distance(color_ratio, FloatPixel(0.56, 0.21, 0.23)) < 0.2 + ){ +// cout << "bottom" << endl; + candidate_bot.emplace(object.area, object); + } + } +// if (!candidate_top.empty()){ +// hook_image.save("test.png"); +// } + + for (auto iter = candidate_top.rbegin(); iter != candidate_top.rend(); ++iter){ + const FillGeometry& top = iter->second; + size_t top_area = top.area; + size_t area_low = top_area / 7.; + size_t area_high = top_area / 5.; +// cout << "area = " << top_area << ", low = " << area_low << ", high = " << area_high << endl; + auto iter0 = candidate_bot.lower_bound(area_low); + auto iter1 = candidate_bot.upper_bound(area_high); + for (; iter0 != iter1; ++iter0){ + const FillGeometry& bottom = iter0->second; + +// cout << "top_area = " << top_area << ", bot_area = " << bottom.area << endl; + + // Verify that top is above bottom. + if (top.box.max_y >= bottom.box.min_y){ + continue; + } +// cout << "check 1" << endl; + + // Verify bottom is left of the top. + if (top.center_x <= bottom.center_x){ + continue; + } + + // Make sure horizontal alignment is reasonable. + int mid = (top.box.min_x + top.box.max_x) / 2; + if (std::abs(bottom.box.max_x - mid) * 3 > top.box.width()){ + continue; + } + + // Make sure vertical alignment is reasonable. + if (top.box.max_y + top.box.height() <= bottom.box.min_y){ + continue; + } + +// cout << "match!" << endl; + return Detection::HOOKED; + } + } + + return Detection::NO_DETECTION; +} +FishingDetector::Detection FishingDetector::wait_for_detection( + ProgramEnvironment& env, Logger& logger, + std::chrono::seconds timeout +){ + auto start = std::chrono::system_clock::now(); + auto last = start; + + while (true){ + env.check_stopping(); + + Detection detection = detect_now(); + switch (detection){ + case Detection::NO_DETECTION: + break; + case Detection::HOOKED: + logger.log("FishEncounterDetector: Detected hook!", "purple"); + return detection; + case Detection::MISSED: + logger.log("FishEncounterDetector: Missed a hook.", "red"); + return detection; + case Detection::BATTLE_MENU: + logger.log("FishEncounterDetector: Expected battle menu.", "red"); + return detection; + } + + auto now = std::chrono::system_clock::now(); + if (now - start > timeout){ + logger.log("FishEncounterDetector: Timed out.", "red"); + return Detection::NO_DETECTION; + } + auto time_since_last_frame = now - last; + if (time_since_last_frame > std::chrono::milliseconds(50)){ + env.wait(std::chrono::milliseconds(50) - time_since_last_frame); + } + last = now; + } +} + + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h new file mode 100644 index 0000000000..f69b6f8f94 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h @@ -0,0 +1,54 @@ +/* Fishing Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_FishingDetector_H +#define PokemonAutomation_PokemonSwSh_FishingDetector_H + +#include +#include "CommonFramework/Tools/VideoFeed.h" +#include "CommonFramework/Tools/Logger.h" +#include "CommonFramework/Tools/ProgramEnvironment.h" +#include "PokemonSwSh_BattleMenuDetector.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class FishingDetector{ +public: + enum Detection{ + NO_DETECTION, + HOOKED, + MISSED, + BATTLE_MENU, + }; + +public: + FishingDetector(VideoFeed& feed); + + Detection detect_now(); + Detection wait_for_detection( + ProgramEnvironment& env, Logger& logger, + std::chrono::seconds timeout = std::chrono::seconds(12) + ); + +private: + VideoFeed& m_feed; + InferenceBoxScope m_hook_box; + InferenceBoxScope m_miss_box; + StandardBattleMenuDetector m_battle_menu; +}; + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.cpp new file mode 100644 index 0000000000..2b23044f6f --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.cpp @@ -0,0 +1,74 @@ +/* Raid Catch Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/ColorClustering.h" +#include "PokemonSwSh_RaidCatchDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +RaidCatchDetector::RaidCatchDetector( + VideoFeed& feed, + std::chrono::milliseconds timeout +) + : m_feed(feed) + , m_timeout(timeout) + , m_box0(feed, 0.82, 0.84 + 0 * 0.078, 0.15, 0.06) + , m_box1(feed, 0.82, 0.84 + 1 * 0.078, 0.15, 0.06) + , m_start_time(std::chrono::system_clock::now()) +{} +bool RaidCatchDetector::has_timed_out() const{ + return std::chrono::system_clock::now() - m_start_time > m_timeout; +} +bool RaidCatchDetector::detect(){ + QImage image = m_feed.snapshot(); +// cout << "==================" << endl; + if (!cluster_fit_2( + extract_box(image, m_box0), + qRgb(0, 0, 0), 0.90, + qRgb(255, 255, 255), 0.10 + )){ + return false; + } +// cout << "------------------" << endl; + if (!cluster_fit_2( + extract_box(image, m_box1), + qRgb(255, 255, 255), 0.90, + qRgb(0, 0, 0), 0.10 + )){ + return false; + } + return true; +} +bool RaidCatchDetector::wait(ProgramEnvironment& env){ + while (true){ + auto start = std::chrono::system_clock::now(); + env.check_stopping(); + + if (has_timed_out()){ + return false; + } + if (detect()){ + return true; + } + + auto end = std::chrono::system_clock::now(); + auto duration = end - start; + if (duration < std::chrono::milliseconds(50)){ + env.wait(std::chrono::milliseconds(50) - duration); + } + } +} + + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h new file mode 100644 index 0000000000..0fdb8c3a9e --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h @@ -0,0 +1,43 @@ +/* Raid Catch Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_RaidCatchDetector_H +#define PokemonAutomation_PokemonSwSh_RaidCatchDetector_H + +#include +#include "CommonFramework/Tools/VideoFeed.h" +#include "CommonFramework/Tools/ProgramEnvironment.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class RaidCatchDetector{ +public: + RaidCatchDetector( + VideoFeed& feed, + std::chrono::milliseconds timeout + ); + + bool has_timed_out() const; + bool detect(); + bool wait(ProgramEnvironment& env); + +private: + VideoFeed& m_feed; + std::chrono::milliseconds m_timeout; + InferenceBoxScope m_box0; + InferenceBoxScope m_box1; + std::chrono::time_point m_start_time; +}; + + +} +} +} +#endif + diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp new file mode 100644 index 0000000000..d6609db1e4 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp @@ -0,0 +1,52 @@ +/* Start Battle Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "CommonFramework/Inference/ImageTools.h" +#include "PokemonSwSh_StartBattleDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + + +StartBattleDetector::StartBattleDetector( + VideoFeed& feed, + std::chrono::milliseconds timeout +) + : m_feed(feed) + , m_timeout(timeout) + , m_box(feed, 0.65, 0.89, 0.25, 0.07) + , m_start_time(std::chrono::system_clock::now()) +{} + +bool StartBattleDetector::has_timed_out() const{ + return std::chrono::system_clock::now() - m_start_time > m_timeout; +} +bool StartBattleDetector::detect(){ + QImage image = extract_box(m_feed.snapshot(), m_box); + ImageStats stats = pixel_stats(image); +// cout << "mean = " << stats.average << ", stddev = " << stats.stddev << endl; +// return stats.stddev.sum() < 10; + + // White screen. + if (stats.average.sum() > 600 && stats.stddev.sum() < 10){ + return true; + } + + // Grey text box. + if (stats.average.sum() < 200 && stats.stddev.sum() < 10){ + return true; + } + + return false; +} + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h new file mode 100644 index 0000000000..f3b2412705 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h @@ -0,0 +1,40 @@ +/* Start Battle Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_StartBattleDetector_H +#define PokemonAutomation_PokemonSwSh_StartBattleDetector_H + +#include +#include "CommonFramework/Tools/VideoFeed.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class StartBattleDetector{ +public: + StartBattleDetector( + VideoFeed& feed, + std::chrono::milliseconds timeout + ); + + bool has_timed_out() const; + bool detect(); + +private: + VideoFeed& m_feed; + std::chrono::milliseconds m_timeout; + InferenceBoxScope m_box; + std::chrono::time_point m_start_time; +}; + + +} +} +} +#endif + diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.cpp new file mode 100644 index 0000000000..28fa5d38a7 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.cpp @@ -0,0 +1,124 @@ +/* Summary Shiny Symbol + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + * + * Determine if a raid is full and ready to start early. + * + */ + +#include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/ColorClustering.h" +#include "PokemonSwSh_SummaryShinySymbolDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +SummaryShinySymbolDetector::SummaryShinySymbolDetector(VideoFeed& feed, Logger& logger) + : m_feed(feed) + , m_logger(logger) + , m_state0_box(feed, 0.02, 0.84, 0.1, 0.1) + , m_state1_box(feed, 0.02, 0.97, 0.5, 0.02) + , m_symbol_box(feed, 0.08, 0.53, 0.02, 0.05) +{} + +SummaryShinySymbolDetector::Detection SummaryShinySymbolDetector::detect_now(){ + QImage screen = m_feed.snapshot(); + + { + QImage state = extract_box(screen, m_state1_box); + if (pixel_stddev(state).sum() > 10){ + return Detection::NO_DETECTION; + } + if (pixel_average(state).sum() > 100){ + return Detection::NO_DETECTION; + } + } + { + QImage state = extract_box(screen, m_state0_box); + if (pixel_stddev(state).sum() > 10){ + return Detection::NO_DETECTION; + } + FloatPixel color = pixel_average_normalized(state); + if (euclidean_distance(color, FloatPixel(0.70, 0.07, 0.23)) > 0.2){ + return Detection::NO_DETECTION; + } + } + + QImage symbol = extract_box(screen, m_symbol_box); + if (cluster_fit_2( + symbol, + qRgb(255, 255, 255), 0.84, + qRgb(156, 33, 80), 0.16, + 0.1 + )){ + return Detection::SHINY; + } + if (cluster_fit_2( + symbol, + qRgb(255, 255, 255), 0.92, + qRgb(0, 0, 0), 0.08, + 0.1 + )){ +// screen.save("test.png"); + return Detection::NOT_SHINY; + } + + return Detection::NO_DETECTION; +} +SummaryShinySymbolDetector::Detection SummaryShinySymbolDetector::wait_for_detection( + ProgramEnvironment& env, + std::chrono::seconds timeout +){ + auto start = std::chrono::system_clock::now(); + auto last = start; + + Detection last_detection = Detection::NO_DETECTION; + size_t confirmations = 0; + while (true){ + env.check_stopping(); + + Detection detection = detect_now(); + if (detection == last_detection){ + confirmations++; + }else{ + last_detection = detection; + confirmations = 0; + } + if (last_detection != Detection::NO_DETECTION && confirmations >= 10){ + break; + } + + auto now = std::chrono::system_clock::now(); + if (now - start > timeout){ + last_detection = Detection::NO_DETECTION; + break; + } + auto time_since_last_frame = now - last; + if (time_since_last_frame > std::chrono::milliseconds(50)){ + env.wait(std::chrono::milliseconds(50) - time_since_last_frame); + } + last = now; + } + + switch (last_detection){ + case Detection::NO_DETECTION: + m_logger.log("SummaryShinySymbolDetector: Nothing found after timeout.", "red"); + break; + case Detection::NOT_SHINY: + m_logger.log("SummaryShinySymbolDetector: Not shiny.", "purple"); + break; + case Detection::SHINY: + m_logger.log("SummaryShinySymbolDetector: Shiny!", "blue"); + break; + } + return last_detection; +} + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h new file mode 100644 index 0000000000..80af465f99 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h @@ -0,0 +1,54 @@ +/* Summary Shiny Symbol + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + * + * Determine if a raid is full and ready to start early. + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_SummaryShinySymbolDetector_H +#define PokemonAutomation_PokemonSwSh_SummaryShinySymbolDetector_H + +#include "CommonFramework/Tools/VideoFeed.h" +#include "CommonFramework/Tools/Logger.h" +#include "CommonFramework/Tools/ProgramEnvironment.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class SummaryShinySymbolDetector{ +public: + enum Detection{ + NO_DETECTION, + NOT_SHINY, + SHINY, + }; + +public: + SummaryShinySymbolDetector(VideoFeed& feed, Logger& logger); + + Detection detect_now(); + Detection wait_for_detection( + ProgramEnvironment& env, + std::chrono::seconds timeout = std::chrono::seconds(10) + ); + +private: + VideoFeed& m_feed; + Logger& m_logger; + InferenceBoxScope m_state0_box; + InferenceBoxScope m_state1_box; + InferenceBoxScope m_symbol_box; +}; + + + +} +} +} + +#endif + diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp new file mode 100644 index 0000000000..5e513f4230 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp @@ -0,0 +1,146 @@ +/* Shiny Encounter Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include "CommonFramework/Inference/FloatStatAccumulator.h" +#include "CommonFramework/Inference/TimeWindowStatTracker.h" +#include "CommonFramework/Inference/ImageTools.h" +#include "PokemonSwSh_ShinyTrigger.h" +#include "PokemonSwSh_ShinyEncounterDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +const InferenceBox ShinyEncounterDetector::REGULAR_BATTLE(0.5, 0.05, 0.5, 0.70); +const InferenceBox ShinyEncounterDetector::RAID_BATTLE(0.3, 0.01, 0.7, 0.75); + + +ShinyEncounterDetector::ShinyEncounterDetector( + VideoFeed& feed, Logger& logger, + const InferenceBox& box, + std::chrono::seconds timeout, + double detection_threshold +) + : m_feed(feed) + , m_logger(logger) + , m_box(feed, box) + , m_timeout(timeout) + , m_detection_threshold(detection_threshold) + , m_menu(feed) +{} + +ShinyEncounterDetector::Detection ShinyEncounterDetector::detect(ProgramEnvironment& env){ + TimeWindowStatTracker tracker(std::chrono::milliseconds(4000)); + + std::deque detection_overlays; + + bool shiny = false; + double star_alpha = 0; + double square_alpha = 0; + + auto start = std::chrono::system_clock::now(); + auto last = start; + for (uint64_t count = 0;; count++){ + env.check_stopping(); + + QImage image = m_feed.snapshot(); + if (m_menu.detect(image)){ + m_logger.log("ShinyDetector: Battle menu found!", "purple"); + break; + } + + QImage inference_image = extract_box(image, m_box); + + ShinyImageDetection signatures; + signatures.accumulate(inference_image, count, &m_logger); + + star_alpha += 3 * signatures.stars.size(); + if (signatures.squares.size() > 1){ + square_alpha += signatures.squares.size() - 1; + } + square_alpha += 5 * signatures.lines.size(); + + double signature_alpha = signatures.alpha(); + tracker.push(signature_alpha, last); + FloatStatAccumulator previous_2_seconds = tracker.accumulate_start_to_point(std::chrono::milliseconds(2000)); + FloatStatAccumulator last_2_seconds = tracker.accumulate_last(std::chrono::milliseconds(2000)); + double mean0 = previous_2_seconds.mean(); + double alpha = last_2_seconds.diff_metric(mean0); + + if (signature_alpha > 1.0){ + if (alpha > m_detection_threshold){ + shiny = true; + m_logger.log( + "ShinyDetector: alpha = " + QString::number(alpha) + " (threshold exceeded)", + "blue" + ); + }else{ + m_logger.log( + "ShinyDetector: alpha = " + QString::number(alpha), + "blue" + ); + } + } + + detection_overlays.clear(); + for (const auto& item : signatures.balls){ + InferenceBox box = translate_to_parent(image, m_box, item); + box.color = Qt::green; + detection_overlays.emplace_back(m_feed, box); + } + for (const auto& item : signatures.stars){ + InferenceBox box = translate_to_parent(image, m_box, item); + box.color = Qt::green; + detection_overlays.emplace_back(m_feed, box); + } + for (const auto& item : signatures.squares){ + InferenceBox box = translate_to_parent(image, m_box, item); + box.color = Qt::green; + detection_overlays.emplace_back(m_feed, box); + } + for (const auto& item : signatures.lines){ + InferenceBox box = translate_to_parent(image, m_box, item); + box.color = Qt::green; + detection_overlays.emplace_back(m_feed, box); + } + + if (!signatures.lines.empty()){ + image.save("DetectionLine.png"); + } + + auto now = std::chrono::system_clock::now(); + if (now - start > m_timeout){ + m_logger.log("ShinyDetector: Battle menu not found after timeout.", "red"); + return Detection::NO_BATTLE_MENU; + } + auto time_since_last_frame = now - last; + if (time_since_last_frame > std::chrono::milliseconds(50)){ + env.wait(std::chrono::milliseconds(50) - time_since_last_frame); + } + last = now; + } + detection_overlays.clear(); + + if (!shiny){ + m_logger.log("ShinyDetector: Not Shiny.", "purple"); + return Detection::NOT_SHINY; + } + + if (star_alpha >= square_alpha){ + m_logger.log("ShinyDetector: Detected Star Shiny!", "blue"); + return Detection::STAR_SHINY; + }else{ + m_logger.log("ShinyDetector: Detected Square Shiny!", "blue"); + return Detection::SQUARE_SHINY; + } +} + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h new file mode 100644 index 0000000000..deb7098d31 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h @@ -0,0 +1,57 @@ +/* Shiny Encounter Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyEncounterDetector_H +#define PokemonAutomation_PokemonSwSh_ShinyEncounterDetector_H + +#include "CommonFramework/Tools/Logger.h" +#include "CommonFramework/Tools/VideoFeed.h" +#include "CommonFramework/Tools/ProgramEnvironment.h" +#include "PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class ShinyEncounterDetector{ +public: + static const InferenceBox REGULAR_BATTLE; + static const InferenceBox RAID_BATTLE; + + enum Detection{ + NO_BATTLE_MENU, + NOT_SHINY, + STAR_SHINY, + SQUARE_SHINY, + }; + +public: + ShinyEncounterDetector( + VideoFeed& feed, Logger& logger, + const InferenceBox& box, + std::chrono::seconds timeout, + double detection_threshold = 4.0 + ); + + Detection detect(ProgramEnvironment& env); + +private: + VideoFeed& m_feed; + Logger& m_logger; + InferenceBoxScope m_box; + std::chrono::seconds m_timeout; + double m_detection_threshold; + StandardBattleMenuDetector m_menu; +}; + + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h new file mode 100644 index 0000000000..36396fe2c5 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h @@ -0,0 +1,36 @@ +/* Shiny Filters + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyFilters_H +#define PokemonAutomation_PokemonSwSh_ShinyFilters_H + +#include "CommonFramework/Inference/FillMatrix.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + + +struct BrightYellowLightFilter{ + size_t count = 0; + + void operator()(FillMatrix::ObjectID& cell, const QImage& image, size_t x, size_t y){ + QRgb pixel = image.pixel(x, y); +// int set = qRed(pixel) > 160 && qGreen(pixel) > 160 && qBlue(pixel) > 128; + int set = (pixel & 0x00c0c080) == 0x00c0c080 ? 1 : 0; + cell = set; + count += set; + } +}; + + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp new file mode 100644 index 0000000000..91961aae45 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp @@ -0,0 +1,131 @@ +/* Shiny Trigger + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include "CommonFramework/Inference/FillMatrix.h" +#include "CommonFramework/Inference/FillGeometry.h" +#include "PokemonSwSh_ShinyFilters.h" +#include "PokemonSwSh_SparkleTrigger.h" +#include "PokemonSwSh_SquareTrigger.h" +#include "PokemonSwSh_ShinyTrigger.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +void ShinyImageDetection::accumulate( + const QImage& image, uint64_t frame_counter, + Logger* logger +){ + FillMatrix matrix(image); + + BrightYellowLightFilter filter; + matrix.apply_filter(image, filter); + + std::vector objects; + objects = find_all_objects(matrix, true); + + QImage debug = image; + for (int r = 0; r < image.height(); r++){ + for (int c = 0; c < image.width(); c++){ + if (matrix[r][c] == 0){ + debug.setPixel(c, r, 0); + } + } + } + +// cout << "objects = " << objects.size() << endl; + +// bool detection = false; + for (FillGeometry& object : objects){ +// this->balls.emplace_back(object.box); + + SparkleDetector detector(matrix, object); +#if 1 + if (detector.is_ball()){ +// detection = true; + this->balls.emplace_back(object.box); + QString str; + str += "ShinyDetector: Ball [{" + QString::number(object.center_x) + ", " + QString::number(object.center_y); + str += "}, " + QString::number(object.area) + "]"; + if (logger){ + logger->log(str, "purple"); + } + } + if (detector.is_star()){ +// detection = true; + this->stars.emplace_back(object.box); + QString str; + str += "ShinyDetector Star: [{" + QString::number(object.center_x) + ", " + QString::number(object.center_y); + str += "}, " + QString::number(object.area) + "]"; + if (logger){ + logger->log(str, "purple"); + } + } +#endif +#if 1 + if (is_square(matrix, object)){ +// detection = true; + this->squares.emplace_back(object.box); + QString str; + str += "ShinyDetector Square: [{" + QString::number(object.center_x) + ", " + QString::number(object.center_y); + str += "}, " + QString::number(object.area) + "]"; + if (logger){ + logger->log(str, "purple"); + } + } +#endif +#if 1 + if (is_square_beam(matrix, object, 0.5)){ +// detection = true; + this->lines.emplace_back(object.box); + QString str; + str += "ShinyDetector Line: [{" + QString::number(object.box.width()); + str += " : {" + QString::number(object.center_x) + ", " + QString::number(object.center_y); + str += "}, " + QString::number(object.area) + "]"; + if (logger){ + logger->log(str, "purple"); + } + } +#endif + } +// cout << "objects = " << objects.size() << endl; + +#if 0 + if (detection){ +// cout << "objects = " << objects.size() << endl; + image.save("test-" + QString::number(frame_counter) + "-O.png"); + debug.save("test-" + QString::number(frame_counter) + "-P.png"); + } +#endif +// debug.save("test.png"); + +} + + + +double ShinyImageDetection::alpha() const{ + double x = 1.0; + x *= std::pow(5.0, lines.size()); + x *= std::pow(2.0, stars.size()); + x *= std::pow(2.0, balls.size()); + x *= std::pow(1.2, squares.size()); + return x; +} + + + + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h new file mode 100644 index 0000000000..6a87e20b01 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h @@ -0,0 +1,44 @@ +/* Shiny Trigger + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyTrigger_H +#define PokemonAutomation_PokemonSwSh_ShinyTrigger_H + +#include +#include +#include "CommonFramework/Tools/Logger.h" +#include "CommonFramework/Tools/VideoFeed.h" +#include "CommonFramework/Inference/ImageTools.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +struct ShinyImageDetection{ + std::vector balls; + std::vector stars; + std::vector squares; + std::vector lines; + + void accumulate( + const QImage& image, uint64_t frame_counter, + Logger* logger = nullptr + ); + + + double alpha() const; +}; + + + + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp new file mode 100644 index 0000000000..0a9e5b0d44 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp @@ -0,0 +1,252 @@ +/* Star Trigger + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include +#include "PokemonSwSh_ShinyFilters.h" +#include "PokemonSwSh_SparkleTrigger.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +const double SparkleDetector::ANGLE_TOLERANCE_DEGREES = 10.; + + +SparkleDetector::SparkleDetector( + const FillMatrix& matrix, + const FillGeometry& box, + double percentile +) + : m_box(box) + , m_percentile(percentile) +{ + if (box.area < 20){ + return; + } + + // Copy the relevant portion of the matrix and keep only the current object. + m_matrix = matrix.extract(box.box, box.id); + + // Sort all pixels by distance from center. + int center_x = box.center_x - box.box.min_x; + int center_y = box.center_y - box.box.min_y; + std::map distances; + for (size_t r = 0; r < m_matrix.height(); r++){ + for (size_t c = 0; c < m_matrix.width(); c++){ + if (m_matrix[r][c] != 0){ + int dist_x = (int)c - center_x; + int dist_y = (int)r - center_y; + uint64_t distance_sqr = (uint64_t)dist_x*dist_x + (uint64_t)dist_y*dist_y; + distances[distance_sqr]++; + } + } + } + + // Filter out the bottom 80%. + int stop = (int)(0.85 * box.area); + int count = 0; + uint64_t distance = 0; + for (auto& item : distances){ + count += item.second; + if (count >= stop){ + distance = item.first; + break; + } + } + m_radius_sqr = distance; + for (size_t r = 0; r < m_matrix.height(); r++){ + for (size_t c = 0; c < m_matrix.width(); c++){ + int dist_x = (int)c - center_x; + int dist_y = (int)r - center_y; + uint64_t distance_sqr = (uint64_t)dist_x*dist_x + (uint64_t)dist_y*dist_y; + if (distance_sqr < distance){ + m_matrix[r][c] = 0; + } + } + } + + // Find new regions. + FillMatrix::ObjectID id = 2; + for (size_t r = 0; r < m_matrix.height(); r++){ + for (size_t c = 0; c < m_matrix.width(); c++){ + FillGeometry region; + if (!fill_geometry(region, m_matrix, c, r, true, id)){ + continue; + } + id++; +// if (region.area < 4){ +// continue; +// } + m_regions.emplace(-(ssize_t)region.area, region); +// cout << region.center_x << ", " << region.center_y << " : " << region.area << endl; + } + } + +#if 0 + cout << "{" << endl; + for (size_t r = 0; r < m_matrix.height(); r++){ + cout << " {"; + for (size_t c = 0; c < m_matrix.width(); c++){ + cout << m_matrix[r][c] << ","; + } + cout << "}" << endl; + } + cout << "}" << endl; +#endif +} + +bool SparkleDetector::is_ball(){ + // Fewer than 4 regions, cannot be a ball. + if (m_regions.size() < 4){ + return false; + } + + // Make sure area is less than 1/2 of the box. + int width = m_box.box.width(); + int height = m_box.box.height(); +// cout << "area = " << m_box.area * 2 << ", box = " << (uint64_t)width * height << endl; + if (m_box.area * 2 > (uint64_t)width * height){ + return false; + } + + // Compute angles for the 4 largest regions. + int center_x = m_box.center_x - m_box.box.min_x; + int center_y = m_box.center_y - m_box.box.min_y; + std::set angles; + size_t c = 0; + for (const auto& region : m_regions){ + int x = region.second.center_x - center_x; + int y = region.second.center_y - center_y; + double angle = std::atan2(y, x) * 57.29577951308232; + if (angle < 0){ + angle += 360; + } + angles.insert(angle); + c++; + if (c >= 4){ + break; + } + } + +#if 0 + cout << "{" << endl; + for (size_t r = 0; r < m_matrix.height(); r++){ + cout << " {"; + for (size_t c = 0; c < m_matrix.width(); c++){ + cout << m_matrix[r][c] << ","; + } + cout << "}" << endl; + } + cout << "}" << endl; +#endif + + // Verify angles are aligned to the corners. + for (double angle : angles){ + double n = angle - 45; + double q = std::round(n / 90.); + double m = n - 90. * q; + if (std::abs(m) > ANGLE_TOLERANCE_DEGREES){ + return false; + } + } + + // Verify that all live pixels are near the diagonals. + for (size_t r = 0; r < m_matrix.height(); r++){ + for (size_t c = 0; c < m_matrix.width(); c++){ + if (m_matrix[r][c] == 0){ + continue; + } + double distance = std::sqrt(center_x*center_x + center_y*center_y); + int dist_x = std::abs((int)c - center_x); + int dist_y = std::abs((int)r - center_y); + int dist = std::abs(dist_y - dist_x); + if (dist > 2 + distance / 5.){ + return false; + } + } + } + + return true; +} +bool SparkleDetector::is_star(){ + // Fewer than 5 regions, cannot be a star. + if (m_regions.size() < 5){ + return false; + } + + int width = m_box.box.width(); + int height = m_box.box.height(); + + // Make sure dimentions are roughly square-ish. + if (width > 2 * height || height > 2 * width){ + return false; + } + + // Compute angles for the 5 largest regions. + int center_x = m_box.center_x - m_box.box.min_x; + int center_y = m_box.center_y - m_box.box.min_y; + std::set angles; + size_t c = 0; + for (const auto& region : m_regions){ + int x = region.second.center_x - center_x; + int y = region.second.center_y - center_y; + double angle = std::atan2(y, x) * 57.29577951308232; + if (angle < 0){ + angle += 360; + } + angles.insert(angle); + c++; + if (c >= 5){ + break; + } + } + + // Attempt best fit. + bool match = false; + for (double angle : angles){ + size_t bad = 0; + uint8_t points = 0; + for (double test : angles){ + double n = test - angle; + double q = std::round(n / 72.); + points |= (size_t)1 << ((size_t)q % 5); + double m = n - 72. * q; + if (std::abs(m) > ANGLE_TOLERANCE_DEGREES){ + bad++; + } + } + + // Not all 5 points are detected. + if ((points & 0x1f) != 0x1f){ + continue; + } + + // Bad angles. + if (bad <= 0){ + match = true; + break; + } + } + + return match; +} + + + + + + + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h new file mode 100644 index 0000000000..ab6744edc7 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h @@ -0,0 +1,51 @@ +/* Shiny Sparkle Trigger + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_SparkleTrigger_H +#define PokemonAutomation_PokemonSwSh_SparkleTrigger_H + +#include "CommonFramework/Inference/FillMatrix.h" +#include "CommonFramework/Inference/FillGeometry.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class SparkleDetector{ + static const double ANGLE_TOLERANCE_DEGREES; + +public: + SparkleDetector( + const FillMatrix& matrix, + const FillGeometry& box, + double percentile = 0.85 + ); + + bool is_ball(); + bool is_star(); + + +private: + FillGeometry m_box; + FillMatrix m_matrix; + double m_percentile; + + std::multimap m_regions; + uint64_t m_radius_sqr; +}; + + + + + + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp new file mode 100644 index 0000000000..9d2fdaa2b4 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp @@ -0,0 +1,374 @@ +/* Square Shiny Trigger + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include +#include "PokemonSwSh_SquareTrigger.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +const double SQUARE_ANGLE_TOLERANCE_DEGREES = 30; + + + +bool is_square_beam( + const FillMatrix& matrix, + const FillGeometry& object, + double min_length +){ + int width = object.box.width(); + int height = object.box.height(); + if (width < 5 || height < 5){ + return false; + } + int min_pixel_width = (int)(matrix.width() * min_length); + if (width < min_pixel_width){ + return false; + } + if ((size_t)width * width < object.area * 50){ + return false; + } + if (width < height * 10){ + return false; + } + + // Make sure it's actually a line. + + std::vector heights(width); + bool row_ok = false; +// cout << "{"; + for (int r = object.box.min_y; r < object.box.max_y; r++){ +// cout << " {"; + int length = 0; + for (int c = object.box.min_x; c < object.box.max_x; c++){ +// cout << matrix[r][c] << ", "; + int ok = matrix[r][c] == object.id ? 1 : 0; + length += ok; + heights[c - object.box.min_x] += ok; + } +// cout << "}," << endl; + if (length >= min_pixel_width){ + row_ok = true; + } + } +// cout << "}" << endl; + if (!row_ok){ + return false; + } + + // Make sure the bulge is not on the edge. + int thickest = 0; + int thickest_column = 0; + for (int c = 0; c < width; c++){ + int h = heights[c]; +// cout << h << ", "; + if (thickest < h){ + thickest = h; + thickest_column = c; + } + } +// cout << "}" << endl; +// cout << "thickest = " << thickest << endl; + int diff_left = thickest_column; + int diff_right = width - thickest_column; +// cout << diff_left << " : " << diff_right << endl; + if (diff_left * 10 < diff_right){ +// cout << "line is off-center" << endl; + return false; + } + if (diff_right * 10 < diff_left){ +// cout << "line is off-center" << endl; + return false; + } + +// cout << width << " x " << height << endl; + + return true; +} + + + +bool is_square( + const FillMatrix& matrix, + const FillGeometry& object +){ + int width = object.box.width(); + int height = object.box.height(); + if (width < 5 || height < 5){ +// cout << "too small" << endl; + return false; + } + if (object.area * 10 > matrix.height() * matrix.width()){ + return false; + } +// cout << "asdf" << endl; + size_t box_area = (size_t)width * height; +// if (object.area * 2 > box_area){ +// return false; +// } +// return true; + +// cout << "qwer" << endl; + + FillMatrix submatrix = matrix.extract(object.box, object.id); + int center_x = object.center_x - object.box.min_x; + int center_y = object.center_y - object.box.min_y; + + // Invert the cells. + for (int r = 0; r < height; r++){ + for (int c = 0; c < width; c++){ + FillMatrix::ObjectID cell = submatrix[r][c]; + cell = cell ? 0 : 1; + submatrix[r][c] = cell; + } + } + + // Check the hole + { + // Fill in the edges. + size_t edge_area = 0; + for (int c = 0; c < width; c++){ + FillGeometry body; + if (fill_geometry(body, submatrix, c, 0, false, 2)){ + edge_area += body.area; + } + if (fill_geometry(body, submatrix, c, height - 1, false, 2)){ + edge_area += body.area; + } + } + for (int r = 0; r < height; r++){ + FillGeometry body; + if (fill_geometry(body, submatrix, 0, r, false, 2)){ + edge_area += body.area; + } + if (fill_geometry(body, submatrix, width - 1, r, false, 2)){ + edge_area += body.area; + } + } + + // The center of the object should be in the hole. + if (submatrix[center_y][center_x] != 1){ +// cout << "no hole in center" << endl; + return false; + } + + size_t hole_area = box_area - edge_area - object.area; + +// cout << "box = " << box_area << ", edge = " << edge_area << ", body = " << object.area +// << ", hole = " << hole_area << endl; + + if (hole_area < 20 || edge_area * 3 > box_area * 2){ + return false; + } + } + +#if 0 + std::set angles; + { + int r = object.box.min_y; + for (int c = object.box.min_x; c < object.box.max_x; c++){ + if (matrix[r][c] != object.id){ + continue; + } + int x = c - object.center_x; + int y = r - object.center_y; + double angle = std::atan2(y, x) * 57.29577951308232; + if (angle < 0){ + angle += 360; + } + angles.insert(angle); + } + } + { + int r = object.box.max_y - 1; + for (int c = object.box.min_x; c < object.box.max_x; c++){ + if (matrix[r][c] != object.id){ + continue; + } + int x = c - object.center_x; + int y = r - object.center_y; + double angle = std::atan2(y, x) * 57.29577951308232; + if (angle < 0){ + angle += 360; + } + angles.insert(angle); + } + } + { + int c = object.box.min_x; + for (int r = object.box.min_y; r < object.box.max_y; r++){ + if (matrix[r][c] != object.id){ + continue; + } + int x = c - object.center_x; + int y = r - object.center_y; + double angle = std::atan2(y, x) * 57.29577951308232; + if (angle < 0){ + angle += 360; + } + angles.insert(angle); + } + } + { + int c = object.box.max_x - 1; + for (int r = object.box.min_y; r < object.box.max_y; r++){ + if (matrix[r][c] != object.id){ + continue; + } + int x = c - object.center_x; + int y = r - object.center_y; + double angle = std::atan2(y, x) * 57.29577951308232; + if (angle < 0){ + angle += 360; + } + angles.insert(angle); + } + } + +// cout << "angles = " << angles.size() << endl; + + // Group angles. + std::map angle_sets; + while (!angles.empty()){ + double pivot = *angles.begin(); + int& high = angle_sets[pivot] = false; + for (auto angle : angles){ + double n = angle - pivot; + double q = std::round(n / 180.); + double r = n - 180*q; + if (std::abs(r) < SQUARE_ANGLE_TOLERANCE_DEGREES){ + angles.erase(angle); + if (n > 100){ + high = 1; + } + } + } + } + + int sum = 0; + for (const auto& angle : angle_sets){ +// cout << "Angle Set: " << angle.first << ":" << angle.second << endl; + sum += angle.second; + } + if (sum < 2){ + return false; + } + + cout << width << " x " << height << endl; + + +#else + // Check vertix angles. + { + submatrix = matrix.extract(object.box, object.id); + + // Clear all but the edges. + for (int r = 1; r < height - 1; r++){ + for (int c = 1; c < width - 1; c++){ + submatrix[r][c] = 0; + } + } + + std::vector regions = find_all_objects(submatrix, false, 1); +// std::multimap regions; +// for (const FillGeometry& region : find_all_objects(submatrix, false, 1)){ +// regions.emplace(region.area, region); +// } + +#if 0 + cout << "{" << endl; + for (int r = 0; r < height; r++){ + cout << " {"; + for (int c = 0; c < width; c++){ + cout << submatrix[r][c] << ","; + } + cout << " }" << endl; + } + cout << "}" << endl; +#endif + +// cout << "regions = " << regions.size() << endl; +// if (regions.size() < 4){ +// return false; +// } + + // Get furthest points. + std::vector> points; + for (const auto& region : regions){ + if (region.box.height() == 1){ + if (region.box.min_x <= center_x){ + points.emplace_back(region.box.min_x, region.center_y); + } + if (region.box.max_x > center_x){ + points.emplace_back(region.box.max_x, region.center_y); + } + }else{ + if (region.box.min_y <= center_y){ + points.emplace_back(region.center_x, region.box.min_y); + } + if (region.box.max_y > center_y){ + points.emplace_back(region.center_x, region.box.max_y); + } + } + } + + // Compute angles. + std::set angles; + for (const auto& point : points){ + int x = point.first - center_x; + int y = point.second - center_y; + double angle = std::atan2(y, x) * 57.29577951308232; + if (angle < 0){ + angle += 360; + } + angles.insert(angle); +// cout << "angle = " << angle << ", [" << x << "," << y << "]" << endl; + } + + // Verify angles. + std::map angle_sets; + while (!angles.empty()){ + double pivot = *angles.begin(); + int& high = angle_sets[pivot] = false; + for (auto angle : angles){ + double n = angle - pivot; + double q = std::round(n / 180.); + double r = n - 180*q; + if (std::abs(r) < SQUARE_ANGLE_TOLERANCE_DEGREES){ + angles.erase(angle); + if (n > 100){ + high = 1; + } + } + } + } + + int sum = 0; + for (const auto& angle : angle_sets){ +// cout << "Angle Set: " << angle.first << ":" << angle.second << endl; + sum += angle.second; + } + if (sum < 2){ + return false; + } + } +#endif + + return true; +} + + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h new file mode 100644 index 0000000000..5f1982e608 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h @@ -0,0 +1,34 @@ +/* Line Trigger + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_LineTrigger_H +#define PokemonAutomation_PokemonSwSh_LineTrigger_H + +#include +#include "CommonFramework/Inference/FillGeometry.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +bool is_square_beam( + const FillMatrix& matrix, + const FillGeometry& object, + double min_length +); + + +bool is_square( + const FillMatrix& matrix, + const FillGeometry& object +); + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_BeamReset.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_BeamReset.cpp index c4d2978373..5bb760ec56 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_BeamReset.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_BeamReset.cpp @@ -58,7 +58,7 @@ void BeamReset::program(SingleSwitchProgramEnvironment& env) const{ } pbf_wait(DELAY_BEFORE_RESET); - reset_game_from_home(TOLERATE_SYSTEM_UPDATE_MENU_SLOW, 0, false); + reset_game_from_home(TOLERATE_SYSTEM_UPDATE_MENU_SLOW); } } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp index b3181809f8..0e2bd7f7c1 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp @@ -4,11 +4,13 @@ * */ +#include "Common/Clientside/PrettyPrint.h" #include "Common/SwitchFramework/FrameworkSettings.h" #include "Common/SwitchFramework/Switch_PushButtons.h" #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "PokemonSwSh/Inference/PokemonSwSh_BeamSetter.h" +#include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" #include "PokemonSwSh_PurpleBeamFinder.h" namespace PokemonAutomation{ @@ -29,7 +31,7 @@ PurpleBeamFinder::PurpleBeamFinder() ) , DETECTION_THRESHOLD( "Red Beam Detection Threshold:", - 0.01, 0.0, 1.0 + 0.02, 0.0, 1.0 ) , TIMEOUT_DELAY( "Timeout Delay:
Reset if no beam is detected after this long.", @@ -48,16 +50,16 @@ void PurpleBeamFinder::program(SingleSwitchProgramEnvironment& env) const{ pbf_mash_button(BUTTON_B, 100); env.console.botbase().wait_for_all_requests(); - BeamSetter setter(env.console, env.logger); - uint64_t count = 0; - while (true){ - { - QString status = "Red Beams: " + QString::number(count); - env.logger.log(status); - env.set_status(status); - } + uint64_t attempts = 0; + uint64_t red_detected = 0; + uint64_t red_assumed = 0; + uint64_t timed_out = 0; + uint64_t purple = 0; + + bool exit = false; + while (true){ // Talk to den. pbf_press_button(BUTTON_A, 10, 450); if (EXTRA_LINE){ @@ -66,16 +68,50 @@ void PurpleBeamFinder::program(SingleSwitchProgramEnvironment& env) const{ pbf_press_button(BUTTON_A, 10, 300); env.console.botbase().wait_for_all_requests(); - if (setter.run(env, env.console, DETECTION_THRESHOLD, TIMEOUT_DELAY)){ + BeamSetter::Detection detection; + { + BeamSetter setter(env.console, env.logger); + detection = setter.run(env, env.console, DETECTION_THRESHOLD, TIMEOUT_DELAY); + attempts++; + } + switch (detection){ + case BeamSetter::NO_DETECTION: + timed_out++; + break; + case BeamSetter::RED_DETECTED: + red_detected++; + break; + case BeamSetter::RED_ASSUMED: + red_assumed++; + break; + case BeamSetter::PURPLE: + purple++; + exit = true; + break; + } + { + std::string status = "Attempts: " + tostr_u_commas(attempts); + status += ", Timeouts: " + tostr_u_commas(timed_out); + status += ", Red Detected: " + tostr_u_commas(red_detected); + status += ", Red Presumed: " + tostr_u_commas(red_assumed); + status += ", Purple: " + tostr_u_commas(purple); + status += "
"; + QString str = status.c_str(); + env.logger.log(str); + env.set_status(str); + } + if (exit){ break; } pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); - reset_game_from_home(TOLERATE_SYSTEM_UPDATE_MENU_SLOW, 0, false); - - count++; + reset_game_from_home_with_inference( + env, env.logger, env.console, + TOLERATE_SYSTEM_UPDATE_MENU_SLOW + ); } + while (true){ pbf_press_button(BUTTON_B, 20, 20); pbf_press_button(BUTTON_LCLICK, 20, 20); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.cpp index 9033159f4c..68e3447234 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.cpp @@ -13,6 +13,7 @@ #include "Common/PokemonSwSh/PokemonSwShAutoHosts.h" #include "Common/Qt/ExpressionEvaluator.h" #include "CommonFramework/Inference/BlackScreenDetector.h" +#include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" #include "PokemonSwSh_DenTools.h" #include "PokemonSwSh_LobbyWait.h" #include "PokemonSwSh_AutoHost-MultiGame.h" @@ -221,7 +222,8 @@ void AutoHostMultiGame::program(SingleSwitchProgramEnvironment& env) const{ game_slot = game_slot_flipped ? 0 : 2; break; } - start_game_from_home( + start_game_from_home_with_inference( + env, env.logger, env.console, TOLERATE_SYSTEM_UPDATE_MENU_SLOW, game_slot, game.user_slot, diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp index dc5e64ca09..c7afc00f97 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp @@ -11,6 +11,7 @@ #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShAutoHosts.h" #include "CommonFramework/Inference/BlackScreenDetector.h" +#include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" #include "PokemonSwSh_DenTools.h" #include "PokemonSwSh_LobbyWait.h" #include "PokemonSwSh_AutoHost-Rolling.h" @@ -212,7 +213,9 @@ void AutoHostRolling::program(SingleSwitchProgramEnvironment& env) const{ last_touch += TOUCH_DATE_INTERVAL; } rollback_date_from_home(SKIPS); - start_game_from_home( + + start_game_from_home_with_inference( + env, env.logger, env.console, TOLERATE_SYSTEM_UPDATE_MENU_SLOW, 0, 0, BACKUP_SAVE diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp index 1a95d6a9fa..51035f31cf 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp @@ -4,6 +4,7 @@ * */ +#include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" #include "PokemonSwSh_DenTools.h" #include "PokemonSwSh_DenRoller.h" @@ -13,7 +14,7 @@ namespace PokemonSwSh{ DenRoller::DenRoller() : SingleSwitchProgram( - FeedbackType::NONE, PABotBaseLevel::PABOTBASE_12KB, + FeedbackType::OPTIONAL_, PABotBaseLevel::PABOTBASE_12KB, "Den Roller", "NativePrograms/DenRoller.md", "Roll den to the N'th day, SR and repeat." @@ -58,7 +59,11 @@ void DenRoller::program(SingleSwitchProgramEnvironment& env) const{ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE - 10); rollback_date_from_home(SKIPS); - reset_game_from_home(TOLERATE_SYSTEM_UPDATE_MENU_SLOW, 0, false); +// reset_game_from_home(TOLERATE_SYSTEM_UPDATE_MENU_SLOW); + reset_game_from_home_with_inference( + env, env.logger, env.console, + TOLERATE_SYSTEM_UPDATE_MENU_SLOW + ); } end_program_callback(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenTools.h b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenTools.h index 123d1d3ac0..33b47cbb85 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenTools.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenTools.h @@ -102,7 +102,7 @@ static void roll_den( // Skip forward. ssf_press_button2(BUTTON_HOME, GAME_TO_HOME_DELAY_FAST, 10); - home_to_date_time(false); + home_to_date_time(true, false); roll_date_forward_1(false); // Enter game @@ -120,7 +120,7 @@ static void rollback_date_from_home(uint8_t skips){ if (skips > 60){ skips = 60; } - home_to_date_time(false); + home_to_date_time(true, false); roll_date_backward_N(skips, false); // pbf_wait(5); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp new file mode 100644 index 0000000000..0c707c5c43 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp @@ -0,0 +1,203 @@ +/* Start Game + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "CommonFramework/Inference/ImageTools.h" +#include "PokemonSwSh_StartGame.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +void enter_loading_game( + ProgramEnvironment& env, + Logger& logger, + ConsoleHandle& console, + bool backup_save, + uint16_t post_wait_time +){ + // Wait for game to load. + { + std::chrono::milliseconds timeout(START_GAME_WAIT * (1000 / TICKS_PER_SECOND)); + + InferenceBoxScope box0(console, 0.2, 0.2, 0.6, 0.1); + InferenceBoxScope box1(console, 0.2, 0.7, 0.6, 0.1); + + auto start = std::chrono::system_clock::now(); + auto last = start; + bool black_found = false; + while (true){ + env.check_stopping(); + + QImage screen = console.video().snapshot(); + if (screen.isNull()){ + logger.log("start_game_with_inference(): Screenshot failed.", "purple"); + timeout = std::chrono::milliseconds(1000); + }else{ + bool black0 = is_black(extract_box(screen, box0)); + bool black1 = is_black(extract_box(screen, box1)); + if (black0 && black1){ + if (!black_found){ + logger.log("start_game_with_inference(): Game load started.", "purple"); + } + black_found = true; + }else if (black_found){ + break; + } + } + + auto now = std::chrono::system_clock::now(); + if (now - start > timeout){ + logger.log("start_game_with_inference(): Game load timed out. Proceeding with default start delay.", "red"); + break; + } + auto time_since_last_frame = now - last; + if (time_since_last_frame > std::chrono::milliseconds(50)){ + env.wait(std::chrono::milliseconds(50) - time_since_last_frame); + } + last = now; + } + } + + logger.log("start_game_with_inference(): Game Loaded. Entering game...", "purple"); + enter_game(backup_save, ENTER_GAME_MASH, 0); + console.botbase().wait_for_all_requests(); + + // Wait to enter game. + { + std::chrono::milliseconds timeout(ENTER_GAME_WAIT * (1000 / TICKS_PER_SECOND)); + + InferenceBoxScope box(console, 0.2, 0.2, 0.6, 0.6); + + auto start = std::chrono::system_clock::now(); + auto last = start; + bool black_found = false; + while (true){ + env.check_stopping(); + + QImage screen = console.video().snapshot(); + if (screen.isNull()){ + logger.log("start_game_with_inference(): Screenshot failed.", "purple"); + timeout = std::chrono::milliseconds(1000); + }else{ + bool black = is_black(extract_box(screen, box)); + if (black){ + if (!black_found){ + logger.log("start_game_with_inference(): Game entry started.", "purple"); + } + black_found = true; + }else if (black_found){ + break; + } + } + + auto now = std::chrono::system_clock::now(); + if (now - start > timeout){ + logger.log("start_game_with_inference(): Game entry timed out. Proceeding with default start delay.", "red"); + break; + } + auto time_since_last_frame = now - last; + if (time_since_last_frame > std::chrono::milliseconds(50)){ + env.wait(std::chrono::milliseconds(50) - time_since_last_frame); + } + last = now; + } + } + logger.log("start_game_with_inference(): Game started.", "purple"); + + if (post_wait_time != 0){ + pbf_wait(post_wait_time); + } +} + +void start_game_from_home_with_inference( + ProgramEnvironment& env, + Logger& logger, + ConsoleHandle& console, + bool tolerate_update_menu, + uint8_t game_slot, + uint8_t user_slot, + bool backup_save, + uint16_t post_wait_time +){ + if (game_slot != 0){ + pbf_press_button(console, BUTTON_HOME, 10, SETTINGS_TO_HOME_DELAY - 10); + for (uint8_t c = 1; c < game_slot; c++){ + pbf_press_dpad(console, DPAD_RIGHT, 5, 5); + } + } + + if (tolerate_update_menu){ + // If the update menu isn't there, these will get swallowed by the opening + // animation for the select user menu. + pbf_press_button(console, BUTTON_A, 5, 35); // Choose game + pbf_press_dpad(console, DPAD_UP, 5, 0); // Skip the update window. + } + + if (!START_GAME_REQUIRES_INTERNET && user_slot == 0){ + // Mash your way into the game. + pbf_mash_button(console, BUTTON_A, START_GAME_MASH); + }else{ + pbf_press_button(console, BUTTON_A, 5, 175); // Enter select user menu. + if (user_slot != 0){ + // Move to correct user. + for (uint8_t c = 0; c < 8; c++){ + pbf_press_dpad(console, DPAD_LEFT, 7, 7); + } +// pbf_wait(50); + for (uint8_t c = 1; c < user_slot; c++){ + pbf_press_dpad(console, DPAD_RIGHT, 7, 7); + } + } + pbf_press_button(console, BUTTON_A, 5, 5); // Enter game + + // Switch to mashing ZR instead of A to get into the game. + // Mash your way into the game. + uint16_t duration = START_GAME_MASH; + if (START_GAME_REQUIRES_INTERNET){ + // Need to wait a bit longer for the internet check. + duration += START_GAME_INTERNET_CHECK_DELAY; + } + pbf_mash_button(console, BUTTON_ZR, duration); + } + console.botbase().wait_for_all_requests(); + + // Wait for game to load. + enter_loading_game(env, logger, console, backup_save, post_wait_time); +} + +void reset_game_from_home_with_inference( + ProgramEnvironment& env, + Logger& logger, + ConsoleHandle& console, + bool tolerate_update_menu, + uint16_t post_wait_time +){ + if (START_GAME_REQUIRES_INTERNET || tolerate_update_menu){ + close_game(); + start_game_from_home_with_inference( + env, logger, console, tolerate_update_menu, 0, 0, false, post_wait_time + ); + return; + } + + fast_reset_game(console, START_GAME_MASH, 0, 0, 0); + console.botbase().wait_for_all_requests(); + + // Wait for game to load. + enter_loading_game(env, logger, console, false, post_wait_time); +} + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h new file mode 100644 index 0000000000..e492e3f1b2 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h @@ -0,0 +1,45 @@ +/* Start Game + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_StartGame_H +#define PokemonAutomation_PokemonSwSh_StartGame_H + +#include "Common/SwitchFramework/SwitchControllerDefs.h" +#include "CommonFramework/Tools/ConsoleHandle.h" +#include "CommonFramework/Tools/Logger.h" +#include "CommonFramework/Tools/ProgramEnvironment.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +// Start the game with the specified "game_slot" and "user_slot". +// If "game_slot" is zero, it uses whatever the cursor is on. +// If "user_slot" is zero, it uses whatever the cursor is on. +void start_game_from_home_with_inference( + ProgramEnvironment& env, + Logger& logger, + ConsoleHandle& console, + bool tolerate_update_menu, + uint8_t game_slot = 0, + uint8_t user_slot = 0, + bool backup_save = false, + uint16_t post_wait_time = 1 * TICKS_PER_SECOND +); + +void reset_game_from_home_with_inference( + ProgramEnvironment& env, + Logger& logger, + ConsoleHandle& console, + bool tolerate_update_menu, + uint16_t post_wait_time = 1 * TICKS_PER_SECOND +); + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/QoLMacros/PokemonSwSh_FriendSearchDisconnect.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/QoLMacros/PokemonSwSh_FriendSearchDisconnect.cpp index 8fe35a8844..91f7e7849e 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/QoLMacros/PokemonSwSh_FriendSearchDisconnect.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/QoLMacros/PokemonSwSh_FriendSearchDisconnect.cpp @@ -33,12 +33,9 @@ FriendSearchDisconnect::FriendSearchDisconnect() void FriendSearchDisconnect::program(SingleSwitchProgramEnvironment& env) const{ ssf_press_button2(BUTTON_HOME, GAME_TO_HOME_DELAY_SAFE, 10); - home_to_add_friends(USER_SLOT - 1, true); - pbf_wait(50); + home_to_add_friends(USER_SLOT - 1, 1, true); // Enter friend search. - pbf_press_dpad(DPAD_RIGHT, 5, 0); - pbf_move_right_joystick(128, 255, 5, 0); pbf_mash_button(BUTTON_A, 100); settings_to_enter_game(true); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp new file mode 100644 index 0000000000..4766064acc --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp @@ -0,0 +1,71 @@ +/* Encounter Stats + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "PokemonSwSh_EncounterStats.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +EncounterStats::EncounterStats(bool shiny_types) + : m_shiny_types(shiny_types) +{} + +void EncounterStats::log_stats(ProgramEnvironment& env, Logger& logger) const{ + QString str = "" + QString(stats().c_str()) + ""; + env.set_status(str); + logger.log(str); +} +std::string EncounterStats::stats() const{ + std::string str; + str += str_encounters(); + str += str_shinies(); + return str; +} +std::string EncounterStats::str_encounters() const{ + std::string str; + str += "Encounters: " + tostr_u_commas(m_encounters); + return str; +} +std::string EncounterStats::str_shinies() const{ + std::string str; + if (m_shiny_types){ + str += " - Star Shinies: " + tostr_u_commas(m_star_shinies); + str += " - Square Shinies: " + tostr_u_commas(m_square_shinies); + }else{ + str += " - Shinies: " + tostr_u_commas(m_shinies); + } + return str; +} + + + +void EncounterStats::add_non_shiny(){ + m_encounters++; +} +void EncounterStats::add_shiny(){ + m_encounters++; + m_shinies++; +} +void EncounterStats::add_star_shiny(){ + m_encounters++; + m_shinies++; + m_star_shinies++; +} +void EncounterStats::add_square_shiny(){ + m_encounters++; + m_shinies++; + m_square_shinies++; +} + + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h new file mode 100644 index 0000000000..af0d04b297 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h @@ -0,0 +1,48 @@ +/* Encounter Stats + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_EncounterStats_H +#define PokemonAutomation_PokemonSwSh_EncounterStats_H + +#include "CommonFramework/Tools/Logger.h" +#include "CommonFramework/Tools/ProgramEnvironment.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class EncounterStats{ +public: + EncounterStats(bool shiny_types); + virtual ~EncounterStats() = default; + + void log_stats(ProgramEnvironment& env, Logger& logger) const; + virtual std::string stats() const; + + uint64_t encounters() const{ return m_encounters; } + + virtual std::string str_encounters() const; + virtual std::string str_shinies() const; + + void add_non_shiny(); + void add_shiny(); + void add_star_shiny(); + void add_square_shiny(); + +protected: + bool m_shiny_types; + uint64_t m_encounters = 0; + uint64_t m_shinies = 0; + uint64_t m_star_shinies = 0; + uint64_t m_square_shinies = 0; +}; + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp new file mode 100644 index 0000000000..6455db0ce9 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp @@ -0,0 +1,63 @@ +/* Encounter Tracker + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "PokemonSwSh_EncounterTracker.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +StandardEncounterTracker::StandardEncounterTracker( + EncounterStats& stats, + ConsoleHandle& console, + bool require_square, + uint16_t exit_battle_time +) + : m_stats(stats) + , m_console(console) + , m_require_square(require_square) + , m_exit_battle_time(exit_battle_time) +{} + +bool StandardEncounterTracker::run_away(){ + pbf_press_dpad(DPAD_UP, 10, 10); + pbf_press_button(BUTTON_A, 10, 10); + pbf_mash_button(BUTTON_B, m_exit_battle_time); + return true; +} + +bool StandardEncounterTracker::process_result(ShinyEncounterDetector::Detection detection){ + switch (detection){ + case ShinyEncounterDetector::NO_BATTLE_MENU: + return false; + case ShinyEncounterDetector::NOT_SHINY: + m_stats.add_non_shiny(); + run_away(); + return false; + case ShinyEncounterDetector::STAR_SHINY: + m_stats.add_star_shiny(); + pbf_wait(m_console, 5 * TICKS_PER_SECOND); + pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); + if (m_require_square){ + run_away(); + return false; + } + return true; + case ShinyEncounterDetector::SQUARE_SHINY: + m_stats.add_square_shiny(); + pbf_wait(m_console, 5 * TICKS_PER_SECOND); + pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); + return true; + } +} + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h new file mode 100644 index 0000000000..ae2277f10c --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h @@ -0,0 +1,44 @@ +/* Encounter Tracker + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_EncounterTracker_H +#define PokemonAutomation_PokemonSwSh_EncounterTracker_H + +#include "CommonFramework/Tools/ConsoleHandle.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh_EncounterStats.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class StandardEncounterTracker{ +public: + StandardEncounterTracker( + EncounterStats& stats, + ConsoleHandle& console, + bool require_square, + uint16_t exit_battle_time + ); + + virtual bool run_away(); + + bool process_result(ShinyEncounterDetector::Detection detection); + +protected: + EncounterStats& m_stats; + ConsoleHandle& m_console; + bool m_require_square; + uint16_t m_exit_battle_time; +}; + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp new file mode 100644 index 0000000000..71897a13b0 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp @@ -0,0 +1,138 @@ +/* ShinyHuntAutonomous-BerryTree + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh_EncounterTracker.h" +#include "PokemonSwSh_ShinyHuntAutonomous-BerryTree.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +ShinyHuntAutonomousBerryTree::ShinyHuntAutonomousBerryTree() + : SingleSwitchProgram( + FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, + "Shiny Hunt Autonomous - Berry Tree", + "SerialPrograms/ShinyHuntAutonomous-BerryTree.md", + "Automatically hunt for shiny berry tree " + STRING_POKEMON + " using video feedback." + ) + , REQUIRE_SQUARE( + "Require Square:
Stop only for a square shiny. Run from star shinies.", + false + ) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , EXIT_BATTLE_MASH_TIME( + "Exit Battle Time:
After running, wait this long to return to overworld.", + "6 * TICKS_PER_SECOND" + ) +{ + m_options.emplace_back(&REQUIRE_SQUARE, "REQUIRE_SQUARE"); + m_options.emplace_back(&m_advanced_options, ""); + m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); +} + + + + + + +std::string ShinyHuntAutonomousBerryTree::Stats::stats() const{ + std::string str; + str += str_encounters(); + str += " - Timeouts: " + tostr_u_commas(m_timeouts); + str += str_shinies(); + return str; +} + +void ShinyHuntAutonomousBerryTree::program(SingleSwitchProgramEnvironment& env) const{ + grip_menu_connect_go_home(); + + Stats stats; + StandardEncounterTracker tracker(stats, env.console, REQUIRE_SQUARE, EXIT_BATTLE_MASH_TIME); + + uint8_t year = MAX_YEAR; + while (true){ + stats.log_stats(env, env.logger); + + home_roll_date_enter_game_autorollback(&year); + pbf_mash_button(BUTTON_B, 90); + env.console.botbase().wait_for_all_requests(); + + { + StartBattleDetector detector(env.console, std::chrono::seconds(60)); + + // Detect start of battle. + bool timed_out = false; + do{ + if (detector.has_timed_out()){ + env.logger.log("ScreenChangeDetector: Timed out.", Qt::red); + stats.m_timeouts++; + timed_out = true; + break;; + } + pbf_mash_button(BUTTON_A, 10); + env.console.botbase().wait_for_all_requests(); + }while (!detector.detect()); + + pbf_mash_button(BUTTON_B, 5 * TICKS_PER_SECOND); + if (timed_out){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_FAST); + continue; + } + } + + // Detect shiny. + ShinyEncounterDetector::Detection detection; + { + ShinyEncounterDetector detector( + env.console, env.logger, + ShinyEncounterDetector::REGULAR_BATTLE, + std::chrono::seconds(30) + ); + detection = detector.detect(env); + } + + if (tracker.process_result(detection)){ + break; + } + if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + stats.m_timeouts++; + pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); + tracker.run_away(); + } + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_FAST); + } + + stats.log_stats(env, env.logger); + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + + home_to_date_time(false, false); + pbf_press_button(BUTTON_A, 5, 5); + pbf_press_button(BUTTON_A, 5, 10); + pbf_press_button(BUTTON_HOME, 10, SETTINGS_TO_HOME_DELAY); + + end_program_callback(); + end_program_loop(); +} + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h new file mode 100644 index 0000000000..bcd17ac8ee --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h @@ -0,0 +1,41 @@ +/* ShinyHuntAutonomous-BerryTree + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousBerryTree_H +#define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousBerryTree_H + +#include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/BooleanCheckBox.h" +#include "NintendoSwitch/Options/TimeExpression.h" +#include "NintendoSwitch/Framework/SingleSwitchProgram.h" +#include "PokemonSwSh_EncounterStats.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +class ShinyHuntAutonomousBerryTree : public SingleSwitchProgram{ +public: + ShinyHuntAutonomousBerryTree(); + + virtual void program(SingleSwitchProgramEnvironment& env) const override; + +private: + struct Stats : public EncounterStats{ + Stats() : EncounterStats(true) {} + virtual std::string stats() const override; + uint64_t m_timeouts = 0; + }; + + BooleanCheckBox REQUIRE_SQUARE; + SectionDivider m_advanced_options; + TimeExpression EXIT_BATTLE_MASH_TIME; +}; + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp new file mode 100644 index 0000000000..6ad1e6e02e --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp @@ -0,0 +1,156 @@ +/* ShinyHuntAutonomous-Fishing + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh_EncounterTracker.h" +#include "PokemonSwSh_ShinyHuntAutonomous-Fishing.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +ShinyHuntAutonomousFishing::ShinyHuntAutonomousFishing() + : SingleSwitchProgram( + FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, + "Shiny Hunt Autonomous - Fishing", + "SerialPrograms/ShinyHuntAutonomous-BerryTree.md", + "Automatically hunt for shiny berry tree " + STRING_POKEMON + " using video feedback." + ) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , EXIT_BATTLE_MASH_TIME( + "Exit Battle Time:
After running, wait this long to return to overworld and for the fish to reappear.", + "6 * TICKS_PER_SECOND" + ) + , FISH_RESPAWN_TIME( + "Fish Respawn Time:
Wait this long for fish to respawn.", + "4 * TICKS_PER_SECOND" + ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 + ) +{ + m_options.emplace_back(&m_advanced_options, ""); + m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); + m_options.emplace_back(&FISH_RESPAWN_TIME, "FISH_RESPAWN_TIME"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); +} + + + + +std::string ShinyHuntAutonomousFishing::Stats::stats() const{ + std::string str; + str += str_encounters(); + str += " - Misses: " + tostr_u_commas(m_misses); + str += " - Timeouts: " + tostr_u_commas(m_timeouts); + str += " - Unexpected Battles: " + tostr_u_commas(m_unexpected_battles); + str += str_shinies(); + return str; +} + +void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) const{ + grip_menu_connect_go_home(); + resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); + + const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; + uint32_t last_touch = system_clock(); + + Stats stats; + StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); + + while (true){ + stats.log_stats(env, env.logger); + + // Touch the date. + if (TIME_ROLLBACK_HOURS > 0 && system_clock() - last_touch >= PERIOD){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + rollback_hours_from_home(TIME_ROLLBACK_HOURS, SETTINGS_TO_HOME_DELAY); + resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); + last_touch += PERIOD; + } + + pbf_wait(FISH_RESPAWN_TIME); + env.console.botbase().wait_for_all_requests(); + + // Trigger encounter. + { + FishingDetector detector(env.console); + pbf_press_button(BUTTON_A, 10, 10); + pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); + env.console.botbase().wait_for_all_requests(); + FishingDetector::Detection detection = detector.wait_for_detection(env, env.logger); + switch (detection){ + case FishingDetector::NO_DETECTION: + stats.m_timeouts++; + pbf_mash_button(BUTTON_B, 2 * TICKS_PER_SECOND); + continue; + case FishingDetector::HOOKED: + pbf_press_button(BUTTON_A, 10, 0); + break; + case FishingDetector::MISSED: + stats.m_misses++; + pbf_mash_button(BUTTON_B, 2 * TICKS_PER_SECOND); + continue; + case FishingDetector::BATTLE_MENU: + stats.m_unexpected_battles++; + tracker.run_away(); + continue; + } + env.wait(std::chrono::seconds(3)); + detection = detector.detect_now(); + if (detection == FishingDetector::MISSED){ + stats.m_misses++; + pbf_mash_button(BUTTON_B, 2 * TICKS_PER_SECOND); + continue; + } + } + + // Detect shiny. + ShinyEncounterDetector::Detection detection; + { + ShinyEncounterDetector detector( + env.console, env.logger, + ShinyEncounterDetector::REGULAR_BATTLE, + std::chrono::seconds(30) + ); + detection = detector.detect(env); + } + + if (tracker.process_result(detection)){ + break; + } + if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + stats.m_timeouts++; + pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); + tracker.run_away(); + } + } + + stats.log_stats(env, env.logger); + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + + end_program_callback(); + end_program_loop(); +} + + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h new file mode 100644 index 0000000000..09c363efa8 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h @@ -0,0 +1,46 @@ +/* ShinyHuntAutonomous-Fishing + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousFishing_H +#define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousFishing_H + +#include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/SimpleInteger.h" +#include "NintendoSwitch/Options/TimeExpression.h" +#include "NintendoSwitch/Framework/SingleSwitchProgram.h" +#include "PokemonSwSh_EncounterStats.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class ShinyHuntAutonomousFishing : public SingleSwitchProgram{ +public: + ShinyHuntAutonomousFishing(); + + virtual void program(SingleSwitchProgramEnvironment& env) const override; + +private: + struct Stats : public EncounterStats{ + Stats() : EncounterStats(true) {} + virtual std::string stats() const override; + uint64_t m_misses = 0; + uint64_t m_timeouts = 0; + uint64_t m_unexpected_battles = 0; + }; + + SectionDivider m_advanced_options; + TimeExpression EXIT_BATTLE_MASH_TIME; + TimeExpression FISH_RESPAWN_TIME; + SimpleInteger TIME_ROLLBACK_HOURS; +}; + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp new file mode 100644 index 0000000000..1809390fa6 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp @@ -0,0 +1,129 @@ +/* ShinyHuntAutonomous-IoATrade + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h" +#include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" +#include "PokemonSwSh_ShinyHuntAutonomous-IoATrade.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +ShinyHuntAutonomousIoATrade::ShinyHuntAutonomousIoATrade() + : SingleSwitchProgram( + FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, + "Shiny Hunt Autonomous - IoATrade", + "SerialPrograms/ShinyHuntAutonomous-IoATrade.md", + "Hunt for shiny Isle of Armor trade using video feedback." + ) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , MASH_TO_TRADE_DELAY( + "Mash to Trade Delay:
Time to perform the trade.", + "29 * TICKS_PER_SECOND" + ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) +{ + m_options.emplace_back(&m_advanced_options, ""); + m_options.emplace_back(&MASH_TO_TRADE_DELAY, "MASH_TO_TRADE_DELAY"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); +} + + + +std::string ShinyHuntAutonomousIoATrade::Stats::stats() const{ + std::string str; + str += "Trades: " + tostr_u_commas(m_encounters); + str += " - Errors: " + tostr_u_commas(m_errors); + str += str_shinies(); + return str; +} + + +void ShinyHuntAutonomousIoATrade::program(SingleSwitchProgramEnvironment& env) const{ + grip_menu_connect_go_home(); + resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 500); + + uint32_t last_touch = system_clock() - TOUCH_DATE_INTERVAL; + + Stats stats; + + while (true){ + stats.log_stats(env, env.logger); + + pbf_press_button(BUTTON_A, 10, 100); + pbf_press_button(BUTTON_A, 10, 50); + pbf_press_button(BUTTON_A, 10, 100); + pbf_press_button(BUTTON_A, 10, 50); + pbf_press_button(BUTTON_A, 10, POKEMON_TO_BOX_DELAY); + pbf_press_dpad(DPAD_LEFT, 10, 10); + pbf_mash_button(BUTTON_A, MASH_TO_TRADE_DELAY); + + // Enter box system. + pbf_press_button(BUTTON_X, 10, OVERWORLD_TO_MENU_DELAY); + pbf_press_dpad(DPAD_RIGHT, 10, 10); + pbf_press_button(BUTTON_A, 10, MENU_TO_POKEMON_DELAY); + + // View summary. + pbf_press_button(BUTTON_A, 10, 100); + pbf_press_button(BUTTON_A, 10, 0); + env.console.botbase().wait_for_all_requests(); + + SummaryShinySymbolDetector::Detection detection; + { + SummaryShinySymbolDetector detector(env.console, env.logger); + detection = detector.wait_for_detection(env); + } + switch (detection){ + case SummaryShinySymbolDetector::NO_DETECTION: + stats.m_errors++; + break; + case SummaryShinySymbolDetector::NOT_SHINY: + stats.add_non_shiny(); + break; + case SummaryShinySymbolDetector::SHINY: + stats.add_shiny(); + pbf_wait(1 * TICKS_PER_SECOND); + pbf_press_button(BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); + goto StopProgram; + } + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + if (TOUCH_DATE_INTERVAL > 0 && system_clock() - last_touch >= TOUCH_DATE_INTERVAL){ + env.logger.log("Touching date to prevent rollover."); + touch_date_from_home(SETTINGS_TO_HOME_DELAY); + last_touch += TOUCH_DATE_INTERVAL; + } + reset_game_from_home_with_inference( + env, env.logger, env.console, + TOLERATE_SYSTEM_UPDATE_MENU_FAST + ); + } + +StopProgram: + stats.log_stats(env, env.logger); + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + + end_program_callback(); + end_program_loop(); +} + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h new file mode 100644 index 0000000000..ae00de385e --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h @@ -0,0 +1,41 @@ +/* ShinyHuntAutonomous-IoATrade + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousIoATrade_H +#define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousIoATrade_H + +#include "CommonFramework/Options/SectionDivider.h" +#include "NintendoSwitch/Options/TimeExpression.h" +#include "NintendoSwitch/Framework/SingleSwitchProgram.h" +#include "PokemonSwSh_EncounterStats.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +class ShinyHuntAutonomousIoATrade : public SingleSwitchProgram{ +public: + ShinyHuntAutonomousIoATrade(); + + virtual void program(SingleSwitchProgramEnvironment& env) const override; + +private: + struct Stats : public EncounterStats{ + Stats() : EncounterStats(false) {} + virtual std::string stats() const override; + uint64_t m_errors = 0; + }; + + SectionDivider m_advanced_options; + TimeExpression MASH_TO_TRADE_DELAY; + TimeExpression TOUCH_DATE_INTERVAL; +}; + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp new file mode 100644 index 0000000000..70387fabdc --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp @@ -0,0 +1,139 @@ +/* ShinyHuntAutonomous-Regi + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh_EncounterTracker.h" +#include "PokemonSwSh_ShinyHunt-Regi.h" +#include "PokemonSwSh_ShinyHuntAutonomous-Regi.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +ShinyHuntAutonomousRegi::ShinyHuntAutonomousRegi() + : SingleSwitchProgram( + FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, + "Shiny Hunt Autonomous - Regi", + "SerialPrograms/ShinyHuntAutonomous-Regi.md", + "Automatically hunt for shiny Regi using video feedback." + ) + , REQUIRE_SQUARE( + "Require Square:
Stop only for a square shiny. Run from star shinies.", + false + ) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , EXIT_BATTLE_MASH_TIME( + "Exit Battle Time:
After running, wait this long to return to overworld.", + "6 * TICKS_PER_SECOND" + ) + , TRANSITION_DELAY( + "Transition Delay:
Time to enter/exit the building.", + "5 * TICKS_PER_SECOND" + ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) +{ + m_options.emplace_back(®I_NAME, "REGI_NAME"); + m_options.emplace_back(&REQUIRE_SQUARE, "REQUIRE_SQUARE"); + m_options.emplace_back(&m_advanced_options, ""); + m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); + m_options.emplace_back(&TRANSITION_DELAY, "TRANSITION_DELAY"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); +} + + + + +std::string ShinyHuntAutonomousRegi::Stats::stats() const{ + std::string str; + str += str_encounters(); + str += " - Light Resets: " + tostr_u_commas(m_light_resets); + str += str_shinies(); + return str; +} + + +void ShinyHuntAutonomousRegi::program(SingleSwitchProgramEnvironment& env) const{ + grip_menu_connect_go_home(); + resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 200); + + Stats stats; + StandardEncounterTracker tracker(stats, env.console, REQUIRE_SQUARE, EXIT_BATTLE_MASH_TIME); + + uint32_t last_touch = system_clock() - TOUCH_DATE_INTERVAL; + bool error = false; + while (true){ + stats.log_stats(env, env.logger); + + move_to_corner(env, error, TRANSITION_DELAY); + if (error){ + stats.m_light_resets++; + stats.log_stats(env, env.logger); + error = false; + } + + // Touch the date. + if (TOUCH_DATE_INTERVAL > 0 && system_clock() - last_touch >= TOUCH_DATE_INTERVAL){ + env.logger.log("Touching date to prevent rollover."); + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + touch_date_from_home(SETTINGS_TO_HOME_DELAY); + resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); + last_touch += TOUCH_DATE_INTERVAL; + } + + // Do the light puzzle. + run_regi_light_puzzle(env, REGI_NAME, stats.encounters()); + + // Start the encounter. + pbf_mash_button(BUTTON_A, 5 * TICKS_PER_SECOND); + env.console.botbase().wait_for_all_requests(); + + // Detect shiny. + ShinyEncounterDetector::Detection detection; + { + ShinyEncounterDetector detector( + env.console, env.logger, + ShinyEncounterDetector::REGULAR_BATTLE, + std::chrono::seconds(30) + ); + detection = detector.detect(env); + } + + if (tracker.process_result(detection)){ + break; + } + if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); + tracker.run_away(); + error = true; + } + } + + stats.log_stats(env, env.logger); + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + + end_program_callback(); + end_program_loop(); +} + + + +} +} +} + + diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h new file mode 100644 index 0000000000..53aa5f82f5 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h @@ -0,0 +1,46 @@ +/* ShinyHuntAutonomous-Regi + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousRegi_H +#define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousRegi_H + +#include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/BooleanCheckBox.h" +#include "CommonFramework/Options/SimpleInteger.h" +#include "NintendoSwitch/Options/TimeExpression.h" +#include "NintendoSwitch/Framework/SingleSwitchProgram.h" +#include "PokemonSwSh/Options/RegiSelector.h" +#include "PokemonSwSh_EncounterStats.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +class ShinyHuntAutonomousRegi : public SingleSwitchProgram{ +public: + ShinyHuntAutonomousRegi(); + + virtual void program(SingleSwitchProgramEnvironment& env) const override; + +private: + struct Stats : public EncounterStats{ + Stats() : EncounterStats(true) {} + virtual std::string stats() const override; + uint64_t m_light_resets = 0; + }; + + RegiSelector REGI_NAME; + BooleanCheckBox REQUIRE_SQUARE; + SectionDivider m_advanced_options; + TimeExpression EXIT_BATTLE_MASH_TIME; + TimeExpression TRANSITION_DELAY; + TimeExpression TOUCH_DATE_INTERVAL; +}; + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp new file mode 100644 index 0000000000..2941bba3e0 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp @@ -0,0 +1,175 @@ +/* ShinyHuntAutonomous-Regigigas2 + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" +#include "PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +ShinyHuntAutonomousRegigigas2::ShinyHuntAutonomousRegigigas2() + : SingleSwitchProgram( + FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, + "Shiny Hunt Autonomous - Regigigas2", + "SerialPrograms/ShinyHuntAutonomous-Regigigas2.md", + "Automatically hunt for shiny Regigigas using video feedback." + ) + , REVERSAL_PP( + "Reversal PP:
The amount of Reversal PP you are saved with.", + 24 + ) + , REQUIRE_SQUARE( + "Require Square:
Stop only for a square shiny. Run from star shinies.", + false + ) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , CATCH_TO_OVERWORLD_DELAY( + "Catch to Overworld Delay:", + "8 * TICKS_PER_SECOND" + ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) +{ + m_options.emplace_back(&REVERSAL_PP, "REVERSAL_PP"); + m_options.emplace_back(&REQUIRE_SQUARE, "REQUIRE_SQUARE"); + m_options.emplace_back(&m_advanced_options, ""); + m_options.emplace_back(&CATCH_TO_OVERWORLD_DELAY, "CATCH_TO_OVERWORLD_DELAY"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); +} + + + + +std::string ShinyHuntAutonomousRegigigas2::Stats::stats() const{ + std::string str; + str += str_encounters(); + str += " - Timeouts: " + tostr_u_commas(m_timeouts); + str += str_shinies(); + return str; +} + +ShinyHuntAutonomousRegigigas2::Tracker::Tracker( + EncounterStats& stats, + ProgramEnvironment& env, + Logger& logger, + ConsoleHandle& console, + bool require_square, + uint16_t exit_battle_time +) + : StandardEncounterTracker(stats, console, require_square, exit_battle_time) + , m_env(env) + , m_logger(logger) +{} +bool ShinyHuntAutonomousRegigigas2::Tracker::run_away(){ + RaidCatchDetector detector(m_console, std::chrono::seconds(30)); + pbf_mash_button(BUTTON_A, 4 * TICKS_PER_SECOND); + + if (!detector.wait(m_env)){ + m_logger.log("Raid Catch Menu not found.", Qt::red); + return false; + } + + pbf_press_dpad(DPAD_DOWN, 10, 0); + pbf_press_button(BUTTON_A, 10, m_exit_battle_time); + return true; +} + +bool ShinyHuntAutonomousRegigigas2::kill_and_return(SingleSwitchProgramEnvironment& env) const{ + RaidCatchDetector detector(env.console, std::chrono::seconds(30)); + pbf_mash_button(BUTTON_A, 4 * TICKS_PER_SECOND); + + if (!detector.wait(env)){ + env.logger.log("Raid Catch Menu not found.", Qt::red); + return false; + } + + pbf_press_dpad(DPAD_DOWN, 10, 0); + pbf_press_button(BUTTON_A, 10, CATCH_TO_OVERWORLD_DELAY); + return true; +} +void ShinyHuntAutonomousRegigigas2::program(SingleSwitchProgramEnvironment& env) const{ + grip_menu_connect_go_home(); + + uint32_t last_touch = system_clock(); + if (TOUCH_DATE_INTERVAL > 0){ + touch_date_from_home(SETTINGS_TO_HOME_DELAY); + } + + resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 500); + + Stats stats; + Tracker tracker(stats, env, env.logger, env.console, REQUIRE_SQUARE, 0); + + while (true){ + for (uint8_t pp = REVERSAL_PP; pp > 0; pp--){ + stats.log_stats(env, env.logger); + + env.logger.log("Starting Regigigas Encounter: " + tostr_u_commas(stats.encounters() + 1)); + + pbf_mash_button(BUTTON_A, 10 * TICKS_PER_SECOND); + env.console.botbase().wait_for_all_requests(); + + ShinyEncounterDetector::Detection detection; + { + ShinyEncounterDetector detector( + env.console, env.logger, + ShinyEncounterDetector::RAID_BATTLE, + std::chrono::seconds(30) + ); + detection = detector.detect(env); + } + + if (tracker.process_result(detection)){ + goto StopProgram; + } + if (detection == ShinyEncounterDetector::NO_BATTLE_MENU || !tracker.run_away()){ + stats.m_timeouts++; + break; + } + } + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + if (TOUCH_DATE_INTERVAL > 0 && system_clock() - last_touch >= TOUCH_DATE_INTERVAL){ + touch_date_from_home(SETTINGS_TO_HOME_DELAY); + last_touch += TOUCH_DATE_INTERVAL; + } + reset_game_from_home_with_inference( + env, env.logger, env.console, + TOLERATE_SYSTEM_UPDATE_MENU_FAST + ); + } + + +StopProgram: + stats.log_stats(env, env.logger); + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + + end_program_callback(); + end_program_loop(); +} + + + +} +} +} + + diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h new file mode 100644 index 0000000000..e2c73656ed --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h @@ -0,0 +1,62 @@ +/* ShinyHuntAutonomous-Regigigas2 + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousRegigigas2_H +#define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousRegigigas2_H + +#include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/BooleanCheckBox.h" +#include "CommonFramework/Options/SimpleInteger.h" +#include "NintendoSwitch/Options/TimeExpression.h" +#include "NintendoSwitch/Framework/SingleSwitchProgram.h" +#include "PokemonSwSh_EncounterStats.h" +#include "PokemonSwSh_EncounterTracker.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +class ShinyHuntAutonomousRegigigas2 : public SingleSwitchProgram{ +public: + ShinyHuntAutonomousRegigigas2(); + + virtual void program(SingleSwitchProgramEnvironment& env) const override; + +private: + bool kill_and_return(SingleSwitchProgramEnvironment& env) const; + +private: + struct Stats : public EncounterStats{ + Stats() : EncounterStats(true) {} + virtual std::string stats() const override; + uint64_t m_timeouts = 0; + }; + struct Tracker : public StandardEncounterTracker{ + Tracker( + EncounterStats& stats, + ProgramEnvironment& env, + Logger& logger, + ConsoleHandle& console, + bool require_square, + uint16_t exit_battle_time + ); + virtual bool run_away() override; + + ProgramEnvironment& m_env; + Logger& m_logger; + }; + + SimpleInteger REVERSAL_PP; + BooleanCheckBox REQUIRE_SQUARE; + SectionDivider m_advanced_options; + TimeExpression CATCH_TO_OVERWORLD_DELAY; + TimeExpression TOUCH_DATE_INTERVAL; +}; + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp new file mode 100644 index 0000000000..9baa48e644 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp @@ -0,0 +1,122 @@ +/* ShinyHuntAutonomous-StrongSpawn + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" +#include "PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +ShinyHuntAutonomousStrongSpawn::ShinyHuntAutonomousStrongSpawn() + : SingleSwitchProgram( + FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, + "Shiny Hunt Autonomous - Strong Spawn", + "SerialPrograms/ShinyHuntAutonomous-StrongSpawn.md", + "Automatically hunt for shiny strong spawns using video feedback." + ) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , EXIT_BATTLE_MASH_TIME( + "Exit Battle Time:
After running, wait this long to return to overworld.", + "6 * TICKS_PER_SECOND" + ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 + ) +{ + m_options.emplace_back(&m_advanced_options, ""); + m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); +} + + +std::string ShinyHuntAutonomousStrongSpawn::Stats::stats() const{ + std::string str; + str += str_encounters(); + str += " - Timeouts: " + tostr_u_commas(m_timeouts); + str += str_shinies(); + return str; +} + +ShinyHuntAutonomousStrongSpawn::Tracker::Tracker(EncounterStats& stats, ConsoleHandle& console) + : StandardEncounterTracker(stats, console, false, 0) +{} +bool ShinyHuntAutonomousStrongSpawn::Tracker::run_away(){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + return true; +} + +void ShinyHuntAutonomousStrongSpawn::program(SingleSwitchProgramEnvironment& env) const{ + grip_menu_connect_go_home(); +// resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); + + const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; + uint32_t last_touch = system_clock(); + + Stats stats; + Tracker tracker(stats, env.console); + + while (true){ + stats.log_stats(env, env.logger); + + uint32_t now = system_clock(); + if (TIME_ROLLBACK_HOURS > 0 && now - last_touch >= PERIOD){ + rollback_hours_from_home(TIME_ROLLBACK_HOURS, SETTINGS_TO_HOME_DELAY); + last_touch += PERIOD; + } + reset_game_from_home_with_inference( + env, env.logger, env.console, + TOLERATE_SYSTEM_UPDATE_MENU_FAST + ); + env.logger.log("Starting Encounter: " + tostr_u_commas(stats.encounters() + 1)); + env.console.botbase().wait_for_all_requests(); + + // Detect shiny. + ShinyEncounterDetector::Detection detection; + { + ShinyEncounterDetector detector( + env.console, env.logger, + ShinyEncounterDetector::REGULAR_BATTLE, + std::chrono::seconds(60) + ); + detection = detector.detect(env); + } + + if (tracker.process_result(detection)){ + break; + } + if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + stats.m_timeouts++; + tracker.run_away(); + } + } + + stats.log_stats(env, env.logger); + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + + end_program_callback(); + end_program_loop(); +} + + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h new file mode 100644 index 0000000000..655d9ddbc7 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h @@ -0,0 +1,47 @@ +/* ShinyHuntAutonomous-StrongSpawn + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousStrongSpawn_H +#define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousStrongSpawn_H + +#include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/BooleanCheckBox.h" +#include "CommonFramework/Options/SimpleInteger.h" +#include "NintendoSwitch/Options/TimeExpression.h" +#include "NintendoSwitch/Framework/SingleSwitchProgram.h" +#include "PokemonSwSh_EncounterStats.h" +#include "PokemonSwSh_EncounterTracker.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +class ShinyHuntAutonomousStrongSpawn : public SingleSwitchProgram{ +public: + ShinyHuntAutonomousStrongSpawn(); + + virtual void program(SingleSwitchProgramEnvironment& env) const override; + +private: + struct Stats : public EncounterStats{ + Stats() : EncounterStats(true) {} + virtual std::string stats() const override; + uint64_t m_timeouts = 0; + }; + struct Tracker : public StandardEncounterTracker{ + Tracker(EncounterStats& stats, ConsoleHandle& console); + virtual bool run_away() override; + }; + + SectionDivider m_advanced_options; + TimeExpression EXIT_BATTLE_MASH_TIME; + SimpleInteger TIME_ROLLBACK_HOURS; +}; + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp new file mode 100644 index 0000000000..623ca20ea7 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp @@ -0,0 +1,134 @@ +/* ShinyHuntAutonomous-SwordsOfJustice + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh_EncounterTracker.h" +#include "PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +ShinyHuntAutonomousSwordsOfJustice::ShinyHuntAutonomousSwordsOfJustice() + : SingleSwitchProgram( + FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, + "Shiny Hunt Autonomous - Swords Of Justice", + "SerialPrograms/ShinyHuntAutonomous-SwordsOfJustice.md", + "Automatically hunt for shiny Sword of Justice using video feedback." + ) + , AIRPLANE_MODE( + "Airplane Mode:
Enable if airplane mode is on.", + false + ) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , EXIT_BATTLE_MASH_TIME( + "Exit Battle Time:
After running, wait this long to return to overworld.", + "6 * TICKS_PER_SECOND" + ) + , ENTER_CAMP_DELAY( + "Enter Camp Delay:", + "8 * TICKS_PER_SECOND" + ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 + ) +{ + m_options.emplace_back(&AIRPLANE_MODE, "AIRPLANE_MODE"); + m_options.emplace_back(&m_advanced_options, ""); + m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); + m_options.emplace_back(&ENTER_CAMP_DELAY, "ENTER_CAMP_DELAY"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); +} + + +std::string ShinyHuntAutonomousSwordsOfJustice::Stats::stats() const{ + std::string str; + str += str_encounters(); + str += " - Timeouts: " + tostr_u_commas(m_timeouts); + str += str_shinies(); + return str; +} + +void ShinyHuntAutonomousSwordsOfJustice::program(SingleSwitchProgramEnvironment& env) const{ + grip_menu_connect_go_home(); + resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); + + const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; + uint32_t last_touch = system_clock(); + + Stats stats; + StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); + + while (true){ + stats.log_stats(env, env.logger); + + // Touch the date. + if (TIME_ROLLBACK_HOURS > 0 && system_clock() - last_touch >= PERIOD){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + rollback_hours_from_home(TIME_ROLLBACK_HOURS, SETTINGS_TO_HOME_DELAY); + resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); + last_touch += PERIOD; + } + + // Trigger encounter. + pbf_press_button(BUTTON_X, 10, OVERWORLD_TO_MENU_DELAY); + pbf_press_button(BUTTON_A, 10, ENTER_CAMP_DELAY); + if (AIRPLANE_MODE){ + pbf_press_button(BUTTON_A, 10, 100); + pbf_press_button(BUTTON_A, 10, 100); + } + pbf_press_button(BUTTON_X, 10, 50); + pbf_press_dpad(DPAD_LEFT, 10, 10); + env.logger.log("Starting Encounter: " + tostr_u_commas(stats.encounters() + 1)); + pbf_press_button(BUTTON_A, 10, 0); + env.console.botbase().wait_for_all_requests(); + + // Detect shiny. + ShinyEncounterDetector::Detection detection; + { + ShinyEncounterDetector detector( + env.console, env.logger, + ShinyEncounterDetector::REGULAR_BATTLE, + std::chrono::seconds(30) + ); + detection = detector.detect(env); + } + + if (tracker.process_result(detection)){ + break; + } + if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + stats.m_timeouts++; + pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); + tracker.run_away(); + } + } + + stats.log_stats(env, env.logger); + + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + + end_program_callback(); + end_program_loop(); +} + + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h new file mode 100644 index 0000000000..d284768f63 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h @@ -0,0 +1,44 @@ +/* ShinyHuntAutonomous-SwordsOfJustice + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousSwordsOfJustice_H +#define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousSwordsOfJustice_H + +#include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/BooleanCheckBox.h" +#include "CommonFramework/Options/SimpleInteger.h" +#include "NintendoSwitch/Options/TimeExpression.h" +#include "NintendoSwitch/Framework/SingleSwitchProgram.h" +#include "PokemonSwSh_EncounterStats.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +class ShinyHuntAutonomousSwordsOfJustice : public SingleSwitchProgram{ +public: + ShinyHuntAutonomousSwordsOfJustice(); + + virtual void program(SingleSwitchProgramEnvironment& env) const override; + +private: + struct Stats : public EncounterStats{ + Stats() : EncounterStats(true) {} + virtual std::string stats() const override; + uint64_t m_timeouts = 0; + }; + + BooleanCheckBox AIRPLANE_MODE; + SectionDivider m_advanced_options; + TimeExpression EXIT_BATTLE_MASH_TIME; + TimeExpression ENTER_CAMP_DELAY; + SimpleInteger TIME_ROLLBACK_HOURS; +}; + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp index b5e08d75e6..b8190f2ebc 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp @@ -10,6 +10,21 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/FillGeometry.h" +#include "CommonFramework/Inference/AnomalyDetector.h" +#include "CommonFramework/Inference/ColorClustering.h" +#include "CommonFramework/Inference/FloatStatAccumulator.h" +#include "CommonFramework/Inference/TimeWindowStatTracker.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h" +#include "PokemonSwSh_StartGame.h" #include "TestProgram.h" #include @@ -34,16 +49,127 @@ TestProgram::TestProgram() - - void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ BotBase& botbase = env.console; VideoFeed& feed = env.console; +// start_game_from_home_with_inference(env, env.logger, env.console, true, 0, 0, true); + #if 0 - RaidCatchDetector detector(feed, std::chrono::seconds(60)); + StandardBattleMenuDetector detector(feed); + cout << "Battle Menu = " << detector.detect(feed.snapshot()) << endl; + env.wait(std::chrono::seconds(600)); +#endif + +#if 0 + FishingDetector detector(feed); + detector.wait_for_detection(env, env.logger); +#endif + + +#if 0 + QImage screen("FishingBig.jpg"); + + FillMatrix matrix(screen); + PinkFilter2 filter; + matrix.apply_filter(screen, filter); + screen.save("test.png"); + + std::vector objects = find_all_objects(matrix, false); + std::multimap candidate_top; + std::multimap candidate_bot; + for (const FillGeometry& object : objects){ + ImageStats stats = object_stats(screen, matrix, object); + + double aspect_ratio = (double)object.box.width() / object.box.height(); + FloatPixel color_ratio = stats.average / stats.average.sum(); + double stddev = stats.stddev.sum(); + +#if 0 + cout << object.area << " : [" << object.center_x << "," << object.center_y + << "][" << object.box.width() << " x " << object.box.height() + << "], mean = " << stats.average / stats.average.sum() + << ", stddev = " << stats.stddev << endl; +#endif + + if (0.4 < aspect_ratio && aspect_ratio < 0.6 && + stddev < 50 && + euclidean_distance(color_ratio, FloatPixel(0.56, 0.14, 0.30)) < 0.2 + ){ +// cout << "top" << endl; + candidate_top.emplace(object.area, object); + } + if (1.0 < aspect_ratio && aspect_ratio < 1.5 && + stddev < 25 && + euclidean_distance(color_ratio, FloatPixel(0.56, 0.21, 0.23)) < 0.2 + ){ +// cout << "bottom" << endl; + candidate_bot.emplace(object.area, object); + } + } + + for (auto iter = candidate_top.rbegin(); iter != candidate_top.rend(); ++iter){ + const FillGeometry& top = iter->second; + size_t top_area = top.area; + size_t area_low = top_area / 7.; + size_t area_high = top_area / 5.; +// cout << "area = " << top_area << ", low = " << area_low << ", high = " << area_high << endl; + auto iter0 = candidate_bot.lower_bound(area_low); + auto iter1 = candidate_bot.upper_bound(area_high); + for (; iter0 != iter1; ++iter0){ + const FillGeometry& bottom = iter0->second; + +// cout << "top_area = " << top_area << ", bot_area = " << bottom.area << endl; + + // Verify that top is above bottom. + if (top.box.max_y >= bottom.box.min_y){ + continue; + } +// cout << "check 1" << endl; + + // Verify bottom is left of the top. + if (top.center_x <= bottom.center_x){ + continue; + } + + // Make sure horizontal alignment is reasonable. + int mid = (top.box.min_x + top.box.max_x) / 2; + if (std::abs(bottom.box.max_x - mid) * 5 > top.box.width()){ + continue; + } + + // Make sure vertical alignment is reasonable. + if (top.box.max_y + top.box.height() <= bottom.box.min_y){ + continue; + } + + cout << "match!" << endl; + } + } +#endif + + +#if 0 + SummaryShinySymbolDetector detector(feed, env.logger); + + detector.wait_for_detection(env); + + env.wait(std::chrono::seconds(600)); +#endif + +#if 1 + ShinyEncounterDetector detector( + feed, env.logger, + ShinyEncounterDetector::REGULAR_BATTLE, + std::chrono::seconds(60) + ); + detector.detect(env); +#endif +#if 0 + RaidCatchDetector detector(feed, std::chrono::seconds(60)); + size_t c = 0; while (true){ auto start = std::chrono::system_clock::now(); @@ -69,7 +195,6 @@ void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ #endif - // QImage image = feed.snapshot(); // cout << detector.detect(image) << endl; // cout << "box1 = " << cluster_distance_2(extract_box(image, box1), qRgb(255, 255, 255), qRgb(90, 180, 90)) << endl; From 7f6829f32530dc63e292432e4df472d512d9d961 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Thu, 25 Mar 2021 23:49:56 -0500 Subject: [PATCH 02/13] - Fix hang when SR'ing without feedback. - Fix inference throttling. - Adjust shiny detection weights. - Fix some compiler errors reported by pifopi. --- Common/Clientside/Unicode.cpp | 1 - Common/Qt/Options/FossilTableOption.cpp | 2 +- Common/Qt/Options/MultiHostTableOption.cpp | 2 +- .../Source/Options/EnumDropdown.cpp | 2 +- GeneratorSource/Source/Panels/JsonProgram.cpp | 2 +- .../Source/Panels/JsonSettings.cpp | 2 +- SerialPrograms/SerialPrograms.pro | 1 + .../Source/CommonFramework/Globals.cpp | 2 +- .../Inference/InferenceThrottler.h | 61 +++++++++++++++++++ .../Inference/PokemonSwSh_BeamSetter.cpp | 12 ++-- .../Inference/PokemonSwSh_FishingDetector.cpp | 15 ++--- ...PokemonSwSh_SummaryShinySymbolDetector.cpp | 14 ++--- .../PokemonSwSh_ShinyEncounterDetector.cpp | 24 ++++---- .../PokemonSwSh_ShinyEncounterDetector.h | 2 +- .../PokemonSwSh_ShinyTrigger.cpp | 6 +- .../PokemonSwSh_PurpleBeamFinder.cpp | 8 +-- .../Programs/PokemonSwSh_StartGame.cpp | 41 +++++-------- .../PokemonSwSh_EncounterTracker.cpp | 1 + ...okemonSwSh_ShinyHuntAutonomous-Fishing.cpp | 4 +- 19 files changed, 121 insertions(+), 81 deletions(-) create mode 100644 SerialPrograms/Source/CommonFramework/Inference/InferenceThrottler.h diff --git a/Common/Clientside/Unicode.cpp b/Common/Clientside/Unicode.cpp index fbe728a003..a1fbfc8880 100644 --- a/Common/Clientside/Unicode.cpp +++ b/Common/Clientside/Unicode.cpp @@ -11,7 +11,6 @@ namespace PokemonAutomation{ const uint32_t MAX_CODEPOINT = 0x10ffff; const uint32_t REPLACEMENT = 0xfffd; -const char REPLACEMENT_UTF8[] = "\xef\xbf\xbd"; void utf8_skip_to_next_codepoint(const char*& str){ while (true){ diff --git a/Common/Qt/Options/FossilTableOption.cpp b/Common/Qt/Options/FossilTableOption.cpp index d21994a0d8..05b28db0ce 100644 --- a/Common/Qt/Options/FossilTableOption.cpp +++ b/Common/Qt/Options/FossilTableOption.cpp @@ -37,7 +37,7 @@ const std::map FOSSIL_MAP{ std::vector parse_fossil_slot(const QJsonValue& json){ std::vector list; - for (const auto& item : json_cast_array(json)){ + for (const auto item : json_cast_array(json)){ QJsonObject line = json_cast_object(item); FossilTableOption::GameSlot slot; diff --git a/Common/Qt/Options/MultiHostTableOption.cpp b/Common/Qt/Options/MultiHostTableOption.cpp index 97ef9721df..a5c91c03db 100644 --- a/Common/Qt/Options/MultiHostTableOption.cpp +++ b/Common/Qt/Options/MultiHostTableOption.cpp @@ -31,7 +31,7 @@ const QString MultiHostTableOption::JSON_POST_RAID_DELAY = "post_raid_delay"; std::vector parse_game_slot(const QJsonValue& json){ std::vector list; - for (const auto& item : json_cast_array(json)){ + for (const auto item : json_cast_array(json)){ QJsonObject line = json_cast_object(item); MultiHostTableOption::GameSlot slot; diff --git a/GeneratorSource/Source/Options/EnumDropdown.cpp b/GeneratorSource/Source/Options/EnumDropdown.cpp index 5d00ca9ae1..ba54d9d8b5 100644 --- a/GeneratorSource/Source/Options/EnumDropdown.cpp +++ b/GeneratorSource/Source/Options/EnumDropdown.cpp @@ -36,7 +36,7 @@ EnumDropdown::EnumDropdown(const QJsonObject& obj) : SingleStatementOption(obj) { QJsonArray options = json_get_array(obj, JSON_OPTIONS); - for (const auto& option : options){ + for (const auto option : options){ if (!option.isArray()){ throw StringException("Config Error - Expected Array: " + JSON_OPTIONS); } diff --git a/GeneratorSource/Source/Panels/JsonProgram.cpp b/GeneratorSource/Source/Panels/JsonProgram.cpp index 83d16d6756..d98199d181 100644 --- a/GeneratorSource/Source/Panels/JsonProgram.cpp +++ b/GeneratorSource/Source/Panels/JsonProgram.cpp @@ -21,7 +21,7 @@ Program_JsonFile::Program_JsonFile(const QString& filepath) Program_JsonFile::Program_JsonFile(const QJsonObject& obj) : Program(obj) { - for (const auto& item : json_get_array(obj, JSON_PARAMETERS)){ + for (const auto item : json_get_array(obj, JSON_PARAMETERS)){ if (!item.isObject()){ throw StringException("Config Error - Expected and object."); } diff --git a/GeneratorSource/Source/Panels/JsonSettings.cpp b/GeneratorSource/Source/Panels/JsonSettings.cpp index 24f14e06fe..e74866da78 100644 --- a/GeneratorSource/Source/Panels/JsonSettings.cpp +++ b/GeneratorSource/Source/Panels/JsonSettings.cpp @@ -21,7 +21,7 @@ Settings_JsonFile::Settings_JsonFile(const QString& filepath) Settings_JsonFile::Settings_JsonFile(const QJsonObject& obj) : ConfigSet(obj) { - for (const auto& item : json_get_array(obj, JSON_OPTIONS)){ + for (const auto item : json_get_array(obj, JSON_OPTIONS)){ if (!item.isObject()){ throw StringException("Config Error - Expected and object."); } diff --git a/SerialPrograms/SerialPrograms.pro b/SerialPrograms/SerialPrograms.pro index d5e9ba4f1d..030921c44f 100644 --- a/SerialPrograms/SerialPrograms.pro +++ b/SerialPrograms/SerialPrograms.pro @@ -211,6 +211,7 @@ HEADERS += \ Source/CommonFramework/Inference/FloatPixel.h \ Source/CommonFramework/Inference/FloatStatAccumulator.h \ Source/CommonFramework/Inference/ImageTools.h \ + Source/CommonFramework/Inference/InferenceThrottler.h \ Source/CommonFramework/Inference/InferenceTypes.h \ Source/CommonFramework/Inference/TimeWindowStatTracker.h \ Source/CommonFramework/Options/BooleanCheckBox.h \ diff --git a/SerialPrograms/Source/CommonFramework/Globals.cpp b/SerialPrograms/Source/CommonFramework/Globals.cpp index 10259a830b..693383e023 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.cpp +++ b/SerialPrograms/Source/CommonFramework/Globals.cpp @@ -9,7 +9,7 @@ namespace PokemonAutomation{ -const QString VERSION = "v0.4.6"; +const QString VERSION = "v0.4.7"; const QString DISCORD = "https://discord.gg/cQ4gWxN"; const QString GITHUB_REPO = "https://github.com/PokemonAutomation/SwSh-Arduino"; diff --git a/SerialPrograms/Source/CommonFramework/Inference/InferenceThrottler.h b/SerialPrograms/Source/CommonFramework/Inference/InferenceThrottler.h new file mode 100644 index 0000000000..494184698e --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/InferenceThrottler.h @@ -0,0 +1,61 @@ +/* Inference Throttler + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_CommonFramework_InferenceThrottler_H +#define PokemonAutomation_CommonFramework_InferenceThrottler_H + +#include +#include "CommonFramework/Tools/ProgramEnvironment.h" + +namespace PokemonAutomation{ + +class InferenceThrottler{ +public: + InferenceThrottler( + std::chrono::milliseconds timeout, + std::chrono::milliseconds period = std::chrono::milliseconds(50) + ) + : m_timeout(timeout) + , m_period(period) + , m_start(std::chrono::system_clock::now()) + , m_wait_until(m_start + period) + {} + + // Call at the end of each loop iteration. This will wait until the + // time since the previous iteration has reached the specified period. + // + // Returns true if this loop has timed out. + bool end_iteration(ProgramEnvironment& env){ + auto now = std::chrono::system_clock::now(); + if (now - m_start >= m_timeout){ + return true; + } + + auto wait = m_wait_until - now; + if (wait <= std::chrono::milliseconds(0)){ + m_wait_until = now + m_period; + }else{ + m_wait_until += m_period; + env.wait(wait); + } + return false; + } + + void set_period(std::chrono::milliseconds period){ + m_period = period; + } + +private: + std::chrono::milliseconds m_timeout; + std::chrono::milliseconds m_period; + std::chrono::system_clock::time_point m_start; + std::chrono::system_clock::time_point m_wait_until; +}; + + + +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp index 174e168b28..1770e96c68 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp @@ -9,6 +9,7 @@ #include "Common/SwitchFramework/Switch_PushButtons.h" #include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/InferenceThrottler.h" #include "PokemonSwSh_BeamSetter.h" //#include @@ -52,7 +53,6 @@ BeamSetter::Detection BeamSetter::run( // Drop the wishing piece. pbf_press_button(botbase, BUTTON_A, 10, 10); botbase.wait_for_all_requests(); - uint32_t start = system_clock(botbase); // Set up detection history. std::map red_detections; @@ -61,8 +61,9 @@ BeamSetter::Detection BeamSetter::run( bool low_stddev_flag = false; std::vector current_values(m_boxes.size()); std::vector current_ratio_diffs(m_boxes.size()); - uint32_t now = start; - while (now - start < timeout_ticks){ + + InferenceThrottler throttler(std::chrono::milliseconds((uint64_t)timeout_ticks * 1000 / TICKS_PER_SECOND)); + do{ // Take screenshot. QImage current = m_feed.snapshot(); if (current.isNull()){ @@ -157,9 +158,8 @@ BeamSetter::Detection BeamSetter::run( return Detection::RED_ASSUMED; } - env.wait(std::chrono::milliseconds(50)); - now = system_clock(botbase); - } + }while (!throttler.end_iteration(env)); + return Detection::NO_DETECTION; } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp index 7d00ae3e04..ec0cc87c10 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp @@ -5,6 +5,7 @@ */ #include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/InferenceThrottler.h" #include "CommonFramework/Inference/FillMatrix.h" #include "PokemonSwSh_FishingDetector.h" @@ -155,9 +156,7 @@ FishingDetector::Detection FishingDetector::wait_for_detection( ProgramEnvironment& env, Logger& logger, std::chrono::seconds timeout ){ - auto start = std::chrono::system_clock::now(); - auto last = start; - + InferenceThrottler throttler(timeout); while (true){ env.check_stopping(); @@ -176,16 +175,10 @@ FishingDetector::Detection FishingDetector::wait_for_detection( return detection; } - auto now = std::chrono::system_clock::now(); - if (now - start > timeout){ - logger.log("FishEncounterDetector: Timed out.", "red"); + if (throttler.end_iteration(env)){ + logger.log("FishEncounterDetector: Timed out.", "red"); return Detection::NO_DETECTION; } - auto time_since_last_frame = now - last; - if (time_since_last_frame > std::chrono::milliseconds(50)){ - env.wait(std::chrono::milliseconds(50) - time_since_last_frame); - } - last = now; } } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.cpp index 28fa5d38a7..7e39ec90fd 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.cpp @@ -8,6 +8,7 @@ */ #include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/InferenceThrottler.h" #include "CommonFramework/Inference/ColorClustering.h" #include "PokemonSwSh_SummaryShinySymbolDetector.h" @@ -72,11 +73,10 @@ SummaryShinySymbolDetector::Detection SummaryShinySymbolDetector::wait_for_detec ProgramEnvironment& env, std::chrono::seconds timeout ){ - auto start = std::chrono::system_clock::now(); - auto last = start; - Detection last_detection = Detection::NO_DETECTION; size_t confirmations = 0; + + InferenceThrottler throttler(timeout); while (true){ env.check_stopping(); @@ -91,16 +91,10 @@ SummaryShinySymbolDetector::Detection SummaryShinySymbolDetector::wait_for_detec break; } - auto now = std::chrono::system_clock::now(); - if (now - start > timeout){ + if (throttler.end_iteration(env)){ last_detection = Detection::NO_DETECTION; break; } - auto time_since_last_frame = now - last; - if (time_since_last_frame > std::chrono::milliseconds(50)){ - env.wait(std::chrono::milliseconds(50) - time_since_last_frame); - } - last = now; } switch (last_detection){ diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp index 5e513f4230..9061a9b29c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp @@ -5,12 +5,18 @@ */ #include +#include "CommonFramework/Inference/ImageTools.h" #include "CommonFramework/Inference/FloatStatAccumulator.h" #include "CommonFramework/Inference/TimeWindowStatTracker.h" -#include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/InferenceThrottler.h" #include "PokemonSwSh_ShinyTrigger.h" #include "PokemonSwSh_ShinyEncounterDetector.h" +#include "ClientSource/Libraries/Logging.h" +#include +using std::cout; +using std::endl; + namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonSwSh{ @@ -33,6 +39,8 @@ ShinyEncounterDetector::ShinyEncounterDetector( , m_menu(feed) {} + + ShinyEncounterDetector::Detection ShinyEncounterDetector::detect(ProgramEnvironment& env){ TimeWindowStatTracker tracker(std::chrono::milliseconds(4000)); @@ -42,12 +50,12 @@ ShinyEncounterDetector::Detection ShinyEncounterDetector::detect(ProgramEnvironm double star_alpha = 0; double square_alpha = 0; - auto start = std::chrono::system_clock::now(); - auto last = start; + InferenceThrottler throttler(m_timeout, std::chrono::milliseconds(50)); for (uint64_t count = 0;; count++){ env.check_stopping(); QImage image = m_feed.snapshot(); + auto timestamp = std::chrono::system_clock::now(); if (m_menu.detect(image)){ m_logger.log("ShinyDetector: Battle menu found!", "purple"); break; @@ -65,7 +73,7 @@ ShinyEncounterDetector::Detection ShinyEncounterDetector::detect(ProgramEnvironm square_alpha += 5 * signatures.lines.size(); double signature_alpha = signatures.alpha(); - tracker.push(signature_alpha, last); + tracker.push(signature_alpha, timestamp); FloatStatAccumulator previous_2_seconds = tracker.accumulate_start_to_point(std::chrono::milliseconds(2000)); FloatStatAccumulator last_2_seconds = tracker.accumulate_last(std::chrono::milliseconds(2000)); double mean0 = previous_2_seconds.mean(); @@ -112,16 +120,10 @@ ShinyEncounterDetector::Detection ShinyEncounterDetector::detect(ProgramEnvironm image.save("DetectionLine.png"); } - auto now = std::chrono::system_clock::now(); - if (now - start > m_timeout){ + if (throttler.end_iteration(env)){ m_logger.log("ShinyDetector: Battle menu not found after timeout.", "red"); return Detection::NO_BATTLE_MENU; } - auto time_since_last_frame = now - last; - if (time_since_last_frame > std::chrono::milliseconds(50)){ - env.wait(std::chrono::milliseconds(50) - time_since_last_frame); - } - last = now; } detection_overlays.clear(); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h index deb7098d31..94e0130d0d 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h @@ -34,7 +34,7 @@ class ShinyEncounterDetector{ VideoFeed& feed, Logger& logger, const InferenceBox& box, std::chrono::seconds timeout, - double detection_threshold = 4.0 + double detection_threshold = 3.0 ); Detection detect(ProgramEnvironment& env); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp index 91961aae45..fb783f4e60 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp @@ -113,10 +113,10 @@ void ShinyImageDetection::accumulate( double ShinyImageDetection::alpha() const{ double x = 1.0; - x *= std::pow(5.0, lines.size()); - x *= std::pow(2.0, stars.size()); - x *= std::pow(2.0, balls.size()); + x *= std::pow(3.0, balls.size()); + x *= std::pow(3.0, stars.size()); x *= std::pow(1.2, squares.size()); + x *= std::pow(5.0, lines.size()); return x; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp index 0e2bd7f7c1..e9842d61a7 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp @@ -91,10 +91,10 @@ void PurpleBeamFinder::program(SingleSwitchProgramEnvironment& env) const{ } { std::string status = "Attempts: " + tostr_u_commas(attempts); - status += ", Timeouts: " + tostr_u_commas(timed_out); - status += ", Red Detected: " + tostr_u_commas(red_detected); - status += ", Red Presumed: " + tostr_u_commas(red_assumed); - status += ", Purple: " + tostr_u_commas(purple); + status += " - Timeouts: " + tostr_u_commas(timed_out); + status += " - Red Detected: " + tostr_u_commas(red_detected); + status += " - Red Presumed: " + tostr_u_commas(red_assumed); + status += " - Purple: " + tostr_u_commas(purple); status += "
"; QString str = status.c_str(); env.logger.log(str); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp index 0c707c5c43..36d8c51443 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp @@ -9,6 +9,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/InferenceThrottler.h" #include "PokemonSwSh_StartGame.h" namespace PokemonAutomation{ @@ -30,16 +31,16 @@ void enter_loading_game( InferenceBoxScope box0(console, 0.2, 0.2, 0.6, 0.1); InferenceBoxScope box1(console, 0.2, 0.7, 0.6, 0.1); - auto start = std::chrono::system_clock::now(); - auto last = start; bool black_found = false; + + InferenceThrottler throttler(timeout); while (true){ env.check_stopping(); QImage screen = console.video().snapshot(); if (screen.isNull()){ - logger.log("start_game_with_inference(): Screenshot failed.", "purple"); - timeout = std::chrono::milliseconds(1000); + logger.log("enter_loading_game(): Screenshot failed.", "purple"); + throttler.set_period(std::chrono::milliseconds(1000)); }else{ bool black0 = is_black(extract_box(screen, box0)); bool black1 = is_black(extract_box(screen, box1)); @@ -53,20 +54,14 @@ void enter_loading_game( } } - auto now = std::chrono::system_clock::now(); - if (now - start > timeout){ - logger.log("start_game_with_inference(): Game load timed out. Proceeding with default start delay.", "red"); + if (throttler.end_iteration(env)){ + logger.log("enter_loading_game(): Game load timed out. Proceeding with default start delay.", "red"); break; } - auto time_since_last_frame = now - last; - if (time_since_last_frame > std::chrono::milliseconds(50)){ - env.wait(std::chrono::milliseconds(50) - time_since_last_frame); - } - last = now; } } - logger.log("start_game_with_inference(): Game Loaded. Entering game...", "purple"); + logger.log("enter_loading_game(): Game Loaded. Entering game...", "purple"); enter_game(backup_save, ENTER_GAME_MASH, 0); console.botbase().wait_for_all_requests(); @@ -76,21 +71,21 @@ void enter_loading_game( InferenceBoxScope box(console, 0.2, 0.2, 0.6, 0.6); - auto start = std::chrono::system_clock::now(); - auto last = start; bool black_found = false; + + InferenceThrottler throttler(timeout); while (true){ env.check_stopping(); QImage screen = console.video().snapshot(); if (screen.isNull()){ - logger.log("start_game_with_inference(): Screenshot failed.", "purple"); - timeout = std::chrono::milliseconds(1000); + logger.log("enter_loading_game(): Screenshot failed.", "purple"); + throttler.set_period(std::chrono::milliseconds(1000)); }else{ bool black = is_black(extract_box(screen, box)); if (black){ if (!black_found){ - logger.log("start_game_with_inference(): Game entry started.", "purple"); + logger.log("enter_loading_game(): Game entry started.", "purple"); } black_found = true; }else if (black_found){ @@ -98,16 +93,10 @@ void enter_loading_game( } } - auto now = std::chrono::system_clock::now(); - if (now - start > timeout){ - logger.log("start_game_with_inference(): Game entry timed out. Proceeding with default start delay.", "red"); + if (throttler.end_iteration(env)){ + logger.log("enter_loading_game(): Game entry timed out. Proceeding with default start delay.", "red"); break; } - auto time_since_last_frame = now - last; - if (time_since_last_frame > std::chrono::milliseconds(50)){ - env.wait(std::chrono::milliseconds(50) - time_since_last_frame); - } - last = now; } } logger.log("start_game_with_inference(): Game started.", "purple"); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp index 6455db0ce9..0bfd8fb886 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp @@ -54,6 +54,7 @@ bool StandardEncounterTracker::process_result(ShinyEncounterDetector::Detection pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); return true; } + return false; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp index 6ad1e6e02e..6196b6fa99 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp @@ -24,8 +24,8 @@ ShinyHuntAutonomousFishing::ShinyHuntAutonomousFishing() : SingleSwitchProgram( FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, "Shiny Hunt Autonomous - Fishing", - "SerialPrograms/ShinyHuntAutonomous-BerryTree.md", - "Automatically hunt for shiny berry tree " + STRING_POKEMON + " using video feedback." + "SerialPrograms/ShinyHuntAutonomous-Fishing.md", + "Automatically hunt for shiny fishing " + STRING_POKEMON + " using video feedback." ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." From 21e4494c6a1bf2778bc3416a72ad03269e0c610d Mon Sep 17 00:00:00 2001 From: pifopi Date: Wed, 24 Mar 2021 22:34:21 +0100 Subject: [PATCH 03/13] add CICD pipeline --- .github/workflows/cpp-ci-generator-source.yml | 51 +++++++++++++++++++ .github/workflows/cpp-ci-serial-programs.yml | 46 +++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 .github/workflows/cpp-ci-generator-source.yml create mode 100644 .github/workflows/cpp-ci-serial-programs.yml diff --git a/.github/workflows/cpp-ci-generator-source.yml b/.github/workflows/cpp-ci-generator-source.yml new file mode 100644 index 0000000000..944844cd58 --- /dev/null +++ b/.github/workflows/cpp-ci-generator-source.yml @@ -0,0 +1,51 @@ +name: C++ CI Hex Generator + +on: [push, pull_request, workflow_dispatch] + +jobs: + build: + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [macOS-latest, windows-latest] + + steps: + - uses: actions/checkout@v2 + - name: Set up MinGW + if: contains( matrix.os, 'windows') + uses: egor-tensin/setup-mingw@v2 + - name: Install Qt + if: contains( matrix.os, 'windows') + uses: jurplel/install-qt-action@v2 + with: + version: '5.12.10' + arch: 'win32_mingw73' + - name: Install Qt + if: contains( matrix.os, 'macOS') + uses: jurplel/install-qt-action@v2 + with: + version: '5.12.10' + - name: C++ generation + run: | + cd GeneratorSource + qmake + make + - name: Generate windows package + if: contains( matrix.os, 'windows') + run: | + mkdir package + cd package + mkdir Binaries + cd .. + move GeneratorSource/release/HexGenerator.exe package/Binaries/ + move Common package/Common/ + move GeneratorConfig package/GeneratorConfig/ + move NativePrograms package/NativePrograms/ + windeployqt ./package/Binaries/HexGenerator.exe + - uses: actions/upload-artifact@v2 + if: contains( matrix.os, 'windows') + with: + name: Hex Generator for windows + path: package/ diff --git a/.github/workflows/cpp-ci-serial-programs.yml b/.github/workflows/cpp-ci-serial-programs.yml new file mode 100644 index 0000000000..06f092b41f --- /dev/null +++ b/.github/workflows/cpp-ci-serial-programs.yml @@ -0,0 +1,46 @@ +name: C++ CI Serial Programs + +on: [push, pull_request, workflow_dispatch] + +jobs: + build: + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [windows-latest] + + steps: + - uses: actions/checkout@v2 + - name: Set up MinGW + if: contains( matrix.os, 'windows') + uses: egor-tensin/setup-mingw@v2 + - name: Install Qt + if: contains( matrix.os, 'windows') + uses: jurplel/install-qt-action@v2 + with: + version: '5.12.10' + arch: 'win64_mingw73' + - name: Install Qt + if: contains( matrix.os, 'macOS') + uses: jurplel/install-qt-action@v2 + with: + version: '5.12.10' + - name: C++ generation + run: | + cd SerialPrograms + qmake + make + - name: Generate windows package + if: contains( matrix.os, 'windows') + run: | + mkdir package + move SerialPrograms/release/SerialPrograms.exe package/ + move SerialPrograms/libtesseractc.dll package/ + windeployqt ./package/SerialPrograms.exe + - uses: actions/upload-artifact@v2 + if: contains( matrix.os, 'windows') + with: + name: Serial Programs for windows + path: package/ From 79b593317f791b3fd262c84153716e6544ca0d69 Mon Sep 17 00:00:00 2001 From: pifopi Date: Fri, 2 Apr 2021 20:05:03 +0200 Subject: [PATCH 04/13] fix some warnings --- .../Source/NintendoSwitch/Options/TimeExpression.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SerialPrograms/Source/NintendoSwitch/Options/TimeExpression.h b/SerialPrograms/Source/NintendoSwitch/Options/TimeExpression.h index c2181afee6..7cadef077d 100644 --- a/SerialPrograms/Source/NintendoSwitch/Options/TimeExpression.h +++ b/SerialPrograms/Source/NintendoSwitch/Options/TimeExpression.h @@ -24,8 +24,8 @@ class TimeExpression : public ConfigOption, public TimeExpressionOption{ Type min_value = std::numeric_limits::min(), Type max_value = std::numeric_limits::max() ) - : ConfigOption(std::move(label)) - , TimeExpressionOption(backing, m_label, min_value, max_value, default_value) + : ConfigOption(label) + , TimeExpressionOption(backing, std::move(label), min_value, max_value, default_value) {} TimeExpression( QString label, @@ -33,8 +33,8 @@ class TimeExpression : public ConfigOption, public TimeExpressionOption{ Type min_value = std::numeric_limits::min(), Type max_value = std::numeric_limits::max() ) - : ConfigOption(std::move(label)) - , TimeExpressionOption(m_label, min_value, max_value, default_value) + : ConfigOption(label) + , TimeExpressionOption(std::move(label), min_value, max_value, default_value) {} virtual void load_json(const QJsonValue& json) override{ From 05b61298dab7aa9aa56c351f379ad5c969ed1929 Mon Sep 17 00:00:00 2001 From: pifopi Date: Sat, 3 Apr 2021 01:46:26 +0200 Subject: [PATCH 05/13] add include guard --- Common/Qt/StringException.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Common/Qt/StringException.h b/Common/Qt/StringException.h index d9f92efb0b..aff6891c11 100644 --- a/Common/Qt/StringException.h +++ b/Common/Qt/StringException.h @@ -4,6 +4,9 @@ * */ +#ifndef PokemonAutomation_StringException_H +#define PokemonAutomation_StringException_H + #include #include @@ -41,3 +44,5 @@ class StringException{ } + +#endif \ No newline at end of file From df7e8a0649a4a6736d07a5b80131b6e3394ef1d9 Mon Sep 17 00:00:00 2001 From: Alexander Yee Date: Sat, 3 Apr 2021 15:18:56 -0500 Subject: [PATCH 06/13] Update SimpleIntegerOption.cpp --- Common/Qt/Options/SimpleIntegerOption.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Common/Qt/Options/SimpleIntegerOption.cpp b/Common/Qt/Options/SimpleIntegerOption.cpp index 8d951a4ee0..c578827a61 100644 --- a/Common/Qt/Options/SimpleIntegerOption.cpp +++ b/Common/Qt/Options/SimpleIntegerOption.cpp @@ -14,11 +14,17 @@ namespace PokemonAutomation{ template class SimpleIntegerOption; template class SimpleIntegerOption; -template class SimpleIntegerOption; - template class SimpleIntegerOptionUI; template class SimpleIntegerOptionUI; + +// COMPILER-BUG-CLANG: unsigned in vs. uint32_t linking +#ifdef __clang__ +template class SimpleIntegerOption; +template class SimpleIntegerOptionUI; +#else +template class SimpleIntegerOption; template class SimpleIntegerOptionUI; +#endif template From 40ed7c45bfd8b2de40c94ffd626bf7d85153ce42 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Sat, 3 Apr 2021 15:40:33 -0500 Subject: [PATCH 07/13] Attempt to fix Clang. --- Common/Qt/Options/SimpleIntegerOption.cpp | 10 ++-------- Common/Qt/Options/SimpleIntegerOption.h | 7 +++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Common/Qt/Options/SimpleIntegerOption.cpp b/Common/Qt/Options/SimpleIntegerOption.cpp index c578827a61..8d951a4ee0 100644 --- a/Common/Qt/Options/SimpleIntegerOption.cpp +++ b/Common/Qt/Options/SimpleIntegerOption.cpp @@ -14,17 +14,11 @@ namespace PokemonAutomation{ template class SimpleIntegerOption; template class SimpleIntegerOption; +template class SimpleIntegerOption; + template class SimpleIntegerOptionUI; template class SimpleIntegerOptionUI; - -// COMPILER-BUG-CLANG: unsigned in vs. uint32_t linking -#ifdef __clang__ -template class SimpleIntegerOption; -template class SimpleIntegerOptionUI; -#else -template class SimpleIntegerOption; template class SimpleIntegerOptionUI; -#endif template diff --git a/Common/Qt/Options/SimpleIntegerOption.h b/Common/Qt/Options/SimpleIntegerOption.h index f950aaf69f..ee27733cdb 100644 --- a/Common/Qt/Options/SimpleIntegerOption.h +++ b/Common/Qt/Options/SimpleIntegerOption.h @@ -65,5 +65,12 @@ class SimpleIntegerOptionUI : public QWidget{ +// COMPILER-BUG-CLANG: unsigned in vs. uint32_t linking +#ifdef __clang__ +template class SimpleIntegerOption; +template class SimpleIntegerOptionUI; +#endif + + } #endif From 9d1243dfcab4592e31502ecfb6c61ccaba38f1d1 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Sat, 3 Apr 2021 15:43:36 -0500 Subject: [PATCH 08/13] Fix Clang take 2. --- Common/Qt/Options/SimpleIntegerOption.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Common/Qt/Options/SimpleIntegerOption.cpp b/Common/Qt/Options/SimpleIntegerOption.cpp index 8d951a4ee0..20bb9882f4 100644 --- a/Common/Qt/Options/SimpleIntegerOption.cpp +++ b/Common/Qt/Options/SimpleIntegerOption.cpp @@ -14,11 +14,15 @@ namespace PokemonAutomation{ template class SimpleIntegerOption; template class SimpleIntegerOption; -template class SimpleIntegerOption; template class SimpleIntegerOptionUI; template class SimpleIntegerOptionUI; + +// COMPILER-BUG-CLANG: unsigned in vs. uint32_t linking +#ifndef __clang__ +template class SimpleIntegerOption; template class SimpleIntegerOptionUI; +#endif template From 7f2ab3e7924647314ec244ad066145511fe17595 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Sat, 3 Apr 2021 16:02:51 -0500 Subject: [PATCH 09/13] Fix Clang take 3. --- Common/Qt/Options/SimpleIntegerOption.cpp | 24 +++++++++++------------ Common/Qt/Options/SimpleIntegerOption.h | 6 ------ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Common/Qt/Options/SimpleIntegerOption.cpp b/Common/Qt/Options/SimpleIntegerOption.cpp index 20bb9882f4..77b88f5894 100644 --- a/Common/Qt/Options/SimpleIntegerOption.cpp +++ b/Common/Qt/Options/SimpleIntegerOption.cpp @@ -12,18 +12,6 @@ namespace PokemonAutomation{ -template class SimpleIntegerOption; -template class SimpleIntegerOption; - -template class SimpleIntegerOptionUI; -template class SimpleIntegerOptionUI; - -// COMPILER-BUG-CLANG: unsigned in vs. uint32_t linking -#ifndef __clang__ -template class SimpleIntegerOption; -template class SimpleIntegerOptionUI; -#endif - template SimpleIntegerOption::SimpleIntegerOption( @@ -127,4 +115,16 @@ void SimpleIntegerOptionUI::restore_defaults(){ } + + + + +template class SimpleIntegerOption; +template class SimpleIntegerOption; +template class SimpleIntegerOption; + +template class SimpleIntegerOptionUI; +template class SimpleIntegerOptionUI; +template class SimpleIntegerOptionUI; + } diff --git a/Common/Qt/Options/SimpleIntegerOption.h b/Common/Qt/Options/SimpleIntegerOption.h index ee27733cdb..632a96a1ed 100644 --- a/Common/Qt/Options/SimpleIntegerOption.h +++ b/Common/Qt/Options/SimpleIntegerOption.h @@ -65,12 +65,6 @@ class SimpleIntegerOptionUI : public QWidget{ -// COMPILER-BUG-CLANG: unsigned in vs. uint32_t linking -#ifdef __clang__ -template class SimpleIntegerOption; -template class SimpleIntegerOptionUI; -#endif - } #endif From 4ec4e734c85df1dd6cca13ba641be59aa52f5886 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Sat, 3 Apr 2021 16:12:37 -0500 Subject: [PATCH 10/13] Fix Clang take 4. --- Common/Qt/Options/TimeExpressionOption.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Common/Qt/Options/TimeExpressionOption.cpp b/Common/Qt/Options/TimeExpressionOption.cpp index 191cf09f74..71bcd1364b 100644 --- a/Common/Qt/Options/TimeExpressionOption.cpp +++ b/Common/Qt/Options/TimeExpressionOption.cpp @@ -14,11 +14,6 @@ namespace PokemonAutomation{ -template class TimeExpressionOption; -template class TimeExpressionOption; - -template class TimeExpressionOptionUI; -template class TimeExpressionOptionUI; template @@ -167,4 +162,14 @@ void TimeExpressionOptionUI::restore_defaults(){ } + + + + +template class TimeExpressionOption; +template class TimeExpressionOption; + +template class TimeExpressionOptionUI; +template class TimeExpressionOptionUI; + } From 65ffa246ed7a6fc16d8e9d7212b861568d502640 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Sat, 3 Apr 2021 19:08:22 -0500 Subject: [PATCH 11/13] Update to latest. --- ClientSource/Connection/PABotBase.cpp | 5 +- ClientSource/Libraries/Logging.h | 13 +- Common/MessageProtocol.h | 2 + Common/PokemonSwSh/PokemonSwShDateSpam.cpp | 3 +- Common/Qt/Options/BooleanCheckBoxOption.cpp | 6 + Common/Qt/Options/FloatingPointOption.cpp | 6 + Common/Qt/Options/FossilTableOption.cpp | 34 +- Common/Qt/Options/MultiHostTableOption.cpp | 47 +- Common/Qt/Options/SimpleIntegerOption.cpp | 11 +- Common/Qt/Options/SimpleIntegerOption.h | 1 - Common/Qt/Options/SwitchDateOption.cpp | 4 +- Common/Qt/Options/TimeExpressionOption.cpp | 10 +- Common/Qt/QtJsonTools.cpp | 84 +-- Common/Qt/QtJsonTools.h | 88 ++- Common/Qt/StringException.h | 2 +- GeneratorConfig/AutoHost-Rolling.json | 9 + .../Source/Options/BooleanCheckBox.cpp | 4 +- GeneratorSource/Source/Options/ConfigItem.cpp | 4 +- .../Source/Options/EnumDropdown.cpp | 13 +- GeneratorSource/Source/Options/FixedCode.cpp | 6 +- .../Source/Options/FloatingPoint.cpp | 8 +- .../Source/Options/FossilTable.cpp | 4 +- .../Source/Options/MultiHostTable.cpp | 4 +- GeneratorSource/Source/Options/RandomCode.cpp | 14 +- .../Source/Options/SimpleInteger.cpp | 8 +- .../Source/Options/SingleStatementOption.cpp | 2 +- GeneratorSource/Source/Options/SwitchDate.cpp | 4 +- .../Source/Options/TimeExpression.cpp | 8 +- GeneratorSource/Source/Panels/ConfigSet.cpp | 6 +- GeneratorSource/Source/Panels/JsonProgram.cpp | 2 +- .../Source/Panels/JsonSettings.cpp | 2 +- GeneratorSource/Source/Panels/Program.cpp | 4 +- .../Source/Tools/PersistentSettings.cpp | 7 +- NativePrograms/AutoHost-Rolling.c | 1 + .../PokemonSwShPrograms/AutoHost-Rolling.h | 1 + SerialPrograms/SerialPrograms.pro | 12 +- .../Source/CommonFramework/Globals.cpp | 2 +- .../Inference/FillGeometry.cpp | 50 +- .../CommonFramework/Inference/FillGeometry.h | 4 +- .../CommonFramework/Inference/FillMatrix.cpp | 22 + .../CommonFramework/Inference/FillMatrix.h | 2 + .../Inference/FloatStatAccumulator.h | 49 -- .../CommonFramework/Inference/ImageTools.cpp | 3 + .../CommonFramework/Inference/ImageTools.h | 1 + .../Inference/StatAccumulator.h | 99 +++ .../CommonFramework/Options/FixedCode.cpp | 3 + .../CommonFramework/Options/RandomCode.cpp | 6 +- .../CommonFramework/Panels/SettingsPanel.cpp | 15 +- .../CommonFramework/PersistentSettings.cpp | 20 +- .../CommonFramework/PersistentSettings.h | 6 +- .../CommonFramework/Tools/BotBaseHandle.cpp | 3 + .../Widgets/CameraSelector.cpp | 28 +- .../Widgets/SerialSelector.cpp | 10 +- .../CommonFramework/Windows/MainWindow.cpp | 2 +- .../CommonFramework/Windows/OutputWindow.cpp | 10 +- .../Framework/MultiSwitchSystem.cpp | 40 +- .../Framework/RunnableSwitchProgram.cpp | 27 +- .../Framework/RunnableSwitchProgram.h | 1 + .../NintendoSwitch/Framework/SwitchSystem.cpp | 10 +- .../VirtualSwitchControllerMapping.cpp | 18 +- .../NintendoSwitch/FrameworkSettingsPanel.cpp | 4 + .../NintendoSwitch/Options/FriendCodeList.cpp | 7 +- SerialPrograms/Source/PanelList.cpp | 54 +- .../Inference/PokemonSwSh_FishingDetector.cpp | 119 +--- .../Inference/PokemonSwSh_MarkFinder.cpp | 336 +++++++++ .../Inference/PokemonSwSh_MarkFinder.h | 40 ++ .../PokemonSwSh_StartBattleDetector.cpp | 71 +- .../PokemonSwSh_StartBattleDetector.h | 12 +- .../PokemonSwSh_ShinyEncounterDetector.cpp | 343 +++++++--- .../PokemonSwSh_ShinyEncounterDetector.h | 52 +- .../ShinyDetection/PokemonSwSh_ShinyFilters.h | 15 + .../PokemonSwSh_ShinyTrigger.cpp | 64 +- .../ShinyDetection/PokemonSwSh_ShinyTrigger.h | 10 +- .../PokemonSwSh_SparkleTrigger.cpp | 5 +- .../PokemonSwSh_SquareDetector.cpp | 641 ++++++++++++++++++ .../PokemonSwSh_SquareDetector.h | 31 + .../PokemonSwSh_SquareTrigger.cpp | 137 +--- .../PokemonSwSh_SquareTrigger.h | 6 +- .../PokemonSwSh_PurpleBeamFinder.cpp | 5 +- .../Hosting/PokemonSwSh_DenRoller.cpp | 1 + .../PokemonSwSh_EncounterStats.cpp | 15 +- .../ShinyHunting/PokemonSwSh_EncounterStats.h | 4 +- .../PokemonSwSh_EncounterTracker.cpp | 15 +- .../PokemonSwSh_EncounterTracker.h | 2 +- ...emonSwSh_ShinyHuntAutonomous-BerryTree.cpp | 31 +- ...okemonSwSh_ShinyHuntAutonomous-BerryTree.h | 3 +- ...okemonSwSh_ShinyHuntAutonomous-Fishing.cpp | 27 +- .../PokemonSwSh_ShinyHuntAutonomous-Fishing.h | 4 +- ...kemonSwSh_ShinyHuntAutonomous-IoATrade.cpp | 17 +- ...PokemonSwSh_ShinyHuntAutonomous-IoATrade.h | 4 +- ...emonSwSh_ShinyHuntAutonomous-Overworld.cpp | 412 +++++++++++ ...okemonSwSh_ShinyHuntAutonomous-Overworld.h | 61 ++ .../PokemonSwSh_ShinyHuntAutonomous-Regi.cpp | 29 +- .../PokemonSwSh_ShinyHuntAutonomous-Regi.h | 3 +- ...monSwSh_ShinyHuntAutonomous-Regigigas2.cpp | 30 +- ...kemonSwSh_ShinyHuntAutonomous-Regigigas2.h | 3 +- ...onSwSh_ShinyHuntAutonomous-StrongSpawn.cpp | 27 +- ...emonSwSh_ShinyHuntAutonomous-StrongSpawn.h | 3 +- ...Sh_ShinyHuntAutonomous-SwordsOfJustice.cpp | 27 +- ...SwSh_ShinyHuntAutonomous-SwordsOfJustice.h | 3 +- ...emonSwSh_ShinyHuntAutonomous-Whistling.cpp | 144 ++++ ...okemonSwSh_ShinyHuntAutonomous-Whistling.h | 44 ++ ...kemonSwSh_ShinyHuntUnattended-IoATrade.cpp | 4 +- .../PokemonSwSh/Programs/TestProgram.cpp | 119 +++- 104 files changed, 2960 insertions(+), 894 deletions(-) delete mode 100644 SerialPrograms/Source/CommonFramework/Inference/FloatStatAccumulator.h create mode 100644 SerialPrograms/Source/CommonFramework/Inference/StatAccumulator.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h diff --git a/ClientSource/Connection/PABotBase.cpp b/ClientSource/Connection/PABotBase.cpp index a5492a274c..36d79c0312 100644 --- a/ClientSource/Connection/PABotBase.cpp +++ b/ClientSource/Connection/PABotBase.cpp @@ -586,7 +586,7 @@ void PABotBase::issue_request_and_wait( issue_request(iter, send_type, send_params, send_bytes, false); // Wait for ack. - while (iter->second.state != AckState::ACKED){ + while (true){ std::unique_lock lg(m_sleep_lock); { SpinLockGuard slg(m_state_lock, "PABotBase::issue_request_and_wait() - 0"); @@ -595,6 +595,9 @@ void PABotBase::issue_request_and_wait( remove_request(iter); throw CancelledException(); } + if (iter->second.state == AckState::ACKED){ + break; + } } m_cv.wait(lg); } diff --git a/ClientSource/Libraries/Logging.h b/ClientSource/Libraries/Logging.h index 3d8ca15b6b..52b13cf347 100644 --- a/ClientSource/Libraries/Logging.h +++ b/ClientSource/Libraries/Logging.h @@ -24,13 +24,22 @@ std::string current_time(); class MessageLogger : public MessageSniffer{ public: MessageLogger(bool log_everything = false) - : m_log_everything(log_everything) + : m_low_everything_owner(false) + , m_log_everything(m_low_everything_owner) {} + MessageLogger(std::atomic& log_everything) + : m_low_everything_owner(false) + , m_log_everything(log_everything) + {} + - std::atomic m_log_everything; virtual void log(std::string msg) override; virtual void on_send(const BotBaseMessage& message, bool is_retransmit) override; virtual void on_recv(const BotBaseMessage& message) override; + +private: + std::atomic m_low_everything_owner; + std::atomic& m_log_everything; }; diff --git a/Common/MessageProtocol.h b/Common/MessageProtocol.h index b4b3acb054..fc2eb2b24d 100644 --- a/Common/MessageProtocol.h +++ b/Common/MessageProtocol.h @@ -252,6 +252,7 @@ typedef struct{ //////////////////////////////////////////////////////////////////////////////// // Requests #define PABB_MSG_SEQNUM_RESET 0x40 +// After you send this message, the next seqnum you should use is (seqnum + 1). typedef struct{ seqnum_t seqnum; } PABB_PACK pabb_MsgInfoSeqnumReset; @@ -277,6 +278,7 @@ typedef struct{ } PABB_PACK pabb_system_clock; #define PABB_MSG_REQUEST_COMMAND_FINISHED 0x45 +// When you receive this message, you must ack it with PABB_MSG_ACK_REQUEST. typedef struct{ seqnum_t seqnum; seqnum_t seq_of_original_command; diff --git a/Common/PokemonSwSh/PokemonSwShDateSpam.cpp b/Common/PokemonSwSh/PokemonSwShDateSpam.cpp index 4859b03d19..60785d0cfc 100644 --- a/Common/PokemonSwSh/PokemonSwShDateSpam.cpp +++ b/Common/PokemonSwSh/PokemonSwShDateSpam.cpp @@ -155,7 +155,8 @@ int register_message_converters_pokemon_date_spam(){ if (body.size() != sizeof(pabb_rollback_hours_from_home)){ ss << "(invalid size)" << std::endl; return ss.str(); } const auto* params = (const pabb_rollback_hours_from_home*)body.c_str(); ss << "seqnum = " << (uint64_t)params->seqnum; - ss << ", rollback_year = " << params->settings_to_home_delay; + ss << ", hours = " << params->hours; + ss << ", settings_to_home_delay = " << params->settings_to_home_delay; return ss.str(); } ); diff --git a/Common/Qt/Options/BooleanCheckBoxOption.cpp b/Common/Qt/Options/BooleanCheckBoxOption.cpp index 604b964311..6d731bf12b 100644 --- a/Common/Qt/Options/BooleanCheckBoxOption.cpp +++ b/Common/Qt/Options/BooleanCheckBoxOption.cpp @@ -31,9 +31,15 @@ BooleanCheckBoxOption::BooleanCheckBoxOption( {} void BooleanCheckBoxOption::load_default(const QJsonValue& json){ + if (!json.isBool()){ + return; + } m_default = json.toBool(); } void BooleanCheckBoxOption::load_current(const QJsonValue& json){ + if (!json.isBool()){ + return; + } m_current = json.toBool(); } QJsonValue BooleanCheckBoxOption::write_default() const{ diff --git a/Common/Qt/Options/FloatingPointOption.cpp b/Common/Qt/Options/FloatingPointOption.cpp index 9f84a9be7d..60f43969bb 100644 --- a/Common/Qt/Options/FloatingPointOption.cpp +++ b/Common/Qt/Options/FloatingPointOption.cpp @@ -26,11 +26,17 @@ FloatingPointOption::FloatingPointOption( {} void FloatingPointOption::load_default(const QJsonValue& json){ + if (!json.isDouble()){ + return; + } m_default = json.toDouble(); m_default = std::max(m_default, m_min_value); m_default = std::min(m_default, m_max_value); } void FloatingPointOption::load_current(const QJsonValue& json){ + if (!json.isDouble()){ + return; + } m_current = json.toDouble(); m_current = std::max(m_current, m_min_value); m_current = std::min(m_current, m_max_value); diff --git a/Common/Qt/Options/FossilTableOption.cpp b/Common/Qt/Options/FossilTableOption.cpp index 05b28db0ce..ad33fa1fb9 100644 --- a/Common/Qt/Options/FossilTableOption.cpp +++ b/Common/Qt/Options/FossilTableOption.cpp @@ -37,31 +37,21 @@ const std::map FOSSIL_MAP{ std::vector parse_fossil_slot(const QJsonValue& json){ std::vector list; - for (const auto item : json_cast_array(json)){ - QJsonObject line = json_cast_object(item); + for (const auto item : json.toArray()){ + QJsonObject line = item.toObject(); FossilTableOption::GameSlot slot; - slot.game_slot = json_get_int(line, FossilTableOption::JSON_GAME_SLOT); - if (slot.game_slot < 1 || slot.game_slot > 2){ - throw StringException("Config Error - Game slot is out of range."); - } - - slot.user_slot = json_get_int(line, FossilTableOption::JSON_USER_SLOT); - if (slot.user_slot < 1 || slot.user_slot > 8){ - throw StringException("Config Error - User slot is out of range."); - } + json_get_int(slot.game_slot, line, FossilTableOption::JSON_GAME_SLOT, 1, 2); + json_get_int(slot.user_slot, line, FossilTableOption::JSON_USER_SLOT, 1, 8); - QString str = json_get_string(line, FossilTableOption::JSON_FOSSIL); + QString str; + json_get_string(str, line, FossilTableOption::JSON_FOSSIL); auto iter = FOSSIL_MAP.find(str); - if (iter == FOSSIL_MAP.end()){ - throw StringException("Config Error - Invalid Fossil Name."); + if (iter != FOSSIL_MAP.end()){ + slot.fossil = (FossilTableOption::Fossil)iter->second; } - slot.fossil = (FossilTableOption::Fossil)iter->second; - slot.revives = json_get_int(line, FossilTableOption::JSON_REVIVES); - if (slot.revives < 0 || slot.revives > 965){ - throw StringException("Config Error - Revives is out of range."); - } + json_get_int(slot.revives, line, FossilTableOption::JSON_REVIVES, 0, 965); list.emplace_back(slot); } @@ -94,9 +84,15 @@ FossilTableOption::FossilTableOption(QString label) {} void FossilTableOption::load_default(const QJsonValue& json){ + if (json.isNull()){ + return; + } m_default = parse_fossil_slot(json); } void FossilTableOption::load_current(const QJsonValue& json){ + if (json.isNull()){ + return; + } m_current = parse_fossil_slot(json); } QJsonValue FossilTableOption::write_default() const{ diff --git a/Common/Qt/Options/MultiHostTableOption.cpp b/Common/Qt/Options/MultiHostTableOption.cpp index a5c91c03db..e94d604d85 100644 --- a/Common/Qt/Options/MultiHostTableOption.cpp +++ b/Common/Qt/Options/MultiHostTableOption.cpp @@ -31,41 +31,22 @@ const QString MultiHostTableOption::JSON_POST_RAID_DELAY = "post_raid_delay"; std::vector parse_game_slot(const QJsonValue& json){ std::vector list; - for (const auto item : json_cast_array(json)){ - QJsonObject line = json_cast_object(item); + for (const auto item : json.toArray()){ + QJsonObject line = item.toObject(); MultiHostTableOption::GameSlot slot; - slot.game_slot = json_get_int(line, MultiHostTableOption::JSON_GAME_SLOT); - if (slot.game_slot < 1 || slot.game_slot > 2){ - throw StringException("Config Error - Game slot is out of range."); - } - - slot.user_slot = json_get_int(line, MultiHostTableOption::JSON_USER_SLOT); - if (slot.user_slot < 1 || slot.user_slot > 8){ - throw StringException("Config Error - User slot is out of range."); - } - - slot.skips = json_get_int(line, MultiHostTableOption::JSON_SKIPS); - if (slot.skips < 0 || slot.skips > 7){ - throw StringException("Config Error - Skips is out of range."); - } + json_get_int(slot.game_slot, line, MultiHostTableOption::JSON_GAME_SLOT, 1, 2); + json_get_int(slot.user_slot, line, MultiHostTableOption::JSON_USER_SLOT, 1, 8); + json_get_int(slot.skips, line, MultiHostTableOption::JSON_SKIPS, 1, 7); - slot.backup_save = json_get_bool(line, MultiHostTableOption::JSON_BACKUP_SAVE); - slot.always_catchable = json_get_bool(line, MultiHostTableOption::JSON_ALWAYS_CATCHABLE); - slot.accept_FRs = json_get_bool(line, MultiHostTableOption::JSON_ACCEPT_FRS); + json_get_bool(slot.backup_save, line, MultiHostTableOption::JSON_BACKUP_SAVE); + json_get_bool(slot.always_catchable, line, MultiHostTableOption::JSON_ALWAYS_CATCHABLE); + json_get_bool(slot.accept_FRs, line, MultiHostTableOption::JSON_ACCEPT_FRS); - slot.move_slot = json_get_int(line, MultiHostTableOption::JSON_MOVE_SLOT); - if (slot.move_slot < 0 || slot.move_slot > 4){ - throw StringException("Config Error - Skips is out of range."); - } - - slot.dynamax = json_get_bool(line, MultiHostTableOption::JSON_DYNAMAX); + json_get_int(slot.move_slot, line, MultiHostTableOption::JSON_MOVE_SLOT, 0, 4); + json_get_bool(slot.dynamax, line, MultiHostTableOption::JSON_DYNAMAX); - slot.post_raid_delay = json_get_string(line, MultiHostTableOption::JSON_POST_RAID_DELAY); - uint32_t delay = parse_ticks_i32(slot.post_raid_delay); - if (delay > 65535){ - throw StringException("Config Error - Skips is out of range."); - } + json_get_string(slot.post_raid_delay, line, MultiHostTableOption::JSON_POST_RAID_DELAY); list.emplace_back(std::move(slot)); } @@ -108,9 +89,15 @@ MultiHostTableOption::MultiHostTableOption(QString label) {} void MultiHostTableOption::load_default(const QJsonValue& json){ + if (json.isNull()){ + return; + } m_default = parse_game_slot(json); } void MultiHostTableOption::load_current(const QJsonValue& json){ + if (json.isNull()){ + return; + } m_current = parse_game_slot(json); } QJsonValue MultiHostTableOption::write_default() const{ diff --git a/Common/Qt/Options/SimpleIntegerOption.cpp b/Common/Qt/Options/SimpleIntegerOption.cpp index 77b88f5894..e631a24988 100644 --- a/Common/Qt/Options/SimpleIntegerOption.cpp +++ b/Common/Qt/Options/SimpleIntegerOption.cpp @@ -12,7 +12,6 @@ namespace PokemonAutomation{ - template SimpleIntegerOption::SimpleIntegerOption( Type& backing, @@ -25,8 +24,7 @@ SimpleIntegerOption::SimpleIntegerOption( , m_min_value(min_value) , m_max_value(max_value) , m_default(default_value) - , m_current(default_value) - , m_backing(backing) + , m_current(backing) {} template SimpleIntegerOption::SimpleIntegerOption( @@ -45,12 +43,18 @@ SimpleIntegerOption::SimpleIntegerOption( template void SimpleIntegerOption::load_default(const QJsonValue& json){ + if (!json.isDouble()){ + return; + } m_default = json.toInt(); m_default = std::max(m_default, m_min_value); m_default = std::min(m_default, m_max_value); } template void SimpleIntegerOption::load_current(const QJsonValue& json){ + if (!json.isDouble()){ + return; + } m_current = json.toInt(); m_current = std::max(m_current, m_min_value); m_current = std::min(m_current, m_max_value); @@ -118,7 +122,6 @@ void SimpleIntegerOptionUI::restore_defaults(){ - template class SimpleIntegerOption; template class SimpleIntegerOption; template class SimpleIntegerOption; diff --git a/Common/Qt/Options/SimpleIntegerOption.h b/Common/Qt/Options/SimpleIntegerOption.h index 632a96a1ed..f950aaf69f 100644 --- a/Common/Qt/Options/SimpleIntegerOption.h +++ b/Common/Qt/Options/SimpleIntegerOption.h @@ -65,6 +65,5 @@ class SimpleIntegerOptionUI : public QWidget{ - } #endif diff --git a/Common/Qt/Options/SwitchDateOption.cpp b/Common/Qt/Options/SwitchDateOption.cpp index fe9c16840b..1bcc82c5d8 100644 --- a/Common/Qt/Options/SwitchDateOption.cpp +++ b/Common/Qt/Options/SwitchDateOption.cpp @@ -22,10 +22,10 @@ SwitchDateOption::SwitchDateOption( , m_current(default_value) {} void SwitchDateOption::load_default(const QJsonValue& json){ - m_default = json_parse_date(json); + json_parse_date(m_default, json); } void SwitchDateOption::load_current(const QJsonValue& json){ - m_current = json_parse_date(json); + json_parse_date(m_current, json); } QJsonValue SwitchDateOption::write_default() const{ return json_write_date(m_default); diff --git a/Common/Qt/Options/TimeExpressionOption.cpp b/Common/Qt/Options/TimeExpressionOption.cpp index 71bcd1364b..61c016226c 100644 --- a/Common/Qt/Options/TimeExpressionOption.cpp +++ b/Common/Qt/Options/TimeExpressionOption.cpp @@ -14,8 +14,6 @@ namespace PokemonAutomation{ - - template TimeExpressionOption::TimeExpressionOption( Type& backing, @@ -52,11 +50,17 @@ TimeExpressionOption::TimeExpressionOption( template void TimeExpressionOption::load_default(const QJsonValue& json){ + if (!json.isString()){ + return; + } m_default = json.toString(); update(); } template void TimeExpressionOption::load_current(const QJsonValue& json){ + if (!json.isString()){ + return; + } m_current = json.toString(); update(); } @@ -165,11 +169,11 @@ void TimeExpressionOptionUI::restore_defaults(){ - template class TimeExpressionOption; template class TimeExpressionOption; template class TimeExpressionOptionUI; template class TimeExpressionOptionUI; + } diff --git a/Common/Qt/QtJsonTools.cpp b/Common/Qt/QtJsonTools.cpp index d12a201cea..e52429a328 100644 --- a/Common/Qt/QtJsonTools.cpp +++ b/Common/Qt/QtJsonTools.cpp @@ -47,41 +47,14 @@ void write_json_file(const QString& path, const QJsonDocument& json){ -int json_cast_int(const QJsonValue& value){ - if (!value.isDouble()){ - throw StringException("Config Error - Expected a number."); - } - return value.toInt(); -} -QString json_cast_string(const QJsonValue& value){ - if (!value.isString()){ - throw StringException("Config Error - Expected a string."); - } - return value.toString(); -} -QJsonArray json_cast_array(const QJsonValue& value){ - if (!value.isArray()){ - throw StringException("Config Error - Expected an array."); - } - return value.toArray(); -} -QJsonObject json_cast_object(const QJsonValue& value){ - if (!value.isObject()){ - throw StringException("Config Error - Expected an object."); - } - return value.toObject(); -} - - - -QJsonValue json_get_value(const QJsonObject& obj, const QString& key){ +QJsonValue json_get_value_throw(const QJsonObject& obj, const QString& key){ auto iter = obj.find(key); if (iter == obj.end()){ throw StringException("Config Error - Key not found: " + key); } return *iter; } -bool json_get_bool(const QJsonObject& obj, const QString& key){ +bool json_get_bool_throw(const QJsonObject& obj, const QString& key){ auto iter = obj.find(key); if (iter == obj.end()){ throw StringException("Config Error - Key not found: " + key); @@ -91,7 +64,7 @@ bool json_get_bool(const QJsonObject& obj, const QString& key){ } return iter->toBool(); } -int json_get_int(const QJsonObject& obj, const QString& key){ +int json_get_int_throw(const QJsonObject& obj, const QString& key){ auto iter = obj.find(key); if (iter == obj.end()){ throw StringException("Config Error - Key not found: " + key); @@ -101,7 +74,7 @@ int json_get_int(const QJsonObject& obj, const QString& key){ } return iter->toInt(); } -double json_get_double(const QJsonObject& obj, const QString& key){ +double json_get_double_throw(const QJsonObject& obj, const QString& key){ auto iter = obj.find(key); if (iter == obj.end()){ throw StringException("Config Error - Key not found: " + key); @@ -111,7 +84,7 @@ double json_get_double(const QJsonObject& obj, const QString& key){ } return iter->toDouble(); } -QString json_get_string(const QJsonObject& obj, const QString& key){ +QString json_get_string_throw(const QJsonObject& obj, const QString& key){ auto iter = obj.find(key); if (iter == obj.end()){ throw StringException("Config Error - Key not found: " + key); @@ -121,7 +94,7 @@ QString json_get_string(const QJsonObject& obj, const QString& key){ } return iter->toString(); } -QJsonArray json_get_array(const QJsonObject& obj, const QString& key){ +QJsonArray json_get_array_throw(const QJsonObject& obj, const QString& key){ auto iter = obj.find(key); if (iter == obj.end()){ throw StringException("Config Error - Key not found: " + key); @@ -131,7 +104,7 @@ QJsonArray json_get_array(const QJsonObject& obj, const QString& key){ } return iter->toArray(); } -QJsonObject json_get_object(const QJsonObject& obj, const QString& key){ +QJsonObject json_get_object_throw(const QJsonObject& obj, const QString& key){ auto iter = obj.find(key); if (iter == obj.end()){ throw StringException("Config Error - Key not found: " + key); @@ -143,6 +116,29 @@ QJsonObject json_get_object(const QJsonObject& obj, const QString& key){ } +QJsonValue json_get_value_nothrow(const QJsonObject& obj, const QString& key){ + auto iter = obj.find(key); + if (iter == obj.end()){ + return QJsonArray(); + } + return *iter; +} +QJsonArray json_get_array_nothrow(const QJsonObject& obj, const QString& key){ + auto iter = obj.find(key); + if (iter == obj.end() || !iter->isArray()){ + return QJsonArray(); + } + return iter->toArray(); +} +QJsonObject json_get_object_nothrow(const QJsonObject& obj, const QString& key){ + auto iter = obj.find(key); + if (iter == obj.end() || !iter->isObject()){ + return QJsonObject(); + } + return iter->toObject(); +} + + bool valid_switch_date(const QDate& date){ if (date < QDate(2000, 1, 1)){ @@ -160,26 +156,6 @@ QJsonArray json_write_date(const QDate& date){ array += date.day(); return array; } -QDate json_parse_date(const QJsonValue& value){ - QJsonArray array = json_cast_array(value); - if (array.size() != 3){ - throw StringException("Config Error - Date should be 3 elements"); - } - int year = json_cast_int(array[0]); - int month = json_cast_int(array[1]); - int day = json_cast_int(array[2]); - QDate date(year, month, day); - if (!date.isValid()){ - throw StringException("Config Error - Invalid Date"); - } - if (!valid_switch_date(date)){ - throw StringException("Config Error - Invalid Switch Date"); - } - return date; -} -QDate json_get_date(const QJsonObject& obj, const QString& key){ - return json_parse_date(json_get_value(obj, key)); -} diff --git a/Common/Qt/QtJsonTools.h b/Common/Qt/QtJsonTools.h index 31c9dda394..9cf62f32f7 100644 --- a/Common/Qt/QtJsonTools.h +++ b/Common/Qt/QtJsonTools.h @@ -10,6 +10,8 @@ #include #include #include +#include +#include namespace PokemonAutomation{ @@ -17,23 +19,83 @@ namespace PokemonAutomation{ QJsonDocument read_json_file(const QString& path); void write_json_file(const QString& path, const QJsonDocument& json); -int json_cast_int(const QJsonValue& value); -QString json_cast_string(const QJsonValue& value); -QJsonArray json_cast_array(const QJsonValue& value); -QJsonObject json_cast_object(const QJsonValue& value); +QJsonValue json_get_value_throw(const QJsonObject& obj, const QString& key); +bool json_get_bool_throw(const QJsonObject& obj, const QString& key); +int json_get_int_throw(const QJsonObject& obj, const QString& key); +double json_get_double_throw(const QJsonObject& obj, const QString& key); +QString json_get_string_throw(const QJsonObject& obj, const QString& key); +QJsonArray json_get_array_throw(const QJsonObject& obj, const QString& key); +QJsonObject json_get_object_throw(const QJsonObject& obj, const QString& key); -QJsonValue json_get_value(const QJsonObject& obj, const QString& key); -bool json_get_bool(const QJsonObject& obj, const QString& key); -int json_get_int(const QJsonObject& obj, const QString& key); -double json_get_double(const QJsonObject& obj, const QString& key); -QString json_get_string(const QJsonObject& obj, const QString& key); -QJsonArray json_get_array(const QJsonObject& obj, const QString& key); -QJsonObject json_get_object(const QJsonObject& obj, const QString& key); +QJsonValue json_get_value_nothrow(const QJsonObject& obj, const QString& key); +QJsonArray json_get_array_nothrow(const QJsonObject& obj, const QString& key); +QJsonObject json_get_object_nothrow(const QJsonObject& obj, const QString& key); bool valid_switch_date(const QDate& date); QJsonArray json_write_date(const QDate& date); -QDate json_parse_date(const QJsonValue& value); -QDate json_get_date(const QJsonObject& obj, const QString& key); + + +template +bool json_get_bool(Destination& destination, const QJsonObject& obj, const QString& key){ + auto iter = obj.find(key); + if (iter == obj.end() || !iter->isBool()){ + return false; + } + destination = iter->toBool(); + return true; +} +template +bool json_get_int( + Destination& destination, const QJsonObject& obj, const QString& key, + int min_value = std::numeric_limits::min(), + int max_value = std::numeric_limits::max() +){ + auto iter = obj.find(key); + if (iter == obj.end() || !iter->isDouble()){ + return false; + } + int x = iter->toInt(); + if (x < min_value){ + x = min_value; + } + if (x > max_value){ + x = max_value; + } + destination = x; + return true; +} +template +bool json_get_string(Destination& destination, const QJsonObject& obj, const QString& key){ + auto iter = obj.find(key); + if (iter == obj.end() || !iter->isString()){ + return false; + } + destination = iter->toString(); + return true; +} + + +template +bool json_parse_date(Destination& date, const QJsonValue& value){ + QJsonArray array = value.toArray(); + if (array.size() != 3){ + return false; + } + for (int c = 0; c < 3; c++){ + if (!array[c].isDouble()){ + return false; + } + } + int year = array[0].toInt(); + int month = array[1].toInt(); + int day = array[2].toInt(); + QDate try_date(year, month, day); + if (!try_date.isValid() || !valid_switch_date(try_date)){ + return false; + } + date = try_date; + return true; +} } diff --git a/Common/Qt/StringException.h b/Common/Qt/StringException.h index aff6891c11..b12a823606 100644 --- a/Common/Qt/StringException.h +++ b/Common/Qt/StringException.h @@ -45,4 +45,4 @@ class StringException{ } -#endif \ No newline at end of file +#endif diff --git a/GeneratorConfig/AutoHost-Rolling.json b/GeneratorConfig/AutoHost-Rolling.json index 5e2828e836..fd9b4931af 100644 --- a/GeneratorConfig/AutoHost-Rolling.json +++ b/GeneratorConfig/AutoHost-Rolling.json @@ -84,6 +84,15 @@ "98-Default": "0 * TICKS_PER_SECOND", "99-Current": "0 * TICKS_PER_SECOND" }, + { + "00-Type": "SimpleInteger", + "01-Label": "Troll hosting:
Zero disables the troll hosting mode, 1 attacks the first ally, 2 attacks the second, 3 attacks the third. Dynamaxing will disable this option.", + "02-Declaration": "const uint8_t TROLL_HOSTING", + "03-MinValue": 0, + "04-MaxValue": 3, + "98-Default": 0, + "99-Current": 0 + }, { "00-Type": "SimpleInteger", "01-Label": "1st Move Select Slot:
Zero disables 1st move select.", diff --git a/GeneratorSource/Source/Options/BooleanCheckBox.cpp b/GeneratorSource/Source/Options/BooleanCheckBox.cpp index 0231adcd4b..0fcaff5088 100644 --- a/GeneratorSource/Source/Options/BooleanCheckBox.cpp +++ b/GeneratorSource/Source/Options/BooleanCheckBox.cpp @@ -31,8 +31,8 @@ BooleanCheckBox::BooleanCheckBox(const QJsonObject& obj) : SingleStatementOption(obj) , BooleanCheckBoxOption(SingleStatementOption::m_label, false) { - load_default(json_get_bool(obj, JSON_DEFAULT)); - load_current(json_get_bool(obj, JSON_CURRENT)); + load_default(json_get_value_throw(obj, JSON_DEFAULT)); + load_current(json_get_value_throw(obj, JSON_CURRENT)); } void BooleanCheckBox::restore_defaults(){ BooleanCheckBoxOption::restore_defaults(); diff --git a/GeneratorSource/Source/Options/ConfigItem.cpp b/GeneratorSource/Source/Options/ConfigItem.cpp index f065e9a335..93ce134682 100644 --- a/GeneratorSource/Source/Options/ConfigItem.cpp +++ b/GeneratorSource/Source/Options/ConfigItem.cpp @@ -16,7 +16,7 @@ const QString ConfigItem::JSON_TYPE = "00-Type"; const QString ConfigItem::JSON_LABEL = "01-Label"; ConfigItem::ConfigItem(const QJsonObject& obj) - : m_label(json_get_string(obj, JSON_LABEL)) + : m_label(json_get_string_throw(obj, JSON_LABEL)) {} QJsonObject ConfigItem::to_json() const{ @@ -33,7 +33,7 @@ std::map& OPTION_FACTORIES(){ return map; } std::unique_ptr parse_option(const QJsonObject& obj){ - QString type = json_get_string(obj, ConfigItem::JSON_TYPE); + QString type = json_get_string_throw(obj, ConfigItem::JSON_TYPE); std::map& map = OPTION_FACTORIES(); auto iter = map.find(type); if (iter == map.end()){ diff --git a/GeneratorSource/Source/Options/EnumDropdown.cpp b/GeneratorSource/Source/Options/EnumDropdown.cpp index ba54d9d8b5..67dd54289b 100644 --- a/GeneratorSource/Source/Options/EnumDropdown.cpp +++ b/GeneratorSource/Source/Options/EnumDropdown.cpp @@ -35,7 +35,7 @@ int EnumDropdown_init = register_option( EnumDropdown::EnumDropdown(const QJsonObject& obj) : SingleStatementOption(obj) { - QJsonArray options = json_get_array(obj, JSON_OPTIONS); + QJsonArray options = json_get_array_throw(obj, JSON_OPTIONS); for (const auto option : options){ if (!option.isArray()){ throw StringException("Config Error - Expected Array: " + JSON_OPTIONS); @@ -44,9 +44,12 @@ EnumDropdown::EnumDropdown(const QJsonObject& obj) if (pair.size() != 2){ throw StringException("Config Error - Enum pairs should be 2 elements: " + JSON_OPTIONS); } + if (!pair[0].isString() || !pair[1].isString()){ + throw StringException("Config Error - Enum pairs should be strings: " + JSON_OPTIONS); + } m_options.emplace_back( - json_cast_string(pair.at(0)), - json_cast_string(pair.at(1)) + pair[0].toString(), + pair[1].toString() ); } for (size_t c = 0; c < m_options.size(); c++){ @@ -55,14 +58,14 @@ EnumDropdown::EnumDropdown(const QJsonObject& obj) } } { - auto iter = m_map.find(json_get_string(obj, JSON_DEFAULT)); + auto iter = m_map.find(json_get_string_throw(obj, JSON_DEFAULT)); if (iter == m_map.end()){ throw StringException("Config Error - Unrecognized token."); } m_default = iter->second; } { - auto iter = m_map.find(json_get_string(obj, JSON_CURRENT)); + auto iter = m_map.find(json_get_string_throw(obj, JSON_CURRENT)); if (iter == m_map.end()){ throw StringException("Config Error - Unrecognized token."); } diff --git a/GeneratorSource/Source/Options/FixedCode.cpp b/GeneratorSource/Source/Options/FixedCode.cpp index 03a98c381c..1ebe0a8121 100644 --- a/GeneratorSource/Source/Options/FixedCode.cpp +++ b/GeneratorSource/Source/Options/FixedCode.cpp @@ -31,9 +31,9 @@ int FixedCode_init = register_option( FixedCode::FixedCode(const QJsonObject& obj) : SingleStatementOption(obj) - , m_digits(json_get_int(obj, JSON_DIGITS)) - , m_default(json_get_string(obj, JSON_DEFAULT)) - , m_current(json_get_string(obj, JSON_CURRENT)) + , m_digits(json_get_int_throw(obj, JSON_DIGITS)) + , m_default(json_get_string_throw(obj, JSON_DEFAULT)) + , m_current(json_get_string_throw(obj, JSON_CURRENT)) { if (!validate_code(m_digits, m_default)){ throw StringException("Invalid code."); diff --git a/GeneratorSource/Source/Options/FloatingPoint.cpp b/GeneratorSource/Source/Options/FloatingPoint.cpp index 7819aad7a3..35205ed283 100644 --- a/GeneratorSource/Source/Options/FloatingPoint.cpp +++ b/GeneratorSource/Source/Options/FloatingPoint.cpp @@ -34,12 +34,12 @@ FloatingPoint::FloatingPoint(const QJsonObject& obj) : SingleStatementOption(obj) , FloatingPointOption( SingleStatementOption::m_label, - json_get_double(obj, JSON_MIN_VALUE), - json_get_double(obj, JSON_MAX_VALUE), - json_get_double(obj, JSON_DEFAULT) + json_get_double_throw(obj, JSON_MIN_VALUE), + json_get_double_throw(obj, JSON_MAX_VALUE), + json_get_double_throw(obj, JSON_DEFAULT) ) { - m_current = json_get_double(obj, JSON_CURRENT); + m_current = json_get_double_throw(obj, JSON_CURRENT); } bool FloatingPoint::is_valid() const{ return FloatingPointOption::is_valid(); diff --git a/GeneratorSource/Source/Options/FossilTable.cpp b/GeneratorSource/Source/Options/FossilTable.cpp index 574e29d8a5..e693c49599 100644 --- a/GeneratorSource/Source/Options/FossilTable.cpp +++ b/GeneratorSource/Source/Options/FossilTable.cpp @@ -35,8 +35,8 @@ FossilTable::FossilTable(const QJsonObject& obj) : SingleStatementOption(obj) , FossilTableOption(SingleStatementOption::m_label) { - load_default(json_get_array(obj, JSON_DEFAULT)); - load_current(json_get_array(obj, JSON_CURRENT)); + load_default(json_get_array_throw(obj, JSON_DEFAULT)); + load_current(json_get_array_throw(obj, JSON_CURRENT)); } bool FossilTable::is_valid() const{ return FossilTableOption::is_valid(); diff --git a/GeneratorSource/Source/Options/MultiHostTable.cpp b/GeneratorSource/Source/Options/MultiHostTable.cpp index 1eed449872..46e5191cea 100644 --- a/GeneratorSource/Source/Options/MultiHostTable.cpp +++ b/GeneratorSource/Source/Options/MultiHostTable.cpp @@ -40,8 +40,8 @@ MultiHostTable::MultiHostTable(const QJsonObject& obj) : SingleStatementOption(obj) , MultiHostTableOption(SingleStatementOption::m_label) { - load_default(json_get_array(obj, JSON_DEFAULT)); - load_current(json_get_array(obj, JSON_CURRENT)); + load_default(json_get_array_throw(obj, JSON_DEFAULT)); + load_current(json_get_array_throw(obj, JSON_CURRENT)); } bool MultiHostTable::is_valid() const{ return MultiHostTableOption::is_valid(); diff --git a/GeneratorSource/Source/Options/RandomCode.cpp b/GeneratorSource/Source/Options/RandomCode.cpp index 888f4d088b..24ff012d3d 100644 --- a/GeneratorSource/Source/Options/RandomCode.cpp +++ b/GeneratorSource/Source/Options/RandomCode.cpp @@ -37,13 +37,13 @@ int RandomCode_init = register_option( RandomCode::RandomCode(const QJsonObject& obj) : ConfigItem(obj) - , m_declaration_random(json_get_string(obj, JSON_DECLARATION_RANDOM)) - , m_declaration_code(json_get_string(obj, JSON_DECLARATION_CODE)) - , m_digits(json_get_int(obj, JSON_DIGITS)) - , m_default_random(json_get_int(obj, JSON_DEFAULT_RANDOM)) - , m_default_code(json_get_string(obj, JSON_DEFAULT_CODE)) - , m_current_random(json_get_int(obj, JSON_CURRENT_RANDOM)) - , m_current_code(json_get_string(obj, JSON_CURRENT_CODE)) + , m_declaration_random(json_get_string_throw(obj, JSON_DECLARATION_RANDOM)) + , m_declaration_code(json_get_string_throw(obj, JSON_DECLARATION_CODE)) + , m_digits(json_get_int_throw(obj, JSON_DIGITS)) + , m_default_random(json_get_int_throw(obj, JSON_DEFAULT_RANDOM)) + , m_default_code(json_get_string_throw(obj, JSON_DEFAULT_CODE)) + , m_current_random(json_get_int_throw(obj, JSON_CURRENT_RANDOM)) + , m_current_code(json_get_string_throw(obj, JSON_CURRENT_CODE)) { if (m_default_random > m_digits){ m_default_random = m_digits; diff --git a/GeneratorSource/Source/Options/SimpleInteger.cpp b/GeneratorSource/Source/Options/SimpleInteger.cpp index d525a9406c..4f7194351e 100644 --- a/GeneratorSource/Source/Options/SimpleInteger.cpp +++ b/GeneratorSource/Source/Options/SimpleInteger.cpp @@ -34,12 +34,12 @@ SimpleInteger::SimpleInteger(const QJsonObject& obj) : SingleStatementOption(obj) , SimpleIntegerOption( SingleStatementOption::m_label, - json_get_int(obj, JSON_MIN_VALUE), - json_get_int(obj, JSON_MAX_VALUE), - json_get_int(obj, JSON_DEFAULT) + json_get_int_throw(obj, JSON_MIN_VALUE), + json_get_int_throw(obj, JSON_MAX_VALUE), + json_get_int_throw(obj, JSON_DEFAULT) ) { - m_current = json_get_int(obj, JSON_CURRENT); + m_current = json_get_int_throw(obj, JSON_CURRENT); } bool SimpleInteger::is_valid() const{ return SimpleIntegerOption::is_valid(); diff --git a/GeneratorSource/Source/Options/SingleStatementOption.cpp b/GeneratorSource/Source/Options/SingleStatementOption.cpp index d2ef79856c..5ddd363736 100644 --- a/GeneratorSource/Source/Options/SingleStatementOption.cpp +++ b/GeneratorSource/Source/Options/SingleStatementOption.cpp @@ -18,7 +18,7 @@ const QString SingleStatementOption::JSON_CURRENT = "99-Current"; SingleStatementOption::SingleStatementOption(const QJsonObject& obj) : ConfigItem(obj) - , m_declaration(json_get_string(obj, JSON_DECLARATION)) + , m_declaration(json_get_string_throw(obj, JSON_DECLARATION)) {} QJsonObject SingleStatementOption::to_json() const{ diff --git a/GeneratorSource/Source/Options/SwitchDate.cpp b/GeneratorSource/Source/Options/SwitchDate.cpp index 77036fedcb..d32fa1ee00 100644 --- a/GeneratorSource/Source/Options/SwitchDate.cpp +++ b/GeneratorSource/Source/Options/SwitchDate.cpp @@ -32,8 +32,8 @@ SwitchDate::SwitchDate(const QJsonObject& obj) : SingleStatementOption(obj) , SwitchDateOption(SingleStatementOption::m_label, QDate(2000, 1, 1)) { - load_default(json_get_value(obj, JSON_DEFAULT)); - load_current(json_get_value(obj, JSON_CURRENT)); + load_default(json_get_value_throw(obj, JSON_DEFAULT)); + load_current(json_get_value_throw(obj, JSON_CURRENT)); } bool SwitchDate::is_valid() const{ return SwitchDateOption::is_valid(); diff --git a/GeneratorSource/Source/Options/TimeExpression.cpp b/GeneratorSource/Source/Options/TimeExpression.cpp index 5321cb6f0a..380644711c 100644 --- a/GeneratorSource/Source/Options/TimeExpression.cpp +++ b/GeneratorSource/Source/Options/TimeExpression.cpp @@ -37,12 +37,12 @@ TimeExpression::TimeExpression(const QJsonObject& obj) : SingleStatementOption(obj) , TimeExpressionOption( SingleStatementOption::m_label, - json_get_int(obj, JSON_MIN_VALUE), - json_get_int(obj, JSON_MAX_VALUE), - json_get_string(obj, JSON_DEFAULT) + json_get_int_throw(obj, JSON_MIN_VALUE), + json_get_int_throw(obj, JSON_MAX_VALUE), + json_get_string_throw(obj, JSON_DEFAULT) ) { - load_current(json_get_string(obj, JSON_CURRENT)); + load_current(json_get_string_throw(obj, JSON_CURRENT)); update(); } diff --git a/GeneratorSource/Source/Panels/ConfigSet.cpp b/GeneratorSource/Source/Panels/ConfigSet.cpp index 36e4f238b2..9becce2902 100644 --- a/GeneratorSource/Source/Panels/ConfigSet.cpp +++ b/GeneratorSource/Source/Panels/ConfigSet.cpp @@ -26,9 +26,9 @@ const QString ConfigSet::JSON_DESCRIPTION = "2-Description"; const QString ConfigSet::JSON_OPTIONS = "3-Options"; ConfigSet::ConfigSet(const QJsonObject& obj) - : RightPanel(json_get_string(obj, JSON_CONFIG_NAME)) - , m_path(json_get_string(obj, JSON_CONFIG_PATH)) - , m_description(json_get_string(obj, JSON_DESCRIPTION)) + : RightPanel(json_get_string_throw(obj, JSON_CONFIG_NAME)) + , m_path(json_get_string_throw(obj, JSON_CONFIG_PATH)) + , m_description(json_get_string_throw(obj, JSON_DESCRIPTION)) {} QJsonDocument ConfigSet::to_json() const{ diff --git a/GeneratorSource/Source/Panels/JsonProgram.cpp b/GeneratorSource/Source/Panels/JsonProgram.cpp index d98199d181..bb977f944a 100644 --- a/GeneratorSource/Source/Panels/JsonProgram.cpp +++ b/GeneratorSource/Source/Panels/JsonProgram.cpp @@ -21,7 +21,7 @@ Program_JsonFile::Program_JsonFile(const QString& filepath) Program_JsonFile::Program_JsonFile(const QJsonObject& obj) : Program(obj) { - for (const auto item : json_get_array(obj, JSON_PARAMETERS)){ + for (const auto item : json_get_array_throw(obj, JSON_PARAMETERS)){ if (!item.isObject()){ throw StringException("Config Error - Expected and object."); } diff --git a/GeneratorSource/Source/Panels/JsonSettings.cpp b/GeneratorSource/Source/Panels/JsonSettings.cpp index e74866da78..89d037521a 100644 --- a/GeneratorSource/Source/Panels/JsonSettings.cpp +++ b/GeneratorSource/Source/Panels/JsonSettings.cpp @@ -21,7 +21,7 @@ Settings_JsonFile::Settings_JsonFile(const QString& filepath) Settings_JsonFile::Settings_JsonFile(const QJsonObject& obj) : ConfigSet(obj) { - for (const auto item : json_get_array(obj, JSON_OPTIONS)){ + for (const auto item : json_get_array_throw(obj, JSON_OPTIONS)){ if (!item.isObject()){ throw StringException("Config Error - Expected and object."); } diff --git a/GeneratorSource/Source/Panels/Program.cpp b/GeneratorSource/Source/Panels/Program.cpp index 9ac6187ad1..63027c4cb9 100644 --- a/GeneratorSource/Source/Panels/Program.cpp +++ b/GeneratorSource/Source/Panels/Program.cpp @@ -39,8 +39,8 @@ Program::Program(const QString& name, const QString& description) , m_description(description) {} Program::Program(const QJsonObject& obj) - : RightPanel(json_get_string(obj, JSON_PROGRAM_NAME)) - , m_description(json_get_string(obj, JSON_DESCRIPTION)) + : RightPanel(json_get_string_throw(obj, JSON_PROGRAM_NAME)) + , m_description(json_get_string_throw(obj, JSON_DESCRIPTION)) {} Program::~Program(){ if (m_builder.joinable()){ diff --git a/GeneratorSource/Source/Tools/PersistentSettings.cpp b/GeneratorSource/Source/Tools/PersistentSettings.cpp index 637aea98d1..f2b2368bf0 100644 --- a/GeneratorSource/Source/Tools/PersistentSettings.cpp +++ b/GeneratorSource/Source/Tools/PersistentSettings.cpp @@ -20,7 +20,7 @@ using std::endl; namespace PokemonAutomation{ -const QString VERSION = "v0.4.0"; +const QString VERSION = "v0.4.1"; const QString DISCORD = "https://discord.gg/cQ4gWxN"; const QString GITHUB_REPO = "https://github.com/PokemonAutomation/SwSh-Arduino"; @@ -68,10 +68,7 @@ void PersistentSettings::load(){ QJsonObject root = doc.object(); - board_index = json_get_int(root, "Board"); - if (board_index >= 4){ - board_index = 0; - } + json_get_int(board_index, root, "Board", 0, 3); }catch (const StringException& str){ std::cout << ("Error Parsing " + SETTINGS_NAME + ": " + str.message()).toUtf8().data() << std::endl; diff --git a/NativePrograms/AutoHost-Rolling.c b/NativePrograms/AutoHost-Rolling.c index c1e27ccf4d..19bde262c9 100644 --- a/NativePrograms/AutoHost-Rolling.c +++ b/NativePrograms/AutoHost-Rolling.c @@ -75,6 +75,7 @@ const uint16_t EXTRA_DELAY_BETWEEN_RAIDS = 0 * TICKS_PER_SECOND; // MOVE_SLOT = # Select this move for the first turn. const uint8_t MOVE_SLOT = 0; const bool DYNAMAX = true; // Must set to false if you cannot dmax. +const uint8_t TROLL_HOSTING = 0; //0 to disable, 1/2/3 to attack one of your allies. // Alternate Games: (experimental feature) diff --git a/NativePrograms/PokemonSwShPrograms/AutoHost-Rolling.h b/NativePrograms/PokemonSwShPrograms/AutoHost-Rolling.h index 2b95304410..6374a612f6 100644 --- a/NativePrograms/PokemonSwShPrograms/AutoHost-Rolling.h +++ b/NativePrograms/PokemonSwShPrograms/AutoHost-Rolling.h @@ -69,6 +69,7 @@ extern const uint16_t EXTRA_DELAY_BETWEEN_RAIDS; // MOVE_SLOT = # Select this move for the first turn. extern const uint8_t MOVE_SLOT; extern const bool DYNAMAX; // Must set to false if you cannot dmax. +extern const uint8_t TROLL_HOSTING; // 0 disables the troll hosting option, 1 attacks the first ally, 2 attacks the second one, 3 attacks the third one. Dynamaxing will disable this option. // Alternate Games: (experimental feature) diff --git a/SerialPrograms/SerialPrograms.pro b/SerialPrograms/SerialPrograms.pro index 030921c44f..a9fb0601be 100644 --- a/SerialPrograms/SerialPrograms.pro +++ b/SerialPrograms/SerialPrograms.pro @@ -10,7 +10,7 @@ CONFIG += c++14 INCLUDEPATH += ../ INCLUDEPATH += Source/ -#CONFIG += force_debug_info +CONFIG += force_debug_info QMAKE_CXXFLAGS += -msse4.2 QMAKE_CXXFLAGS += -Wnarrowing @@ -99,6 +99,7 @@ SOURCES += \ Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.cpp \ Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp \ Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp \ + Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.cpp \ Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.cpp \ Source/PokemonSwSh/Inference/PokemonSwSh_RaidLobbyReader.cpp \ Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp \ @@ -106,6 +107,7 @@ SOURCES += \ Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp \ Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp \ Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp \ Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp \ Source/PokemonSwSh/Options/Catchability.cpp \ Source/PokemonSwSh/Options/EggStepCount.cpp \ @@ -125,10 +127,12 @@ SOURCES += \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp \ @@ -209,10 +213,10 @@ HEADERS += \ Source/CommonFramework/Inference/FillGeometry.h \ Source/CommonFramework/Inference/FillMatrix.h \ Source/CommonFramework/Inference/FloatPixel.h \ - Source/CommonFramework/Inference/FloatStatAccumulator.h \ Source/CommonFramework/Inference/ImageTools.h \ Source/CommonFramework/Inference/InferenceThrottler.h \ Source/CommonFramework/Inference/InferenceTypes.h \ + Source/CommonFramework/Inference/StatAccumulator.h \ Source/CommonFramework/Inference/TimeWindowStatTracker.h \ Source/CommonFramework/Options/BooleanCheckBox.h \ Source/CommonFramework/Options/ConfigOption.h \ @@ -260,6 +264,7 @@ HEADERS += \ Source/PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h \ Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.h \ Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h \ + Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h \ Source/PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h \ Source/PokemonSwSh/Inference/PokemonSwSh_RaidLobbyReader.h \ Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h \ @@ -268,6 +273,7 @@ HEADERS += \ Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h \ Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h \ Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h \ + Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.h \ Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h \ Source/PokemonSwSh/Options/Catchability.h \ Source/PokemonSwSh/Options/EggStepCount.h \ @@ -289,10 +295,12 @@ HEADERS += \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h \ + Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntTools.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.h \ diff --git a/SerialPrograms/Source/CommonFramework/Globals.cpp b/SerialPrograms/Source/CommonFramework/Globals.cpp index 693383e023..5d984c35fc 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.cpp +++ b/SerialPrograms/Source/CommonFramework/Globals.cpp @@ -9,7 +9,7 @@ namespace PokemonAutomation{ -const QString VERSION = "v0.4.7"; +const QString VERSION = "v0.5.3"; const QString DISCORD = "https://discord.gg/cQ4gWxN"; const QString GITHUB_REPO = "https://github.com/PokemonAutomation/SwSh-Arduino"; diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp index efa579f7f7..9289604174 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp @@ -17,11 +17,11 @@ namespace PokemonAutomation{ bool fill_geometry( FillGeometry& geometry, - FillMatrix& matrix, + FillMatrix& matrix, FillMatrix::ObjectID required_existing_id, int x, int y, bool allow_diagonal, - FillMatrix::ObjectID id + FillMatrix::ObjectID object_id ){ - if (matrix[y][x] != 1){ + if (matrix[y][x] != required_existing_id){ return false; } @@ -42,7 +42,7 @@ bool fill_geometry( std::vector> queue; queue.emplace_back(x, y); - matrix[y][x] = id; + matrix[y][x] = object_id; // cout << " (|" << starty << "," << startx << " : " << id << " = " << matrix[starty][startx] << ")" << endl; while (!queue.empty()){ @@ -57,32 +57,32 @@ bool fill_geometry( // Load the verticals. if (y - 1 >= 0){ FillMatrix::ObjectID& neighbor = matrix[y - 1][x]; - if (neighbor == 1){ - neighbor = id; + if (neighbor == required_existing_id){ + neighbor = object_id; queue.emplace_back(x, y - 1); min_y = std::min(min_y, y - 1); } } if (y + 1 < height){ FillMatrix::ObjectID& neighbor = matrix[y + 1][x]; - if (neighbor == 1){ - neighbor = id; + if (neighbor == required_existing_id){ + neighbor = object_id; queue.emplace_back(x, y + 1); max_y = std::max(max_y, y + 1); } } if (x - 1 >= 0){ FillMatrix::ObjectID& neighbor = matrix[y][x - 1]; - if (neighbor == 1){ - neighbor = id; + if (neighbor == required_existing_id){ + neighbor = object_id; queue.emplace_back(x - 1, y); min_x = std::min(min_x, x - 1); } } if (x + 1 < width){ FillMatrix::ObjectID& neighbor = matrix[y][x + 1]; - if (neighbor == 1){ - neighbor = id; + if (neighbor == required_existing_id){ + neighbor = object_id; queue.emplace_back(x + 1, y); max_x = std::max(max_x, x + 1); } @@ -95,8 +95,8 @@ bool fill_geometry( // If all verticals are blank, try the diagonals. if (y - 1 >= 0 && x - 1 >= 0){ FillMatrix::ObjectID& neighbor = matrix[y - 1][x - 1]; - if (neighbor == 1){ - neighbor = id; + if (neighbor == required_existing_id){ + neighbor = object_id; queue.emplace_back(x - 1, y - 1); min_x = std::min(min_x, x - 1); min_y = std::min(min_y, y - 1); @@ -104,8 +104,8 @@ bool fill_geometry( } if (y - 1 >= 0 && x + 1 < width){ FillMatrix::ObjectID& neighbor = matrix[y - 1][x + 1]; - if (neighbor == 1){ - neighbor = id; + if (neighbor == required_existing_id){ + neighbor = object_id; queue.emplace_back(x + 1, y - 1); max_x = std::max(max_x, x + 1); min_y = std::min(min_y, y - 1); @@ -113,8 +113,8 @@ bool fill_geometry( } if (y + 1 < height && x - 1 >= 0){ FillMatrix::ObjectID& neighbor = matrix[y + 1][x - 1]; - if (neighbor == 1){ - neighbor = id; + if (neighbor == required_existing_id){ + neighbor = object_id; queue.emplace_back(x - 1, y + 1); min_x = std::min(min_x, x - 1); max_y = std::max(max_y, y + 1); @@ -122,8 +122,8 @@ bool fill_geometry( } if (y + 1 < height && x + 1 < width){ FillMatrix::ObjectID& neighbor = matrix[y + 1][x + 1]; - if (neighbor == 1){ - neighbor = id; + if (neighbor == required_existing_id){ + neighbor = object_id; queue.emplace_back(x + 1, y + 1); max_x = std::max(max_x, x + 1); max_y = std::max(max_y, y + 1); @@ -136,14 +136,18 @@ bool fill_geometry( geometry.box.min_y = min_y; geometry.box.max_x = max_x + 1; geometry.box.max_y = max_y + 1; - geometry.id = id; + geometry.id = object_id; geometry.area = area; geometry.center_x = (int)(sum_x / area); geometry.center_y = (int)(sum_y / area); return true; } -std::vector find_all_objects(FillMatrix& matrix, bool allow_diagonal, size_t min_area){ +std::vector find_all_objects( + FillMatrix& matrix, FillMatrix::ObjectID required_existing_id, + bool allow_diagonal, + size_t min_area +){ size_t width = matrix.width(); size_t height = matrix.height(); std::vector objects; @@ -151,7 +155,7 @@ std::vector find_all_objects(FillMatrix& matrix, bool allow_diagon for (size_t r = 0; r < height; r++){ for (size_t c = 0; c < width; c++){ FillGeometry region; - if (fill_geometry(region, matrix, c, r, allow_diagonal, id)){ + if (fill_geometry(region, matrix, required_existing_id, c, r, allow_diagonal, id)){ id++; if (region.area >= min_area){ objects.emplace_back(region); diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h index 953bfe699d..7b21ca0a4e 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h +++ b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h @@ -35,12 +35,12 @@ enum class MapState : uint8_t{ bool fill_geometry( FillGeometry& geometry, - FillMatrix& matrix, + FillMatrix& matrix, FillMatrix::ObjectID required_existing_id, int x, int y, bool allow_diagonal, FillMatrix::ObjectID id ); std::vector find_all_objects( - FillMatrix& matrix, + FillMatrix& matrix, FillMatrix::ObjectID required_existing_id, bool allow_diagonal, size_t min_area = 10 ); diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp index 2c69c09353..a00537ee98 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp @@ -6,6 +6,10 @@ #include "FillMatrix.h" +#include +using std::cout; +using std::endl; + namespace PokemonAutomation{ @@ -57,6 +61,24 @@ FillMatrix FillMatrix::extract(PixelBox box, ObjectID id) const{ +std::string FillMatrix::dump() const{ + int width = m_width; + int height = m_height; + + std::string str; + str += "{\n"; + for (int r = 0; r < height; r++){ + str += " "; + for (int c = 0; c < width; c++){ + str += " "; + str += std::to_string((int)(*this)[r][c]); + } + str += "\n"; + } + str += "}\n"; + return str; +} + diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h index 9cf289020e..179671cad5 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h +++ b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h @@ -35,6 +35,8 @@ class FillMatrix{ FillMatrix extract(PixelBox box, ObjectID id) const; + std::string dump() const; + public: template bool apply_filter(Image&& image, Filter& filter); diff --git a/SerialPrograms/Source/CommonFramework/Inference/FloatStatAccumulator.h b/SerialPrograms/Source/CommonFramework/Inference/FloatStatAccumulator.h deleted file mode 100644 index 5c63b6f44e..0000000000 --- a/SerialPrograms/Source/CommonFramework/Inference/FloatStatAccumulator.h +++ /dev/null @@ -1,49 +0,0 @@ -/* Float Stat Accumulator - * - * From: https://github.com/PokemonAutomation/Arduino-Source - * - */ - -#ifndef PokemonAutomation_CommonFramework_FloatStatAccumulator_H -#define PokemonAutomation_CommonFramework_FloatStatAccumulator_H - -#include -#include -#include - -class FloatStatAccumulator{ -public: - using StatObject = double; - -public: - void operator+=(double x){ - m_count++; - m_sum += x; - m_sqr += x*x; - m_min = std::min(m_min, x); - m_max = std::max(m_max, x); - } - - uint64_t count() const{ return m_count; }; - double min() const{ return m_min; } - double max() const{ return m_max; } - double mean() const{ - return m_sum / m_count; - } - double stddev() const{ - return std::sqrt((m_sqr - m_sum*m_sum / m_count) / (m_count - 1)); - } - double diff_metric(double reference) const{ - return std::sqrt((m_sqr + reference*(reference * m_count - 2 * m_sum)) / m_count); - } - -private: - uint64_t m_count = 0; - double m_sum = 0; - double m_sqr = 0; - double m_min = INFINITY; - double m_max = -INFINITY; -}; - - -#endif diff --git a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp index 9e2231df5e..d4694147eb 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp @@ -54,6 +54,9 @@ InferenceBox translate_to_parent( ); } +QImage extract_box(const QImage& image, const PixelBox& box){ + return image.copy(box.min_x, box.min_y, box.width(), box.height()); +} QImage extract_box(const QImage& image, const InferenceBox& box){ return image.copy( (int)(image.width() * box.x + 0.5), diff --git a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.h b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.h index 40d45df0be..84ed1e52d2 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.h +++ b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.h @@ -23,6 +23,7 @@ InferenceBox translate_to_parent( const PixelBox& box ); +QImage extract_box(const QImage& image, const PixelBox& box); QImage extract_box(const QImage& image, const InferenceBox& box); double image_diff(const QImage& x, const QImage& y); diff --git a/SerialPrograms/Source/CommonFramework/Inference/StatAccumulator.h b/SerialPrograms/Source/CommonFramework/Inference/StatAccumulator.h new file mode 100644 index 0000000000..9ac8367aa5 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Inference/StatAccumulator.h @@ -0,0 +1,99 @@ +/* Stat Accumulator + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_CommonFramework_StatAccumulator_H +#define PokemonAutomation_CommonFramework_StatAccumulator_H + +#include +#include +#include +#include +#include "Common/Clientside/PrettyPrint.h" + +namespace PokemonAutomation{ + + +class StatAccumulatorI32{ +public: + using StatObject = double; + +public: + void operator+=(uint32_t x){ + m_count++; + m_sum += x; + m_sqr += (uint64_t)x*x; + m_min = std::min(m_min, x); + m_max = std::max(m_max, x); + } + + uint64_t count() const{ return m_count; }; + uint32_t min() const{ return m_min; } + uint32_t max() const{ return m_max; } + double mean() const{ + return (double)m_sum / m_count; + } + double stddev() const{ + double diff = m_sqr - (double)m_sum*m_sum / m_count; + return std::sqrt(diff / (m_count - 1)); + } + + std::string dump() const{ + std::string str; + str += "Count = " + tostr_u_commas(m_count); + str += ", Mean = " + std::to_string(mean()); + str += ", Stddev = " + std::to_string(stddev()); + str += ", Min = " + std::to_string(min()); + str += ", Max = " + std::to_string(max()); + return str; + } + +private: + uint64_t m_count = 0; + uint64_t m_sum = 0; + uint64_t m_sqr = 0; + uint32_t m_min = (uint32_t)-1; + uint32_t m_max = 0; +}; + + + +class FloatStatAccumulator{ +public: + using StatObject = double; + +public: + void operator+=(double x){ + m_count++; + m_sum += x; + m_sqr += x*x; + m_min = std::min(m_min, x); + m_max = std::max(m_max, x); + } + + uint64_t count() const{ return m_count; }; + double min() const{ return m_min; } + double max() const{ return m_max; } + double mean() const{ + return m_sum / m_count; + } + double stddev() const{ + return std::sqrt((m_sqr - m_sum*m_sum / m_count) / (m_count - 1)); + } + double diff_metric(double reference) const{ + return std::sqrt((m_sqr + reference*(reference * m_count - 2 * m_sum)) / m_count); + } + +private: + uint64_t m_count = 0; + double m_sum = 0; + double m_sqr = 0; + double m_min = INFINITY; + double m_max = -INFINITY; +}; + + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/Options/FixedCode.cpp b/SerialPrograms/Source/CommonFramework/Options/FixedCode.cpp index f6dfdf6f60..6397157bf9 100644 --- a/SerialPrograms/Source/CommonFramework/Options/FixedCode.cpp +++ b/SerialPrograms/Source/CommonFramework/Options/FixedCode.cpp @@ -24,6 +24,9 @@ FixedCode::FixedCode( , m_current(std::move(default_value)) {} void FixedCode::load_json(const QJsonValue& json){ + if (!json.isString()){ + return; + } m_current = json.toString(); } QJsonValue FixedCode::to_json() const{ diff --git a/SerialPrograms/Source/CommonFramework/Options/RandomCode.cpp b/SerialPrograms/Source/CommonFramework/Options/RandomCode.cpp index 277ff4c425..04d1402167 100644 --- a/SerialPrograms/Source/CommonFramework/Options/RandomCode.cpp +++ b/SerialPrograms/Source/CommonFramework/Options/RandomCode.cpp @@ -60,9 +60,9 @@ RandomCode::RandomCode() ) {} void RandomCode::load_json(const QJsonValue& json){ - QJsonObject root = json_cast_object(json); - m_current.m_random_digits = json_get_int(root, "RandomDigits"); - m_current.m_code = json_get_string(root, "RaidCode"); + QJsonObject root = json.toObject(); + json_get_int(m_current.m_random_digits, root, "RandomDigits"); + json_get_string(m_current.m_code, root, "RaidCode"); } QJsonValue RandomCode::to_json() const{ QJsonObject root; diff --git a/SerialPrograms/Source/CommonFramework/Panels/SettingsPanel.cpp b/SerialPrograms/Source/CommonFramework/Panels/SettingsPanel.cpp index c1e6dcb717..ff7b972144 100644 --- a/SerialPrograms/Source/CommonFramework/Panels/SettingsPanel.cpp +++ b/SerialPrograms/Source/CommonFramework/Panels/SettingsPanel.cpp @@ -19,18 +19,11 @@ namespace PokemonAutomation{ void SettingsPanel::from_json(const QJsonValue& json){ - try{ - const QJsonObject& obj = json_get_object(json_cast_object(json), m_name); - for (auto& item : m_options){ - if (!item.first.isEmpty()){ - QJsonValue value = json_get_value(obj, item.first); - item.second->load_json(value); - } + QJsonObject obj = json_get_object_nothrow(json.toObject(), m_name); + for (auto& item : m_options){ + if (!item.first.isEmpty()){ + item.second->load_json(json_get_value_nothrow(obj, item.first)); } - }catch (const StringException& str){ - cout << str.message().toUtf8().data() << endl; - }catch (...){ - cout << "Failed to catch." << endl; } } QJsonValue SettingsPanel::to_json() const{ diff --git a/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp b/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp index 59a919239a..74992da003 100644 --- a/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp +++ b/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp @@ -35,7 +35,7 @@ void PersistentSettings::write() const{ } root.insert("03-NaughtyMode", QJsonValue(naughty_mode)); root.insert("04-DeveloperMode", QJsonValue(developer_mode)); - root.insert("05-LogEverything", QJsonValue(log_everything)); + root.insert("05-LogEverything", QJsonValue(log_everything.load(std::memory_order_acquire))); root.insert("10-SwitchKeyboardMapping", NintendoSwitch::read_keyboard_mapping()); @@ -59,10 +59,10 @@ void PersistentSettings::read(){ throw StringException("Invalid settings file."); } QJsonObject root = doc.object(); - config_path = json_get_string(root, "00-ConfigPath"); - source_path = json_get_string(root, "01-SourcePath"); + json_get_string(config_path, root, "00-ConfigPath"); + json_get_string(source_path, root, "01-SourcePath"); { - QJsonArray res = json_get_array(root, "02-WindowSize"); + QJsonArray res = json_get_array_nothrow(root, "02-WindowSize"); if (res.size() == 2){ window_size = QSize( res[0].toInt(window_size.width()), @@ -70,12 +70,12 @@ void PersistentSettings::read(){ ); } } - naughty_mode = json_get_bool(root, "03-NaughtyMode"); - developer_mode = json_get_bool(root, "04-DeveloperMode"); - log_everything = json_get_bool(root, "05-LogEverything"); - NintendoSwitch::set_keyboard_mapping(json_get_array(root, "10-SwitchKeyboardMapping")); - settings = json_get_object(root, "98-SharedSettings"); - programs = json_get_object(root, "99-ProgramSettings"); + json_get_bool(naughty_mode, root, "03-NaughtyMode"); + json_get_bool(developer_mode, root, "04-DeveloperMode"); + json_get_bool(log_everything, root, "05-LogEverything"); + NintendoSwitch::set_keyboard_mapping(json_get_array_nothrow(root, "10-SwitchKeyboardMapping")); + settings = json_get_object_nothrow(root, "98-SharedSettings"); + programs = json_get_object_nothrow(root, "99-ProgramSettings"); } diff --git a/SerialPrograms/Source/CommonFramework/PersistentSettings.h b/SerialPrograms/Source/CommonFramework/PersistentSettings.h index 49a259936f..410b24fa84 100644 --- a/SerialPrograms/Source/CommonFramework/PersistentSettings.h +++ b/SerialPrograms/Source/CommonFramework/PersistentSettings.h @@ -16,6 +16,10 @@ namespace PokemonAutomation{ class PersistentSettings{ public: + PersistentSettings() + : log_everything(false) + {} + void write() const; void read(); @@ -25,7 +29,7 @@ class PersistentSettings{ QSize window_size = QSize(960, 540); bool naughty_mode = false; bool developer_mode = false; - bool log_everything = false; + std::atomic log_everything; QJsonObject settings; QJsonObject programs; diff --git a/SerialPrograms/Source/CommonFramework/Tools/BotBaseHandle.cpp b/SerialPrograms/Source/CommonFramework/Tools/BotBaseHandle.cpp index 1a49b0595a..a6fb692556 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/BotBaseHandle.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/BotBaseHandle.cpp @@ -10,6 +10,7 @@ #include "ClientSource/Connection/SerialConnection.h" #include "ClientSource/Connection/PABotBase.h" #include "CommonFramework/Globals.h" +#include "CommonFramework/PersistentSettings.h" #include "BotBaseHandle.h" namespace PokemonAutomation{ @@ -198,6 +199,8 @@ void BotBaseHandle::thread_body(){ QString("Last Ack: ") + tostr_fixed(seconds.count(), 3).c_str() + " seconds ago" ); +// m_logger.log("Connection issue detected. Turning on all logging..."); +// settings.log_everything.store(true, std::memory_order_release); } std::unique_lock lg(m_cv_lock); diff --git a/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.cpp b/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.cpp index 9ff0a1546c..a1a067182c 100644 --- a/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.cpp +++ b/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.cpp @@ -33,22 +33,20 @@ CameraSelector::CameraSelector(QString label, const QJsonValue& json) } void CameraSelector::load_json(const QJsonValue& json){ - try{ - QJsonObject obj = json_cast_object(json); - QString name = json_get_string(obj, JSON_CAMERA); - for (const auto& item : QCameraInfo::availableCameras()){ - if (name == item.deviceName()){ - m_camera = item; - break; - } - } - QJsonArray res = json_get_array(obj, JSON_RESOLUTION); - if (res.size() != 2){ - throw StringException("Config Error - Resolution should be 2 elements: " + JSON_RESOLUTION); + QJsonObject obj = json.toObject(); + QString name; + if (!json_get_string(name, obj, JSON_CAMERA)){ + return; + } + for (const auto& item : QCameraInfo::availableCameras()){ + if (name == item.deviceName()){ + m_camera = item; + break; } - m_resolution = QSize(json_cast_int(res[0]), json_cast_int(res[1])); - }catch (const StringException& str){ - cout << str.message().toUtf8().data() << endl; + } + QJsonArray res = json_get_array_nothrow(obj, JSON_RESOLUTION); + if (res.size() == 2 && res[0].isDouble() && res[1].isDouble()){ + m_resolution = QSize(res[0].toInt(), res[1].toInt()); } } QJsonValue CameraSelector::to_json() const{ diff --git a/SerialPrograms/Source/CommonFramework/Widgets/SerialSelector.cpp b/SerialPrograms/Source/CommonFramework/Widgets/SerialSelector.cpp index ac6439946e..d422fcf4b6 100644 --- a/SerialPrograms/Source/CommonFramework/Widgets/SerialSelector.cpp +++ b/SerialPrograms/Source/CommonFramework/Widgets/SerialSelector.cpp @@ -35,13 +35,9 @@ SerialSelector::SerialSelector( load_json(json); } void SerialSelector::load_json(const QJsonValue& json){ - try{ - QString name = json.toString(); - if (name.size() > 0){ - m_port = QSerialPortInfo(name); - } - }catch (const StringException& str){ - cout << str.message().toUtf8().data() << endl; + QString name = json.toString(); + if (name.size() > 0){ + m_port = QSerialPortInfo(name); } } QJsonValue SerialSelector::to_json() const{ diff --git a/SerialPrograms/Source/CommonFramework/Windows/MainWindow.cpp b/SerialPrograms/Source/CommonFramework/Windows/MainWindow.cpp index 281a8a6c6b..c02a7695ee 100644 --- a/SerialPrograms/Source/CommonFramework/Windows/MainWindow.cpp +++ b/SerialPrograms/Source/CommonFramework/Windows/MainWindow.cpp @@ -72,7 +72,7 @@ MainWindow::MainWindow(QWidget* parent) program_box->setMaximumWidth(width); setting_box->setMaximumWidth(width); - QGroupBox* support_box = new QGroupBox("Support", centralwidget); + QGroupBox* support_box = new QGroupBox("Support (" + STRING_POKEMON + " Automation " + VERSION + ")", centralwidget); left_layout->addWidget(support_box); QVBoxLayout* support_layout = new QVBoxLayout(support_box); diff --git a/SerialPrograms/Source/CommonFramework/Windows/OutputWindow.cpp b/SerialPrograms/Source/CommonFramework/Windows/OutputWindow.cpp index 42098ce9f0..902e3f9dec 100644 --- a/SerialPrograms/Source/CommonFramework/Windows/OutputWindow.cpp +++ b/SerialPrograms/Source/CommonFramework/Windows/OutputWindow.cpp @@ -81,7 +81,7 @@ OutputWindow::OutputWindow(QWidget* parent) m_text->setReadOnly(true); m_text->setAcceptRichText(true); - m_text->document()->setMaximumBlockCount(100000); + m_text->document()->setMaximumBlockCount(1000); m_default_color = m_text->textColor(); m_log_file.setFileName(QCoreApplication::applicationFilePath() + ".log"); @@ -91,6 +91,14 @@ OutputWindow::OutputWindow(QWidget* parent) std::string bom = "\xef\xbb\xbf"; m_log_file.write(bom.c_str(), bom.size()); } + + log("================================================================================", ""); + log( + QString(PokemonAutomation::current_time().c_str()) + + " - [Application]: " + + "Application Startup...", + "" + ); } OutputWindow::~OutputWindow(){ m_log_file.close(); diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchSystem.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchSystem.cpp index 965c59c2ff..6df0cac07f 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchSystem.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchSystem.cpp @@ -47,39 +47,23 @@ MultiSwitchSystemFactory::MultiSwitchSystemFactory( } } void MultiSwitchSystemFactory::load_json(const QJsonValue& json){ - try{ - QJsonObject obj = json_cast_object(json); - QJsonArray array = json_get_array(obj, "DeviceList"); - if (array.size() == 0 || (size_t)array.size() > MAX_SWITCHES){ - throw StringException("Invalid switch count."); - } - m_switches.clear(); - for (size_t c = 0; c < (size_t)array.size(); c++){ - m_switches.emplace_back( - new SwitchSystemFactory( - QString("Switch ") + QString::number(c), - QString("Switch ") + QString::number(c), - m_min_pabotbase, m_feedback, - array[c] - ) - ); - } - m_active_switches = json_get_int(obj, "ActiveDevices"); - m_active_switches = std::max(m_active_switches, m_min_switches); - m_active_switches = std::min(m_active_switches, m_max_switches); - }catch (const StringException&){ -#if 0 + QJsonObject obj = json.toObject(); + QJsonArray array = json_get_array_nothrow(obj, "DeviceList"); + if (array.size() == 0 || (size_t)array.size() > MAX_SWITCHES){ + return; + } + m_switches.clear(); + for (size_t c = 0; c < (size_t)array.size(); c++){ m_switches.emplace_back( new SwitchSystemFactory( - "Switch 0", - "Switch 0", - m_min_pabotbase, - m_lock_camera_when_running, - m_lock_resolution_when_running + QString("Switch ") + QString::number(c), + QString("Switch ") + QString::number(c), + m_min_pabotbase, m_feedback, + array[c] ) ); -#endif } + json_get_int(m_active_switches, obj, "ActiveDevices", m_min_switches, m_max_switches); } QJsonValue MultiSwitchSystemFactory::to_json() const{ QJsonObject obj; diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp index 80066e8ff3..6f68974a91 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp @@ -48,19 +48,12 @@ RunnableProgram::RunnableProgram( , m_setup(nullptr) {} void RunnableProgram::from_json(const QJsonValue& json){ - try{ - const QJsonObject& obj = json_get_object(json_cast_object(json), m_name); - m_setup->load_json(json_get_value(obj, "SwitchSetup")); - for (auto& item : m_options){ - if (!item.second.isEmpty()){ - QJsonValue value = json_get_value(obj, item.second); - item.first->load_json(value); - } + const QJsonObject& obj = json_get_object_nothrow(json.toObject(), m_name); + m_setup->load_json(json_get_value_nothrow(obj, "SwitchSetup")); + for (auto& item : m_options){ + if (!item.second.isEmpty()){ + item.first->load_json(json_get_value_nothrow(obj, item.second)); } - }catch (const StringException& str){ - cout << str.message().toUtf8().data() << endl; - }catch (...){ - cout << "Failed to catch." << endl; } } QJsonValue RunnableProgram::to_json() const{ @@ -96,6 +89,7 @@ QWidget* RunnableProgram::make_ui(MainWindow& window){ RunnableProgramUI::RunnableProgramUI(RunnableProgram& factory, MainWindow& window) : RightPanelUI(factory) + , m_name(factory.name()) , m_window(window) , m_logger(window.output_window(), "Program") , m_setup(nullptr) @@ -183,7 +177,12 @@ void RunnableProgramUI::make_body(QWidget& parent, QVBoxLayout& layout){ m_status_bar->setVisible(false); m_status_bar->setAlignment(Qt::AlignCenter); layout.addWidget(m_status_bar); - m_status_bar->setText("Encounters: 1,267 - Corrections: 0 - Star Shinies: 1 - Square Shinies: 0"); +// m_status_bar->setText("Encounters: 1,267 - Corrections: 0 - Star Shinies: 1 - Square Shinies: 0"); + QFont font = m_status_bar->font(); +// cout << font.pointSize() << endl; + int font_size = font.pointSize(); + font.setPointSize(font_size + font_size / 2); + m_status_bar->setFont(font); QGroupBox* actions_widget = new QGroupBox("Actions", &parent); layout.addWidget(actions_widget); @@ -335,7 +334,7 @@ void RunnableProgramUI::run_program(){ } try{ - m_logger.log("Starting Program..."); + m_logger.log("Starting Program: " + m_name + ""); program(); m_setup->wait_for_all_requests(); m_logger.log("Ending Program..."); diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h index 95fb4b9666..bd0c4f088f 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h +++ b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h @@ -86,6 +86,7 @@ class RunnableProgramUI : public RightPanelUI{ static BotBase& sanitize_botbase(BotBase* botbase); protected: + const QString& m_name; MainWindow& m_window; TaggedLogger m_logger; diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/SwitchSystem.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/SwitchSystem.cpp index 383de0ee14..aaf6f85e3f 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/SwitchSystem.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/SwitchSystem.cpp @@ -53,13 +53,9 @@ SwitchSystemFactory::SwitchSystemFactory( load_json(json); } void SwitchSystemFactory::load_json(const QJsonValue& json){ - try{ - QJsonObject obj = json_cast_object(json); - m_serial.load_json(json_get_value(obj, JSON_SERIAL)); - m_camera.load_json(json_get_value(obj, JSON_CAMERA)); - }catch (const StringException& str){ - cout << str.message().toUtf8().data() << endl; - } + QJsonObject obj = json.toObject(); + m_serial.load_json(json_get_value_nothrow(obj, JSON_SERIAL)); + m_camera.load_json(json_get_value_nothrow(obj, JSON_CAMERA)); } QJsonValue SwitchSystemFactory::to_json() const{ QJsonObject root; diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/VirtualSwitchControllerMapping.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/VirtualSwitchControllerMapping.cpp index 865dbc2ae4..4b7cc14bf5 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/VirtualSwitchControllerMapping.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/VirtualSwitchControllerMapping.cpp @@ -288,10 +288,20 @@ void set_keyboard_mapping(const QJsonArray& json){ std::vector> mapping; for (const auto& item : json){ - QJsonObject pair = json_cast_object(item); - Qt::Key key = (Qt::Key)json_get_int(pair, "Qt::Key"); - const ControllerButton* button = string_to_controller_button(json_get_string(pair, "Button")); - mapping.emplace_back(key, *button); + QJsonObject pair = item.toObject(); + int key; + if (!json_get_int(key, pair, "Qt::Key")){ + continue; + } + QString button_name; + if (!json_get_string(button_name, pair, "Button")){ + continue; + } + const ControllerButton* button = string_to_controller_button(button_name); + if (button == nullptr){ + continue; + } + mapping.emplace_back((Qt::Key)key, *button); } set_keyboard_mapping(std::move(mapping)); diff --git a/SerialPrograms/Source/NintendoSwitch/FrameworkSettingsPanel.cpp b/SerialPrograms/Source/NintendoSwitch/FrameworkSettingsPanel.cpp index 6216f53a8c..83c7198ae4 100644 --- a/SerialPrograms/Source/NintendoSwitch/FrameworkSettingsPanel.cpp +++ b/SerialPrograms/Source/NintendoSwitch/FrameworkSettingsPanel.cpp @@ -9,6 +9,10 @@ #include "NintendoSwitch/Options/TimeExpression.h" #include "FrameworkSettingsPanel.h" +#include +using std::cout; +using std::endl; + namespace PokemonAutomation{ namespace NintendoSwitch{ diff --git a/SerialPrograms/Source/NintendoSwitch/Options/FriendCodeList.cpp b/SerialPrograms/Source/NintendoSwitch/Options/FriendCodeList.cpp index fff6d6c40f..fe32817e5f 100644 --- a/SerialPrograms/Source/NintendoSwitch/Options/FriendCodeList.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Options/FriendCodeList.cpp @@ -39,9 +39,12 @@ FriendCodeList::FriendCodeList() void FriendCodeList::load_json(const QJsonValue& json){ m_lines.clear(); - QJsonArray list = json_cast_array(json); + QJsonArray list = json.toArray(); for (const auto& line : list){ - m_lines.emplace_back(json_cast_string(line)); + QString str = line.toString(); + if (str.size() > 0){ + m_lines.emplace_back(str); + } } } QJsonValue FriendCodeList::to_json() const{ diff --git a/SerialPrograms/Source/PanelList.cpp b/SerialPrograms/Source/PanelList.cpp index 4f580319e6..951f7b0f52 100644 --- a/SerialPrograms/Source/PanelList.cpp +++ b/SerialPrograms/Source/PanelList.cpp @@ -36,6 +36,18 @@ #include "PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-StowOnSideFarmer.h" #include "PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-DailyHighlightFarmer.h" +#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_BeamReset.h" +#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.h" +#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_EventBeamFinder.h" +#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN.h" +#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperEU.h" +#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperUS.h" +#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN-7.8k.h" + +#include "PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.h" +#include "PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.h" +#include "PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.h" + #include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_MultiGameFossil.h" #include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.h" #include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.h" @@ -49,19 +61,9 @@ #include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h" #include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h" #include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h" +#include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h" #include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h" - -#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_BeamReset.h" -#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.h" -#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_EventBeamFinder.h" -#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN.h" -#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperEU.h" -#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperUS.h" -#include "PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN-7.8k.h" - -#include "PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.h" -#include "PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.h" -#include "PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.h" +#include "PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h" #include "PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggFetcher2.h" #include "PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggHatcher.h" @@ -139,6 +141,18 @@ const std::vector>& PROGRAM_LIST(){ list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); @@ -152,19 +166,11 @@ const std::vector>& PROGRAM_LIST(){ list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); - list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); +// if (settings.developer_mode){ + list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); +// } list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp index ec0cc87c10..5b0730aece 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp @@ -6,43 +6,13 @@ #include "CommonFramework/Inference/ImageTools.h" #include "CommonFramework/Inference/InferenceThrottler.h" -#include "CommonFramework/Inference/FillMatrix.h" +#include "PokemonSwSh_MarkFinder.h" #include "PokemonSwSh_FishingDetector.h" namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonSwSh{ -struct PinkFilter{ - size_t count = 0; - - void operator()(FillMatrix::ObjectID& cell, const QImage& image, size_t x, size_t y){ - QRgb pixel = image.pixel(x, y); - int red = qRed(pixel); - int green = qGreen(pixel); -// int blue = qBlue(pixel); - int set = red > 128 && green < 128; - cell = set; - count += set; - } -}; -struct PinkFilter2{ - size_t count = 0; - - void operator()(FillMatrix::ObjectID& cell, QImage& image, size_t x, size_t y){ - QRgb pixel = image.pixel(x, y); - int red = qRed(pixel); - int green = qGreen(pixel); -// int blue = qBlue(pixel); - int set = red > 128 && green < 128; - if (!set){ - image.setPixel(x, y, 0); - } - cell = set; - count += set; - } -}; - FishingDetector::FishingDetector( VideoFeed& feed @@ -68,89 +38,12 @@ FishingDetector::Detection FishingDetector::detect_now(){ } } -// return Detection::NO_DETECTION; - - - FillMatrix matrix(hook_image); - PinkFilter filter; - matrix.apply_filter(hook_image, filter); - - std::vector objects = find_all_objects(matrix, false, 10); - std::multimap candidate_top; - std::multimap candidate_bot; - for (const FillGeometry& object : objects){ - ImageStats stats = object_stats(hook_image, matrix, object); - double aspect_ratio = (double)object.box.width() / object.box.height(); - FloatPixel color_ratio = stats.average / stats.average.sum(); - double stddev = stats.stddev.sum(); - -#if 0 - cout << object.area << " : [" << object.center_x << "," << object.center_y - << "][" << object.box.width() << " x " << object.box.height() - << "], mean = " << stats.average / stats.average.sum() - << ", stddev = " << stats.stddev << endl; -#endif - - if (0.4 < aspect_ratio && aspect_ratio < 0.6 && - stddev < 100 && - euclidean_distance(color_ratio, FloatPixel(0.56, 0.14, 0.30)) < 0.2 - ){ -// cout << "top" << endl; - candidate_top.emplace(object.area, object); - } - if (1.0 < aspect_ratio && aspect_ratio < 1.5 && - stddev < 100 && - euclidean_distance(color_ratio, FloatPixel(0.56, 0.21, 0.23)) < 0.2 - ){ -// cout << "bottom" << endl; - candidate_bot.emplace(object.area, object); - } - } -// if (!candidate_top.empty()){ -// hook_image.save("test.png"); -// } - - for (auto iter = candidate_top.rbegin(); iter != candidate_top.rend(); ++iter){ - const FillGeometry& top = iter->second; - size_t top_area = top.area; - size_t area_low = top_area / 7.; - size_t area_high = top_area / 5.; -// cout << "area = " << top_area << ", low = " << area_low << ", high = " << area_high << endl; - auto iter0 = candidate_bot.lower_bound(area_low); - auto iter1 = candidate_bot.upper_bound(area_high); - for (; iter0 != iter1; ++iter0){ - const FillGeometry& bottom = iter0->second; - -// cout << "top_area = " << top_area << ", bot_area = " << bottom.area << endl; - - // Verify that top is above bottom. - if (top.box.max_y >= bottom.box.min_y){ - continue; - } -// cout << "check 1" << endl; - - // Verify bottom is left of the top. - if (top.center_x <= bottom.center_x){ - continue; - } - - // Make sure horizontal alignment is reasonable. - int mid = (top.box.min_x + top.box.max_x) / 2; - if (std::abs(bottom.box.max_x - mid) * 3 > top.box.width()){ - continue; - } - - // Make sure vertical alignment is reasonable. - if (top.box.max_y + top.box.height() <= bottom.box.min_y){ - continue; - } - -// cout << "match!" << endl; - return Detection::HOOKED; - } - } + std::vector exclamation_marks; + find_marks(hook_image, &exclamation_marks, nullptr); - return Detection::NO_DETECTION; + return exclamation_marks.empty() + ? Detection::NO_DETECTION + : Detection::HOOKED; } FishingDetector::Detection FishingDetector::wait_for_detection( ProgramEnvironment& env, Logger& logger, diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.cpp new file mode 100644 index 0000000000..f6a248799d --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.cpp @@ -0,0 +1,336 @@ +/* Mark Finder + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/FillGeometry.h" +#include "PokemonSwSh_MarkFinder.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + + +void WhiteFilter::operator()(FillMatrix::ObjectID& cell, const QImage& image, size_t x, size_t y){ + QRgb pixel = image.pixel(x, y); + int set = (pixel & 0x00808080) == 0x00808080 ? 1 : 0; +// if (!set){ +// image.setPixel(x, y, 0); +// } + cell = set; + count += set; +} + + +struct WhiteFilter2{ + size_t count = 0; + + void operator()(FillMatrix::ObjectID& cell, QImage& image, size_t x, size_t y){ + QRgb pixel = image.pixel(x, y); + int set = (pixel & 0x00808080) == 0x00808080 ? 1 : 0; + if (!set){ + image.setPixel(x, y, 0); + } + cell = set; + count += set; + } +}; + + + + +bool is_exclamation_mark( + const FillGeometry& top, + const FillGeometry& bot +){ + + // Make sure dimensions are reasonable. + double top_aspect_ratio = (double)top.box.width() / top.box.height(); + if (0.4 > top_aspect_ratio || top_aspect_ratio > 0.6){ + return false; + } + double bot_aspect_ratio = (double)bot.box.width() / bot.box.height(); + if (0.5 > bot_aspect_ratio || bot_aspect_ratio > 2.0){ + return false; + } + +// cout << "=========" << endl; +// cout << top.box.width() << " x " << top.box.height() << endl; +// cout << bot.box.width() << " x " << bot.box.height() << endl; +// cout << "check 1" << endl; + + +// if (top.area > 8 * bot.area){ +// cout << "Ratio too large: " << top.area << " / " << bot.area << endl; +// return false; +// } + if (top.area < 4 * bot.area){ +// cout << "Ratio too small" << endl; + return false; + } + + // Verify that top is above bottom. + if (top.box.max_y >= bot.box.min_y){ + return false; + } +// cout << "check 1" << endl; + + // Verify bottom is left of the top. + if (top.center_x < bot.center_x){ + return false; + } + + // Make sure horizontal alignment is reasonable. + int mid = (top.box.min_x + top.box.max_x) / 2; + if (std::abs(bot.box.max_x - mid) * 3 > top.box.width()){ +// cout << "Bad horizontal alignment." << endl; + return false; + } + + // Make sure vertical alignment is reasonable. + if (top.box.max_y + top.box.height() <= bot.box.min_y){ + return false; + } + + return true; +} + +bool is_question_mark( + const FillGeometry& top, + const FillGeometry& bot +){ + // Make sure dimensions are reasonable. + double top_aspect_ratio = (double)top.box.width() / top.box.height(); + if (0.8 > top_aspect_ratio || top_aspect_ratio > 1.2){ + return false; + } + + + if (bot.area * 2 < (size_t)bot.box.width() * bot.box.height()){ + return false; + } + + // Verify that the top part of the question is between 5-10x the area of the bottom. + if (top.area > 10 * bot.area){ + return false; + } + if (top.area < 5 * bot.area){ + return false; + } + + // Verify that top is completely above the bottom. + if (top.box.max_y >= bot.box.min_y){ + return false; + } + + // Verify that bottom isn't too far below the top. + if (top.box.max_y + bot.box.height() < bot.box.min_y){ + return false; + } + + // Verify that bottom is completely within the x-axis of the top. + if (bot.box.min_x < top.box.min_x){ + return false; + } + if (bot.box.max_x > top.box.max_x){ + return false; + } + +// cout << top.box.width() << " x " << top.box.height() << " : " << top.area << " / " << top.box.width() * top.box.height() << endl; + + return true; +} + +size_t find_exclamation_marks( + const QImage& image, const FillMatrix& matrix, + const FillGeometry& object, + std::vector& marks +){ + FillMatrix search_matrix(image); + for (int r = 0; r < image.height(); r++){ + for (int c = 0; c < image.width(); c++){ + if (matrix[r][c] != 1){ + search_matrix[r][c] = 0; + continue; + } + QRgb pixel = image.pixel(c, r); + int red = qRed(pixel); + int green = qGreen(pixel); + search_matrix[r][c] = red >= 96 && green <= 160 ? 1 : 0; + } + } + + std::vector objects = find_all_objects(search_matrix, 1, false, 4); + if (objects.size() > 5){ + return 0; + } + + size_t count = 0; + for (const FillGeometry& top : objects){ +// cout << top.box.width() << " x " << top.box.height() << endl; + for (const FillGeometry& bot : objects){ + if (!is_exclamation_mark(top, bot)){ + continue; + } + PixelBox box{ + top.box.min_x + object.box.min_x, + top.box.min_y + object.box.min_y, + top.box.max_x + object.box.min_x, + bot.box.max_y + object.box.min_y + }; + marks.emplace_back(box); + count++; + } + } + + return count; +} +size_t find_question_marks( + const QImage& image, const FillMatrix& matrix, + const FillGeometry& object, + std::vector& marks +){ + FillMatrix search_matrix(image); + for (int r = 0; r < image.height(); r++){ + for (int c = 0; c < image.width(); c++){ + if (matrix[r][c] != 1){ + search_matrix[r][c] = 0; + continue; + } + QRgb pixel = image.pixel(c, r); + int blue = qBlue(pixel); + search_matrix[r][c] = blue >= 128 ? 1 : 0; + } + } + + std::vector objects = find_all_objects(search_matrix, 1, false, 4); + if (objects.size() > 5){ + return 0; + } + + size_t count = 0; + for (const FillGeometry& top : objects){ +// cout << top.box.width() << " x " << top.box.height() << endl; + for (const FillGeometry& bot : objects){ + if (!is_question_mark(top, bot)){ + continue; + } + PixelBox box{ + top.box.min_x + object.box.min_x, + top.box.min_y + object.box.min_y, + top.box.max_x + object.box.min_x, + bot.box.max_y + object.box.min_y + }; + marks.emplace_back(box); + count++; + } + } + + return count; +} + +size_t find_marks( + const QImage& image, const FillMatrix& matrix, + const FillGeometry& object, + std::vector* exclamation_marks, + std::vector* question_marks +){ + int width = object.box.width(); + int height = object.box.height(); +// if (object.area > 10000){ +// cout << "too big: " << object.area << endl; +// return 0; +// } + + // Find the holes. + + FillMatrix edge_matrix = matrix.extract(object.box, object.id); + // Invert the cells. + for (int r = 0; r < height; r++){ + for (int c = 0; c < width; c++){ + FillMatrix::ObjectID cell = edge_matrix[r][c]; + cell = cell ? 0 : 1; + edge_matrix[r][c] = cell; + } + } + { + // Fill in the edges. + size_t edge_area = 0; + for (int c = 0; c < width; c++){ + FillGeometry body; + if (fill_geometry(body, edge_matrix, 1, c, 0, false, 2)){ + edge_area += body.area; + } + if (fill_geometry(body, edge_matrix, 1, c, height - 1, false, 2)){ + edge_area += body.area; + } + } + for (int r = 0; r < height; r++){ + FillGeometry body; + if (fill_geometry(body, edge_matrix, 1, 0, r, false, 2)){ + edge_area += body.area; + } + if (fill_geometry(body, edge_matrix, 1, width - 1, r, false, 2)){ + edge_area += body.area; + } + } + } + +// QImage copy = image; + QImage subimage = extract_box(image, object.box); + + size_t count = 0; + if (exclamation_marks){ + count += find_exclamation_marks(subimage, edge_matrix, object, *exclamation_marks); + } + if (question_marks){ + count += find_question_marks(subimage, edge_matrix, object, *question_marks); + } + + static size_t id = 0; + if (count != 0){ +// subimage.save("mark-" + QString::number(id++) + ".png"); + } + return count; +} + + +size_t find_marks( + const QImage& image, + std::vector* exclamation_marks, + std::vector* question_marks +){ +#if 1 + FillMatrix matrix(image); + WhiteFilter filter; + matrix.apply_filter(image, filter); +#else + QImage copy = image; + + FillMatrix matrix(image); + WhiteFilter2 filter; + matrix.apply_filter(copy, filter); + copy.save("white_filter.png"); +#endif + + size_t count = 0; + std::vector white_boxes = find_all_objects(matrix, true, 100); + for (const FillGeometry& white_box : white_boxes){ + count += find_marks(image, matrix, white_box, exclamation_marks, question_marks); + } + + return count; +} + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h new file mode 100644 index 0000000000..97561511f1 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h @@ -0,0 +1,40 @@ +/* Mark Finder + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_MarkFinder_H +#define PokemonAutomation_PokemonSwSh_MarkFinder_H + +#include +#include +#include "CommonFramework/Inference/InferenceTypes.h" +#include "CommonFramework/Inference/FillMatrix.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + + +struct WhiteFilter{ + size_t count = 0; + + void operator()(FillMatrix::ObjectID& cell, const QImage& image, size_t x, size_t y); +}; + + + +size_t find_marks( + const QImage& image, + std::vector* exclamation_marks, + std::vector* question_marks +); + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp index d6609db1e4..d6db307d32 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp @@ -5,13 +5,39 @@ */ #include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/InferenceThrottler.h" #include "PokemonSwSh_StartBattleDetector.h" +#include +using std::cout; +using std::endl; + namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonSwSh{ +bool is_dialog_grey(const ImageStats& stats){ +// cout << "stddev = " << stats.stddev.sum() << endl; +// cout << "average = " << stats.average << endl; + + if (stats.average.sum() > 200 || stats.stddev.sum() > 5){ + return false; + } + + FloatPixel color_ratio = stats.average / stats.average.sum(); + double distance = euclidean_distance( + color_ratio, + FloatPixel(0.337067, 0.325728, 0.337204) + ); + + return distance < 0.1; +} +bool is_dialog_grey(const QImage& image){ + return is_dialog_grey(pixel_stats(image)); +} + + StartBattleDetector::StartBattleDetector( VideoFeed& feed, @@ -19,32 +45,55 @@ StartBattleDetector::StartBattleDetector( ) : m_feed(feed) , m_timeout(timeout) - , m_box(feed, 0.65, 0.89, 0.25, 0.07) + , m_screen_box(feed, 0.2, 0.2, 0.6, 0.6) + , m_dialog_box(feed, 0.50, 0.89, 0.40, 0.07) , m_start_time(std::chrono::system_clock::now()) {} bool StartBattleDetector::has_timed_out() const{ return std::chrono::system_clock::now() - m_start_time > m_timeout; } -bool StartBattleDetector::detect(){ - QImage image = extract_box(m_feed.snapshot(), m_box); - ImageStats stats = pixel_stats(image); +bool StartBattleDetector::detect(const QImage& screen){ + QImage image0 = extract_box(screen, m_screen_box); + QImage image1 = extract_box(screen, m_dialog_box); + + ImageStats stats0 = pixel_stats(image0); + ImageStats stats1 = pixel_stats(image1); // cout << "mean = " << stats.average << ", stddev = " << stats.stddev << endl; // return stats.stddev.sum() < 10; // White screen. - if (stats.average.sum() > 600 && stats.stddev.sum() < 10){ - return true; - } - - // Grey text box. - if (stats.average.sum() < 200 && stats.stddev.sum() < 10){ + if ( + stats0.average.sum() > 600 && stats0.stddev.sum() < 10 && + stats1.average.sum() > 600 && stats1.stddev.sum() < 10 + ){ return true; } - return false; + // Grey text box. + bool dialog = stats0.stddev.sum() > 50 && is_dialog_grey(stats1); + if (dialog){ +// cout << stats0.stddev.sum() << endl; + } + return dialog; } +bool StartBattleDetector::wait(ProgramEnvironment& env, Logger& logger){ + InferenceThrottler throttler(m_timeout, std::chrono::milliseconds(50)); + while (true){ + env.check_stopping(); + QImage screen = m_feed.snapshot(); + if (detect(screen)){ + logger.log("StartBattleDetector: Detected start of battle!", "purple"); + return true; + } + + if (throttler.end_iteration(env)){ + logger.log("StartBattleDetector: Timed out.", "red"); + return false; + } + } +} } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h index f3b2412705..aee0fa2d36 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h @@ -8,13 +8,19 @@ #define PokemonAutomation_PokemonSwSh_StartBattleDetector_H #include +#include "CommonFramework/Tools/Logger.h" #include "CommonFramework/Tools/VideoFeed.h" +#include "CommonFramework/Tools/ProgramEnvironment.h" namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonSwSh{ + +bool is_dialog_grey(const QImage& image); + + class StartBattleDetector{ public: StartBattleDetector( @@ -23,12 +29,14 @@ class StartBattleDetector{ ); bool has_timed_out() const; - bool detect(); + bool detect(const QImage& screen); + bool wait(ProgramEnvironment& env, Logger& logger); private: VideoFeed& m_feed; std::chrono::milliseconds m_timeout; - InferenceBoxScope m_box; + InferenceBoxScope m_screen_box; + InferenceBoxScope m_dialog_box; std::chrono::time_point m_start_time; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp index 9061a9b29c..402c8f01e6 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp @@ -6,13 +6,14 @@ #include #include "CommonFramework/Inference/ImageTools.h" -#include "CommonFramework/Inference/FloatStatAccumulator.h" +#include "CommonFramework/Inference/StatAccumulator.h" #include "CommonFramework/Inference/TimeWindowStatTracker.h" #include "CommonFramework/Inference/InferenceThrottler.h" +#include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h" #include "PokemonSwSh_ShinyTrigger.h" #include "PokemonSwSh_ShinyEncounterDetector.h" -#include "ClientSource/Libraries/Logging.h" #include using std::cout; using std::endl; @@ -21,124 +22,292 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonSwSh{ -const InferenceBox ShinyEncounterDetector::REGULAR_BATTLE(0.5, 0.05, 0.5, 0.70); -const InferenceBox ShinyEncounterDetector::RAID_BATTLE(0.3, 0.01, 0.7, 0.75); + +const ShinyDetectionBattle SHINY_BATTLE_REGULAR {{0.5, 0.05, 0.5, 0.70}, std::chrono::milliseconds(2300)}; +const ShinyDetectionBattle SHINY_BATTLE_RAID {{0.3, 0.01, 0.7, 0.75}, std::chrono::milliseconds(3900)}; + + + +class ShinyEncounterDetector{ +public: + enum class DialogTimerState{ + NOT_STARTED, + LISTENING, + FINISHED, + }; + +public: + ShinyEncounterDetector( + VideoFeed& feed, Logger& logger, + const ShinyDetectionBattle& battle_settings, + double detection_threshold + ); + + ShinyDetection results() const; + + void push( + const QImage& screen, + std::chrono::system_clock::time_point timestamp = std::chrono::system_clock::now() + ); + + +private: + VideoFeed& m_feed; + Logger& m_logger; + + InferenceBoxScope m_dialog_box; + InferenceBoxScope m_shiny_box; + + std::chrono::milliseconds m_min_delay; + std::chrono::milliseconds m_max_delay; + + double m_detection_threshold; + StandardBattleMenuDetector m_menu; + + bool m_dialog_on = false; + DialogTimerState m_dialog_state = DialogTimerState::NOT_STARTED; + + std::chrono::system_clock::time_point m_last_dialog_off; + ShinyImageAlpha m_dialog_alpha; + + std::deque m_detection_overlays; + + bool m_dialog_trigger = false; + ShinyImageAlpha m_image_alpha; + +}; + + ShinyEncounterDetector::ShinyEncounterDetector( VideoFeed& feed, Logger& logger, - const InferenceBox& box, - std::chrono::seconds timeout, + const ShinyDetectionBattle& battle_settings, double detection_threshold ) : m_feed(feed) , m_logger(logger) - , m_box(feed, box) - , m_timeout(timeout) + , m_dialog_box(feed, 0.50, 0.89, 0.40, 0.07) + , m_shiny_box(feed, battle_settings.detection_box) + , m_min_delay(battle_settings.dialog_delay_when_shiny - std::chrono::milliseconds(500)) + , m_max_delay(battle_settings.dialog_delay_when_shiny + std::chrono::milliseconds(500)) , m_detection_threshold(detection_threshold) , m_menu(feed) + , m_last_dialog_off(std::chrono::system_clock::now()) {} - -ShinyEncounterDetector::Detection ShinyEncounterDetector::detect(ProgramEnvironment& env){ - TimeWindowStatTracker tracker(std::chrono::milliseconds(4000)); - - std::deque detection_overlays; - - bool shiny = false; - double star_alpha = 0; - double square_alpha = 0; - - InferenceThrottler throttler(m_timeout, std::chrono::milliseconds(50)); - for (uint64_t count = 0;; count++){ - env.check_stopping(); - - QImage image = m_feed.snapshot(); - auto timestamp = std::chrono::system_clock::now(); - if (m_menu.detect(image)){ - m_logger.log("ShinyDetector: Battle menu found!", "purple"); +void ShinyEncounterDetector::push( + const QImage& screen, + std::chrono::system_clock::time_point timestamp +){ + // Dialog timing detector. + bool dialog_on = is_dialog_grey(extract_box(screen, m_dialog_box)); + do{ + if (m_dialog_on){ + if (dialog_on){ + // on -> on : Zero the alpha. + m_dialog_alpha = ShinyImageAlpha(); + }else{ + // on -> off : Reset the timer. + if (m_dialog_state != DialogTimerState::FINISHED){ + m_dialog_state = DialogTimerState::LISTENING; + } + m_last_dialog_off = timestamp; + m_dialog_on = false; + m_logger.log("ShinyDetector: Dialog on -> off.", "purple"); + } break; } - QImage inference_image = extract_box(image, m_box); + if (!dialog_on){ + break; + } + if (m_dialog_state == DialogTimerState::NOT_STARTED){ + m_logger.log("ShinyDetector: Dialog off -> on. Starting timer.", "purple"); + break; + } - ShinyImageDetection signatures; - signatures.accumulate(inference_image, count, &m_logger); + // off -> on : Check if we sandwiched a shiny sparkle with the appropriate time delay. + auto time_off = timestamp - m_last_dialog_off; + bool shiny_delay = m_min_delay < time_off && time_off < m_max_delay; + bool sandwiched_shiny = m_dialog_alpha.shiny >= 0.5; + bool dialog_trigger = shiny_delay && sandwiched_shiny; - star_alpha += 3 * signatures.stars.size(); - if (signatures.squares.size() > 1){ - square_alpha += signatures.squares.size() - 1; - } - square_alpha += 5 * signatures.lines.size(); - - double signature_alpha = signatures.alpha(); - tracker.push(signature_alpha, timestamp); - FloatStatAccumulator previous_2_seconds = tracker.accumulate_start_to_point(std::chrono::milliseconds(2000)); - FloatStatAccumulator last_2_seconds = tracker.accumulate_last(std::chrono::milliseconds(2000)); - double mean0 = previous_2_seconds.mean(); - double alpha = last_2_seconds.diff_metric(mean0); - - if (signature_alpha > 1.0){ - if (alpha > m_detection_threshold){ - shiny = true; - m_logger.log( - "ShinyDetector: alpha = " + QString::number(alpha) + " (threshold exceeded)", - "blue" - ); + m_logger.log( + "ShinyDetector: Dialog Delay = " + + QString::number(std::chrono::duration_cast(time_off).count() / 1000.) + " seconds", + "purple" + ); + if (dialog_trigger){ + if (m_dialog_state == DialogTimerState::LISTENING){ + m_logger.log("ShinyDetector: Dialog delay is consistent with a shiny!", "blue"); + m_dialog_trigger = true; }else{ - m_logger.log( - "ShinyDetector: alpha = " + QString::number(alpha), - "blue" - ); + m_logger.log("ShinyDetector: Dialog delay is consistent with a shiny, but it's the wrong dialog.", "blue"); } } - detection_overlays.clear(); - for (const auto& item : signatures.balls){ - InferenceBox box = translate_to_parent(image, m_box, item); - box.color = Qt::green; - detection_overlays.emplace_back(m_feed, box); - } - for (const auto& item : signatures.stars){ - InferenceBox box = translate_to_parent(image, m_box, item); - box.color = Qt::green; - detection_overlays.emplace_back(m_feed, box); - } - for (const auto& item : signatures.squares){ - InferenceBox box = translate_to_parent(image, m_box, item); - box.color = Qt::green; - detection_overlays.emplace_back(m_feed, box); + m_dialog_state = DialogTimerState::FINISHED; + }while (false); + m_dialog_on = dialog_on; + + + + + QImage shiny_box = extract_box(screen, m_shiny_box); + + ShinyImageDetection signatures; + signatures.accumulate(shiny_box, timestamp.time_since_epoch().count(), &m_logger); + + ShinyImageAlpha frame_alpha = signatures.alpha(); + m_image_alpha.max(frame_alpha); + + if (!m_dialog_on){ + m_dialog_alpha.max(frame_alpha); + } + + + if (frame_alpha.shiny > 0){ + if (frame_alpha.shiny > m_detection_threshold){ + m_logger.log( + "ShinyDetector: alpha = " + QString::number(frame_alpha.shiny) + " / " + QString::number(m_image_alpha.shiny) + " (threshold exceeded)", + "blue" + ); + }else{ + m_logger.log( + "ShinyDetector: alpha = " + QString::number(frame_alpha.shiny) + " / " + QString::number(m_image_alpha.shiny), + "blue" + ); } - for (const auto& item : signatures.lines){ - InferenceBox box = translate_to_parent(image, m_box, item); - box.color = Qt::green; - detection_overlays.emplace_back(m_feed, box); + } + + m_detection_overlays.clear(); + for (const auto& item : signatures.balls){ + InferenceBox box = translate_to_parent(screen, m_shiny_box, item); + box.color = Qt::green; + m_detection_overlays.emplace_back(m_feed, box); + } + for (const auto& item : signatures.stars){ + InferenceBox box = translate_to_parent(screen, m_shiny_box, item); + box.color = Qt::green; + m_detection_overlays.emplace_back(m_feed, box); + } + for (const auto& item : signatures.squares){ + InferenceBox box = translate_to_parent(screen, m_shiny_box, item); + box.color = Qt::green; + m_detection_overlays.emplace_back(m_feed, box); + } + for (const auto& item : signatures.lines){ + InferenceBox box = translate_to_parent(screen, m_shiny_box, item); + box.color = Qt::green; + m_detection_overlays.emplace_back(m_feed, box); + } +} + +ShinyDetection ShinyEncounterDetector::results() const{ + double alpha = m_image_alpha.shiny; + if (m_dialog_trigger){ + alpha += 1.0; + } + m_logger.log( + "ShinyDetector: Overall Alpha = " + QString::number(alpha) + + ", Star Alpha = " + QString::number(m_image_alpha.star) + + ", Square Alpha = " + QString::number(m_image_alpha.square), + "purple" + ); + + if (alpha < m_detection_threshold){ + m_logger.log("ShinyDetector: Not Shiny.", "purple"); + return ShinyDetection::NOT_SHINY; + } + if (m_image_alpha.star > 0 && m_image_alpha.star > m_image_alpha.square * 2){ + m_logger.log("ShinyDetector: Detected Star Shiny!", "blue"); + return ShinyDetection::STAR_SHINY; + } + if (m_image_alpha.square > 0 && m_image_alpha.square > m_image_alpha.star * 2){ + m_logger.log("ShinyDetector: Detected Square Shiny!", "blue"); + return ShinyDetection::SQUARE_SHINY; + } + + m_logger.log("ShinyDetector: Detected Shiny! But ambiguous shiny type.", "blue"); + return ShinyDetection::UNKNOWN_SHINY; +} + + + + +ShinyDetection detect_shiny_battle( + ProgramEnvironment& env, + VideoFeed& feed, Logger& logger, + const ShinyDetectionBattle& battle_settings, + std::chrono::seconds timeout, + double detection_threshold +){ +#if 0 + ShinyEncounterDetector detector(env, feed, logger, shiny_box, timeout, detection_threshold); + return detector.detect(); +#else + + StatAccumulatorI32 capture_stats; + StatAccumulatorI32 menu_stats; + StatAccumulatorI32 inference_stats; + StatAccumulatorI32 throttle_stats; + + StandardBattleMenuDetector menu(feed); + ShinyEncounterDetector detector(feed, logger, battle_settings, detection_threshold); + + bool no_detection = false; + + InferenceThrottler throttler(timeout, std::chrono::milliseconds(50)); + while (true){ + env.check_stopping(); + + auto time0 = std::chrono::system_clock::now(); + QImage screen = feed.snapshot(); + if (screen.isNull()){ + } + auto time1 = std::chrono::system_clock::now(); + capture_stats += std::chrono::duration_cast(time1 - time0).count(); + auto timestamp = time1; - if (!signatures.lines.empty()){ - image.save("DetectionLine.png"); + if (menu.detect(screen)){ + logger.log("ShinyDetector: Battle menu found!", "purple"); + break; } + auto time2 = std::chrono::system_clock::now(); + menu_stats += std::chrono::duration_cast(time2 - time1).count(); + + detector.push(screen, timestamp); + auto time3 = std::chrono::system_clock::now(); + inference_stats += std::chrono::duration_cast(time3 - time2).count(); + +// if (time3 - time2 > std::chrono::milliseconds(50)){ +// screen.save("slow-inference.png"); +// } if (throttler.end_iteration(env)){ - m_logger.log("ShinyDetector: Battle menu not found after timeout.", "red"); - return Detection::NO_BATTLE_MENU; + no_detection = true; + break; } + auto time4 = std::chrono::system_clock::now(); + throttle_stats += std::chrono::duration_cast(time4 - time3).count(); } - detection_overlays.clear(); - if (!shiny){ - m_logger.log("ShinyDetector: Not Shiny.", "purple"); - return Detection::NOT_SHINY; - } + logger.log("Diagnostics: Screenshot:\r\n" + capture_stats.dump(), Qt::magenta); + logger.log("Diagnostics: Menu Detection:\r\n" + menu_stats.dump(), Qt::magenta); + logger.log("Diagnostics: Inference:\r\n" + inference_stats.dump(), Qt::magenta); + logger.log("Diagnostics: Throttle:\r\n" + throttle_stats.dump(), Qt::magenta); - if (star_alpha >= square_alpha){ - m_logger.log("ShinyDetector: Detected Star Shiny!", "blue"); - return Detection::STAR_SHINY; - }else{ - m_logger.log("ShinyDetector: Detected Square Shiny!", "blue"); - return Detection::SQUARE_SHINY; + if (no_detection){ + logger.log("ShinyDetector: Battle menu not found after timeout.", "red"); + return ShinyDetection::NO_BATTLE_MENU; } + + return detector.results(); + +#endif } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h index 94e0130d0d..b8c1cb5b6a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h @@ -10,43 +10,35 @@ #include "CommonFramework/Tools/Logger.h" #include "CommonFramework/Tools/VideoFeed.h" #include "CommonFramework/Tools/ProgramEnvironment.h" -#include "PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h" namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonSwSh{ +enum class ShinyDetection{ + NO_BATTLE_MENU, + NOT_SHINY, + STAR_SHINY, + SQUARE_SHINY, + UNKNOWN_SHINY, +}; -class ShinyEncounterDetector{ -public: - static const InferenceBox REGULAR_BATTLE; - static const InferenceBox RAID_BATTLE; - - enum Detection{ - NO_BATTLE_MENU, - NOT_SHINY, - STAR_SHINY, - SQUARE_SHINY, - }; - -public: - ShinyEncounterDetector( - VideoFeed& feed, Logger& logger, - const InferenceBox& box, - std::chrono::seconds timeout, - double detection_threshold = 3.0 - ); - - Detection detect(ProgramEnvironment& env); - -private: - VideoFeed& m_feed; - Logger& m_logger; - InferenceBoxScope m_box; - std::chrono::seconds m_timeout; - double m_detection_threshold; - StandardBattleMenuDetector m_menu; +struct ShinyDetectionBattle{ + InferenceBox detection_box; + std::chrono::milliseconds dialog_delay_when_shiny; }; +extern const ShinyDetectionBattle SHINY_BATTLE_REGULAR; +extern const ShinyDetectionBattle SHINY_BATTLE_RAID; + + + +ShinyDetection detect_shiny_battle( + ProgramEnvironment& env, + VideoFeed& feed, Logger& logger, + const ShinyDetectionBattle& battle_settings, + std::chrono::seconds timeout, + double detection_threshold = 2.0 +); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h index 36396fe2c5..1cd6dd3343 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h @@ -27,6 +27,21 @@ struct BrightYellowLightFilter{ } }; +struct BrightYellowLightFilterDebug{ + size_t count = 0; + + void operator()(FillMatrix::ObjectID& cell, QImage& image, size_t x, size_t y){ + QRgb pixel = image.pixel(x, y); +// int set = qRed(pixel) > 160 && qGreen(pixel) > 160 && qBlue(pixel) > 128; + int set = (pixel & 0x00c0c080) == 0x00c0c080 ? 1 : 0; + cell = set; + if (cell == 0){ + image.setPixel(x, y, 0); + } + count += set; + } +}; + diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp index fb783f4e60..5cdafb0ad1 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp @@ -9,6 +9,7 @@ #include "CommonFramework/Inference/FillGeometry.h" #include "PokemonSwSh_ShinyFilters.h" #include "PokemonSwSh_SparkleTrigger.h" +#include "PokemonSwSh_SquareDetector.h" #include "PokemonSwSh_SquareTrigger.h" #include "PokemonSwSh_ShinyTrigger.h" @@ -20,6 +21,14 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonSwSh{ + +void ShinyImageAlpha::max(const ShinyImageAlpha& x){ + shiny = std::max(shiny, x.shiny); + star = std::max(star, x.star); + square = std::max(square, x.square); +} + + void ShinyImageDetection::accumulate( const QImage& image, uint64_t frame_counter, Logger* logger @@ -29,9 +38,14 @@ void ShinyImageDetection::accumulate( BrightYellowLightFilter filter; matrix.apply_filter(image, filter); + if (filter.count * 2 > (size_t)image.width() * image.height()){ + return; + } + std::vector objects; - objects = find_all_objects(matrix, true); + objects = find_all_objects(matrix, 1, true); +#if 0 QImage debug = image; for (int r = 0; r < image.height(); r++){ for (int c = 0; c < image.width(); c++){ @@ -40,13 +54,21 @@ void ShinyImageDetection::accumulate( } } } +#endif // cout << "objects = " << objects.size() << endl; // bool detection = false; +// int c = 0; for (FillGeometry& object : objects){ // this->balls.emplace_back(object.box); +// if (c++ != 4){ +// continue; +// } + +// image.copy(object.box.min_x, object.box.min_y, object.box.width(), object.box.height()).save("test-" + QString::number(c++) + ".png"); + SparkleDetector detector(matrix, object); #if 1 if (detector.is_ball()){ @@ -63,7 +85,7 @@ void ShinyImageDetection::accumulate( // detection = true; this->stars.emplace_back(object.box); QString str; - str += "ShinyDetector Star: [{" + QString::number(object.center_x) + ", " + QString::number(object.center_y); + str += "ShinyDetector: Star [{" + QString::number(object.center_x) + ", " + QString::number(object.center_y); str += "}, " + QString::number(object.area) + "]"; if (logger){ logger->log(str, "purple"); @@ -71,23 +93,28 @@ void ShinyImageDetection::accumulate( } #endif #if 1 - if (is_square(matrix, object)){ +// auto time0 = std::chrono::system_clock::now(); + if (is_square2(image, matrix, object)){ // detection = true; this->squares.emplace_back(object.box); QString str; - str += "ShinyDetector Square: [{" + QString::number(object.center_x) + ", " + QString::number(object.center_y); + str += "ShinyDetector: Square [{" + QString::number(object.center_x) + ", " + QString::number(object.center_y); str += "}, " + QString::number(object.area) + "]"; if (logger){ logger->log(str, "purple"); } } +// auto time1 = std::chrono::system_clock::now(); +// if (time1 - time0 > std::chrono::milliseconds(20)){ +// image.save("slow-inference.png"); +// } #endif #if 1 if (is_square_beam(matrix, object, 0.5)){ // detection = true; this->lines.emplace_back(object.box); QString str; - str += "ShinyDetector Line: [{" + QString::number(object.box.width()); + str += "ShinyDetector: Line [{" + QString::number(object.box.width()); str += " : {" + QString::number(object.center_x) + ", " + QString::number(object.center_y); str += "}, " + QString::number(object.area) + "]"; if (logger){ @@ -99,10 +126,11 @@ void ShinyImageDetection::accumulate( // cout << "objects = " << objects.size() << endl; #if 0 - if (detection){ -// cout << "objects = " << objects.size() << endl; - image.save("test-" + QString::number(frame_counter) + "-O.png"); - debug.save("test-" + QString::number(frame_counter) + "-P.png"); + if (!this->squares.empty()){ + image.save("detection-square-" + QString::number(frame_counter) + "-O.png"); + } + if (!this->lines.empty()){ + image.save("detection-line-" + QString::number(frame_counter) + "-O.png"); } #endif // debug.save("test.png"); @@ -111,13 +139,17 @@ void ShinyImageDetection::accumulate( -double ShinyImageDetection::alpha() const{ - double x = 1.0; - x *= std::pow(3.0, balls.size()); - x *= std::pow(3.0, stars.size()); - x *= std::pow(1.2, squares.size()); - x *= std::pow(5.0, lines.size()); - return x; +ShinyImageAlpha ShinyImageDetection::alpha() const{ + double ball_alpha = 0.4 * balls.size(); + double star_alpha = 0.4 * stars.size(); + double square_alpha = 0.3 * squares.size(); + double line_alpha = 0.5 * lines.size(); + + ShinyImageAlpha alpha; + alpha.shiny = ball_alpha + star_alpha + square_alpha + line_alpha; + alpha.star = star_alpha; + alpha.square = square_alpha + line_alpha; + return alpha; } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h index 6a87e20b01..0722f06770 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h @@ -18,6 +18,14 @@ namespace NintendoSwitch{ namespace PokemonSwSh{ +struct ShinyImageAlpha{ + double shiny = 0; + double star = 0; + double square = 0; + + void max(const ShinyImageAlpha& x); +}; + struct ShinyImageDetection{ std::vector balls; std::vector stars; @@ -30,7 +38,7 @@ struct ShinyImageDetection{ ); - double alpha() const; + ShinyImageAlpha alpha() const; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp index 0a9e5b0d44..f3474a50d9 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp @@ -31,6 +31,9 @@ SparkleDetector::SparkleDetector( if (box.area < 20){ return; } + if (box.box.width() * box.box.height() > 10000){ +// return; + } // Copy the relevant portion of the matrix and keep only the current object. m_matrix = matrix.extract(box.box, box.id); @@ -78,7 +81,7 @@ SparkleDetector::SparkleDetector( for (size_t r = 0; r < m_matrix.height(); r++){ for (size_t c = 0; c < m_matrix.width(); c++){ FillGeometry region; - if (!fill_geometry(region, m_matrix, c, r, true, id)){ + if (!fill_geometry(region, m_matrix, 1, c, r, true, id)){ continue; } id++; diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp new file mode 100644 index 0000000000..2deb1d1d7a --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp @@ -0,0 +1,641 @@ +/* Square Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include "PokemonSwSh_SquareDetector.h" + +#include +using std::cout; +using std::endl; + + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +struct Point{ + int x; + int y; + double distance; + double angle; + bool remove; +}; + +template +void dump_matrix( + FillMatrix submatrix, + int center_x, int center_y, + const MapType& points_by_distance +){ + submatrix[center_y][center_x] = 9; + for (const auto& item : points_by_distance){ + submatrix[item.second.y][item.second.x] = 9; + } + cout << submatrix.dump() << endl; +} + + +// +// Make sure the square has a hole and that the center of the square is in +// the hole. +// +bool check_hole( + size_t& background_area, + FillMatrix& submatrix, + size_t object_area, + int object_center_x, + int object_center_y +){ + int width = submatrix.width(); + int height = submatrix.height(); + size_t box_area = (size_t)width * height; + + // Fill in the edges. + size_t edge_area = 0; + for (int c = 0; c < width; c++){ + FillGeometry body; + if (fill_geometry(body, submatrix, 1, c, 0, false, 2)){ + edge_area += body.area; + } + if (fill_geometry(body, submatrix, 1, c, height - 1, false, 2)){ + edge_area += body.area; + } + } + for (int r = 0; r < height; r++){ + FillGeometry body; + if (fill_geometry(body, submatrix, 1, 0, r, false, 2)){ + edge_area += body.area; + } + if (fill_geometry(body, submatrix, 1, width - 1, r, false, 2)){ + edge_area += body.area; + } + } + background_area = edge_area; + + // Edge area is too large relative to the entire enclosing box. + if (edge_area * 3 > box_area * 2){ + return false; + } + + // The center of the object should be in the hole. + FillGeometry hole; + if (!fill_geometry(hole, submatrix, 1, object_center_x, object_center_y, false, 3)){ +// cout << "no hole in center" << endl; + return false; + } + if (hole.area < 20){ + return false; + } + + // Non-center hole area is more than 1/4 of the total hole area. + size_t total_hole_area = box_area - edge_area - object_area; + size_t non_center_hole_area = total_hole_area - hole.area; + if (non_center_hole_area * 4 > total_hole_area){ + return false; + } + + return true; +} + + + + +// +// Get a set of points that are locally the furthest away from the center of +// the object. +// +std::vector get_furthest_points( + const FillMatrix& submatrix, + int center_x, int center_y +){ + int width = submatrix.width(); + int height = submatrix.height(); +// cout << "width = " << width << endl; +// cout << "height = " << height << endl; + + // Keep points that are locally furthest away from the center. + std::vector points; + for (int r = 0; r < height; r++){ + for (int c = 0; c < width; c++){ + FillMatrix::ObjectID cell = submatrix[r][c]; + if (cell != 0){ + continue; + } + + int my_dx2 = c - center_x; + int my_dy2 = r - center_y; + my_dx2 *= my_dx2; + my_dy2 *= my_dy2; + + { + int dx2 = c + 1 - center_x; + dx2 *= dx2; + if (dx2 > my_dx2 && c + 1 < width && submatrix[r][c + 1] == 0){ + continue; + } + } + { + int dx2 = c - 1 - center_x; + dx2 *= dx2; + if (dx2 > my_dx2 && c > 0 && submatrix[r][c - 1] == 0){ + continue; + } + } + { + int dy2 = r + 1 - center_y; + dy2 *= dy2; + if (dy2 > my_dy2 && r + 1 < height && submatrix[r + 1][c] == 0){ + continue; + } + } + { + int dy2 = r - 1 - center_y; + dy2 *= dy2; + if (dy2 > my_dy2 && r > 0 && submatrix[r - 1][c] == 0){ + continue; + } + } + + int my_distance = my_dx2 + my_dy2; + { + int dx2 = c + 1 - center_x; + int dy2 = r + 1 - center_y; + dx2 *= dx2; + dy2 *= dy2; + if (dx2 + dy2 > my_distance && c + 1 < width && r + 1 < height && submatrix[r + 1][c + 1] == 0){ + continue; + } + } + { + int dx2 = c - 1 - center_x; + int dy2 = r + 1 - center_y; + dx2 *= dx2; + dy2 *= dy2; + if (dx2 + dy2 > my_distance && c > 0 && r + 1 < height && submatrix[r + 1][c - 1] == 0){ + continue; + } + } + { + int dx2 = c + 1 - center_x; + int dy2 = r - 1 - center_y; + dx2 *= dx2; + dy2 *= dy2; + if (dx2 + dy2 > my_distance && c + 1 < width && r > 0 && submatrix[r - 1][c + 1] == 0){ + continue; + } + } + { + int dx2 = c - 1 - center_x; + int dy2 = r - 1 - center_y; + dx2 *= dx2; + dy2 *= dy2; + if (dx2 + dy2 > my_distance && c > 0 && r > 0 && submatrix[r - 1][c - 1] == 0){ + continue; + } + } + +// double angle = std::atan2(r - center_y, c - center_x); + points.emplace_back( + Point{c, r, std::sqrt(my_distance), std::nan(""), false} + ); +// cout << std::sqrt(my_distance) << " : [" << c << "," << r << "]" << endl; + } + } + return points; +} + + +// +// Given the output of "get_furthest_points()", attempt to merge the ones that +// are close together - keeping the one that is further away. +// +void merge_nearby_points(std::map& points, double min_distance_diff = 0.2){ + // Merge points that are close together. + const int DISTANCE_THRESHOLD = 5 * 5; + bool changed; + do{ + // Iterate adjacent points and merge points that are close together. + changed = false; + auto iter = points.begin(); + Point* last = &iter->second; + ++iter; + for (; iter != points.end(); ++iter){ + + Point* current = &iter->second; + int dx = current->x - last->x; + int dy = current->y - last->y; + int distance = dx*dx + dy*dy; + if (distance < DISTANCE_THRESHOLD){ + if (current->distance + min_distance_diff < last->distance){ + current->remove = true; + changed = true; + } + if (current->distance > last->distance + min_distance_diff){ + last->remove = true; + changed = true; + } + } + last = current; + } + { + Point* current = &points.begin()->second; + int dx = current->x - last->x; + int dy = current->y - last->y; + int distance = dx*dx + dy*dy; + if (distance < DISTANCE_THRESHOLD){ + if (current->distance + min_distance_diff < last->distance){ + current->remove = true; + changed = true; + } + if (current->distance > last->distance + min_distance_diff){ + last->remove = true; + changed = true; + } + } + last = current; + } + + // Remove points marked for removal. + for (iter = points.begin(); iter != points.end(); ++iter){ + if (iter->second.remove){ +// cout << "remove" << endl; + points.erase(iter); + }else{ + iter->second.remove = false; + } + } +// cout << "asdf" << endl; + }while (changed); +} + + +// +// An fmod() variant that reduces to [0, 360) degrees. +// +double normalize_angle_0_360(double angle){ + while (angle < 0){ + angle += 360; + } + while (angle >= 360){ + angle -= 360; + } + return angle; +} + + +double area_of_triangle( + int x0, int y0, + int x1, int y1, + int x2, int y2 +){ + return ((double)(x0 - x2) * (y1 - y0) - (double)(x0 - x1) * (y2 - y0)) * 0.5; +} + + +// +// Return a set of all pixels that border the background. +// +// The returned map is keyed by the angle of the point to the center of the +// object. This angle is relative to "base_angle" and reduced [0, 360). +// +std::multimap> get_edge_points( + const FillMatrix& submatrix, + int center_x, int center_y, double base_angle, + FillMatrix::ObjectID object, + FillMatrix::ObjectID background +){ + int width = submatrix.width(); + int height = submatrix.height(); + + std::multimap> ret; + for (int r = 0; r < height; r++){ + for (int c = 0; c < width; c++){ + if (submatrix[r][c] != object){ + continue; + } + if ( + (c > 0 && submatrix[r][c - 1] == background) || + (c + 1 < width && submatrix[r][c + 1] == background) || + (r > 0 && submatrix[r - 1][c] == background) || + (r + 1 < width && submatrix[r + 1][c] == background) + ){ + double angle = std::atan2(r - center_y, c - center_x) * 57.295779513082320877; + angle = normalize_angle_0_360(angle - base_angle); + ret.emplace( + angle, + std::pair{c, r} + ); + } + } + } + + return ret; +} + + +// +// Given a set of points, "points_by_angle" with angles "point_lo_angle" +// and "point_hi_angle", compute the sum of squares of distances of the +// points to the line that intersects: +// [point_lo_x, point_lo_y] +// [point_hi_x, point_hi_y] +// +double sum_squares_from_line( + const std::multimap>& points_by_angle, + double point_lo_angle, int point_lo_x, int point_lo_y, + double point_hi_angle, int point_hi_x, int point_hi_y +){ +// cout << "Point 0: angle = " << point_lo_angle << ", [" << point_lo_x << "," << point_lo_y << "]" << endl; +// cout << "Point 1: angle = " << point_hi_angle << ", [" << point_hi_x << "," << point_hi_y << "]" << endl; + +// size_t count = 0; +// double sum = 0; + double sum_sqr = 0; + + auto iter0 = points_by_angle.lower_bound(point_lo_angle); + auto iter1 = points_by_angle.lower_bound(point_hi_angle); + if (point_lo_x == point_hi_x){ + for (; iter0 != iter1; ++iter0){ + double distance_sqr = iter0->second.first - point_lo_x; +// sum += std::abs(distance_sqr); + sum_sqr += distance_sqr * distance_sqr; +// count++; + } + }else{ + double slope = (double)(point_hi_y - point_lo_y) / (point_hi_x - point_lo_x); +// cout << "slope = " << slope << endl; + double inverse = 1 / (slope*slope + 1); + for (; iter0 != iter1; ++iter0){ + int dx = iter0->second.first - point_lo_x; + int dy = iter0->second.second - point_lo_y; + double top = slope * dx - dy; + double distance_sqr = top * top * inverse; +// cout << " angle = " << iter0->first +// << ", [" << iter0->second.first +// << "," << iter0->second.second +// << "] d = " << std::sqrt(distance_sqr) << endl; +// sum += std::sqrt(distance_sqr); + sum_sqr += distance_sqr; +// count++; + } + } +// cout << "average distance = " << sum / count << endl; + return sum_sqr; +} + + +// +// Return true if the object is likely a square. +// +bool is_square2( + const QImage& image, + const FillMatrix& matrix, + const FillGeometry& object, + double max_deviation +){ +// static int c = 0; +// image.copy(object.box.min_x, object.box.min_y, object.box.width(), object.box.height()).save("item-" + QString::number(++c) + ".png"); + + int width = object.box.width(); + int height = object.box.height(); +// cout << "width = " << width << endl; +// cout << "height = " << height << endl; + if (width < 10 || height < 10){ + return false; + } + if (object.area * 10 > matrix.height() * matrix.width()){ +// cout << "Bad area." << endl; + return false; + } +// size_t box_area = (size_t)width * height; + + FillMatrix submatrix = matrix.extract(object.box, object.id); + int center_x = object.center_x - object.box.min_x; + int center_y = object.center_y - object.box.min_y; + + // Invert the cells. + for (int r = 0; r < height; r++){ + for (int c = 0; c < width; c++){ + FillMatrix::ObjectID cell = submatrix[r][c]; + cell = cell ? 0 : 1; + submatrix[r][c] = cell; + } + } +// cout << submatrix.dump() << endl; + + // Check the hole + size_t background_area; + if (!check_hole(background_area, submatrix, object.area, center_x, center_y)){ + return false; + } + // The hole will be 1. + // The edge will be 2. + // The square will be 0. +// cout << submatrix.dump() << endl; + + + // Keep points that are locally furthest away from the center. + std::vector corner_candidates = get_furthest_points(submatrix, center_x, center_y); + if (corner_candidates.size() < 4){ + return false; + } + + const Point* best_point = nullptr; + { + double furthest = 0; + for (const Point& point : corner_candidates){ + if (furthest < point.distance){ + furthest = point.distance; + best_point = &point; + } + } + } + const Point& far_corner0 = *best_point; + +// dump_matrix(submatrix, center_x, center_y, corner_candidates); + + // Too few points. + if (corner_candidates.size() < 4){ +// cout << "too few points" << endl; + return false; + } + + // Calculate angles. + std::map points_by_angle; + for (auto& point : corner_candidates){ + double angle = std::atan2( + point.y - center_y, + point.x - center_x + ) * 57.295779513082320877; + + point.angle = angle; + points_by_angle[angle] = point; +// cout << "distance = " << point.first << ": " << point.second.x << ", " << point.second.y << endl; + } + corner_candidates.clear(); + + + // Merge points that are close together. + merge_nearby_points(points_by_angle); + +// dump_matrix(submatrix, center_x, center_y, points_by_angle); + + + // Look for a point opposite to the furthest. + { + double best_opposite = 0; + for (const auto& point : points_by_angle){ + double diff = point.second.angle - far_corner0.angle; + if (diff >= 180){ + diff -= 360; + } + if (diff <= -180){ + diff += 360; + } + diff = std::abs(diff); + if (best_opposite < diff){ + best_opposite = diff; + best_point = &point.second; + } + } + if (best_opposite < 160){ +// cout << "bad angle" << endl; + return false; + } + } + + + const Point& far_corner1 = *best_point; + double far_corner1_angle = normalize_angle_0_360(far_corner1.angle - far_corner0.angle); + + + // Build sets of points on each edge. + std::map edge0; + std::map edge1; + { + for (const auto& point : points_by_angle){ + if (&point.second == &far_corner0 || &point.second == &far_corner1){ + continue; + } + + double diff = normalize_angle_0_360(point.second.angle - far_corner0.angle); + if (diff < far_corner1_angle){ + edge0[diff] = point.second; + }else{ + edge1[diff] = point.second; + } + } + } + + + // Build array of all edge pixels. + std::multimap> edge_pixels = get_edge_points( + submatrix, + center_x, center_y, far_corner0.angle, + 0, 2 + ); + +// dump_matrix(submatrix, center_x, center_y, points_by_angle); + +// double size_scaling = 1 / std::sqrt(width + height); + double size_scaling = (double)1 / (width + height); + + // Attempt to find a good fit. + for (const auto& near_corner0 : edge0){ + double lo = std::max(near_corner0.first + 160, far_corner1_angle); + double hi = std::min(near_corner0.first + 200, 360.); + + auto near_corner1_iter0 = edge1.lower_bound(lo); + auto near_corner1_iter1 = edge1.upper_bound(hi); + if (near_corner1_iter0 == near_corner1_iter1){ + // No compatible corner. + continue; + } + + // Calculate things on 2 of the edges. + double edge0 = sum_squares_from_line( + edge_pixels, + 0, far_corner0.x, far_corner0.y, + near_corner0.first, near_corner0.second.x, near_corner0.second.y + ); + double edge1 = sum_squares_from_line( + edge_pixels, + near_corner0.first, near_corner0.second.x, near_corner0.second.y, + far_corner1_angle, far_corner1.x, far_corner1.y + ); + double area0 = area_of_triangle( + center_x, center_y, + far_corner0.x, far_corner0.y, + near_corner0.second.x, near_corner0.second.y + ); + double area1 = area_of_triangle( + center_x, center_y, + near_corner0.second.x, near_corner0.second.y, + far_corner1.x, far_corner1.y + ); + + // Iterate through the candidate corners on the other side. + for (; near_corner1_iter0 != near_corner1_iter1; ++near_corner1_iter0){ + double edge2 = sum_squares_from_line( + edge_pixels, + far_corner1_angle, far_corner1.x, far_corner1.y, + near_corner1_iter0->first, near_corner1_iter0->second.x, near_corner1_iter0->second.y + ); + double edge3 = sum_squares_from_line( + edge_pixels, + near_corner1_iter0->first, near_corner1_iter0->second.x, near_corner1_iter0->second.y, + 360, far_corner0.x, far_corner0.y + ); + double area2 = area_of_triangle( + center_x, center_y, + far_corner1.x, far_corner1.y, + near_corner1_iter0->second.x, near_corner1_iter0->second.y + ); + double area3 = area_of_triangle( + center_x, center_y, + near_corner1_iter0->second.x, near_corner1_iter0->second.y, + far_corner0.x, far_corner0.y + ); + + double sum_squares = edge0 + edge1 + edge2 + edge3; + double normalized_distance = std::sqrt(sum_squares / edge_pixels.size()); + + normalized_distance *= size_scaling; +// cout << "normalized_distance = " << normalized_distance << endl; + + // Too much deviation from edges. + if (normalized_distance > max_deviation){ + continue; + } + + double regression_area = area0 + area1 + area2 + area3; + double actual_object_area = width * height - background_area; +// cout << "area = " << regression_area << " / " << actual_object_area << endl; + + if (actual_object_area < regression_area){ + continue; + } + +// cout << "image: " << c << endl; +// cout << "normalized_distance = " << normalized_distance << endl; +// dump_matrix(submatrix, center_x, center_y, points_by_angle); + return true; + } + } + +// cout << "bad" << endl; + return false; +} + + + + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.h new file mode 100644 index 0000000000..a2e9a687d0 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.h @@ -0,0 +1,31 @@ +/* Square Detector + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_SquareDetector_H +#define PokemonAutomation_PokemonSwSh_SquareDetector_H + +#include "CommonFramework/Inference/FillMatrix.h" +#include "CommonFramework/Inference/FillGeometry.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + + +bool is_square2( + const QImage& image, + const FillMatrix& matrix, + const FillGeometry& object, + double max_deviation = 0.04 +); + + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp index 9d2fdaa2b4..cdb375c039 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp @@ -96,7 +96,10 @@ bool is_square_beam( } - +// +// The old square detector. Deprecated. +// +#if 0 bool is_square( const FillMatrix& matrix, const FillGeometry& object @@ -138,19 +141,19 @@ bool is_square( size_t edge_area = 0; for (int c = 0; c < width; c++){ FillGeometry body; - if (fill_geometry(body, submatrix, c, 0, false, 2)){ + if (fill_geometry(body, submatrix, 1, c, 0, false, 2)){ edge_area += body.area; } - if (fill_geometry(body, submatrix, c, height - 1, false, 2)){ + if (fill_geometry(body, submatrix, 1, c, height - 1, false, 2)){ edge_area += body.area; } } for (int r = 0; r < height; r++){ FillGeometry body; - if (fill_geometry(body, submatrix, 0, r, false, 2)){ + if (fill_geometry(body, submatrix, 1, 0, r, false, 2)){ edge_area += body.area; } - if (fill_geometry(body, submatrix, width - 1, r, false, 2)){ + if (fill_geometry(body, submatrix, 1, width - 1, r, false, 2)){ edge_area += body.area; } } @@ -171,103 +174,10 @@ bool is_square( } } -#if 0 - std::set angles; - { - int r = object.box.min_y; - for (int c = object.box.min_x; c < object.box.max_x; c++){ - if (matrix[r][c] != object.id){ - continue; - } - int x = c - object.center_x; - int y = r - object.center_y; - double angle = std::atan2(y, x) * 57.29577951308232; - if (angle < 0){ - angle += 360; - } - angles.insert(angle); - } - } - { - int r = object.box.max_y - 1; - for (int c = object.box.min_x; c < object.box.max_x; c++){ - if (matrix[r][c] != object.id){ - continue; - } - int x = c - object.center_x; - int y = r - object.center_y; - double angle = std::atan2(y, x) * 57.29577951308232; - if (angle < 0){ - angle += 360; - } - angles.insert(angle); - } - } - { - int c = object.box.min_x; - for (int r = object.box.min_y; r < object.box.max_y; r++){ - if (matrix[r][c] != object.id){ - continue; - } - int x = c - object.center_x; - int y = r - object.center_y; - double angle = std::atan2(y, x) * 57.29577951308232; - if (angle < 0){ - angle += 360; - } - angles.insert(angle); - } - } - { - int c = object.box.max_x - 1; - for (int r = object.box.min_y; r < object.box.max_y; r++){ - if (matrix[r][c] != object.id){ - continue; - } - int x = c - object.center_x; - int y = r - object.center_y; - double angle = std::atan2(y, x) * 57.29577951308232; - if (angle < 0){ - angle += 360; - } - angles.insert(angle); - } - } - -// cout << "angles = " << angles.size() << endl; - - // Group angles. - std::map angle_sets; - while (!angles.empty()){ - double pivot = *angles.begin(); - int& high = angle_sets[pivot] = false; - for (auto angle : angles){ - double n = angle - pivot; - double q = std::round(n / 180.); - double r = n - 180*q; - if (std::abs(r) < SQUARE_ANGLE_TOLERANCE_DEGREES){ - angles.erase(angle); - if (n > 100){ - high = 1; - } - } - } - } - - int sum = 0; - for (const auto& angle : angle_sets){ -// cout << "Angle Set: " << angle.first << ":" << angle.second << endl; - sum += angle.second; - } - if (sum < 2){ - return false; - } - - cout << width << " x " << height << endl; +// cout << submatrix.dump() << endl; -#else - // Check vertix angles. + // Check vertex angles. { submatrix = matrix.extract(object.box, object.id); @@ -277,29 +187,10 @@ bool is_square( submatrix[r][c] = 0; } } +// cout << submatrix.dump() << endl; - std::vector regions = find_all_objects(submatrix, false, 1); -// std::multimap regions; -// for (const FillGeometry& region : find_all_objects(submatrix, false, 1)){ -// regions.emplace(region.area, region); -// } - -#if 0 - cout << "{" << endl; - for (int r = 0; r < height; r++){ - cout << " {"; - for (int c = 0; c < width; c++){ - cout << submatrix[r][c] << ","; - } - cout << " }" << endl; - } - cout << "}" << endl; -#endif - + std::vector regions = find_all_objects(submatrix, 1, false, 1); // cout << "regions = " << regions.size() << endl; -// if (regions.size() < 4){ -// return false; -// } // Get furthest points. std::vector> points; @@ -357,14 +248,16 @@ bool is_square( // cout << "Angle Set: " << angle.first << ":" << angle.second << endl; sum += angle.second; } +// cout << "sum = " << sum << endl; if (sum < 2){ return false; } } -#endif +// cout << "square" << endl; return true; } +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h index 5f1982e608..7e73bcce9c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h @@ -1,11 +1,11 @@ -/* Line Trigger +/* Square Trigger * * From: https://github.com/PokemonAutomation/Arduino-Source * */ -#ifndef PokemonAutomation_PokemonSwSh_LineTrigger_H -#define PokemonAutomation_PokemonSwSh_LineTrigger_H +#ifndef PokemonAutomation_PokemonSwSh_SquareTrigger_H +#define PokemonAutomation_PokemonSwSh_SquareTrigger_H #include #include "CommonFramework/Inference/FillGeometry.h" diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp index e9842d61a7..c601e23d8f 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp @@ -90,15 +90,14 @@ void PurpleBeamFinder::program(SingleSwitchProgramEnvironment& env) const{ break; } { - std::string status = "Attempts: " + tostr_u_commas(attempts); + std::string status = "Attempts: " + tostr_u_commas(attempts); status += " - Timeouts: " + tostr_u_commas(timed_out); status += " - Red Detected: " + tostr_u_commas(red_detected); status += " - Red Presumed: " + tostr_u_commas(red_assumed); status += " - Purple: " + tostr_u_commas(purple); - status += "
"; QString str = status.c_str(); - env.logger.log(str); env.set_status(str); + env.logger.log("" + str + ""); } if (exit){ break; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp index 51035f31cf..0becc2d760 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp @@ -29,6 +29,7 @@ DenRoller::DenRoller() ) { m_options.emplace_back(&SKIPS, "SKIPS"); + m_options.emplace_back(&CATCHABILITY, "CATCHABILITY"); m_options.emplace_back(&VIEW_TIME, "VIEW_TIME"); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp index 4766064acc..37b635ff30 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp @@ -17,9 +17,9 @@ EncounterStats::EncounterStats(bool shiny_types) {} void EncounterStats::log_stats(ProgramEnvironment& env, Logger& logger) const{ - QString str = "" + QString(stats().c_str()) + ""; + QString str = QString(stats().c_str()); env.set_status(str); - logger.log(str); + logger.log("" + str + ""); } std::string EncounterStats::stats() const{ std::string str; @@ -37,8 +37,11 @@ std::string EncounterStats::str_shinies() const{ if (m_shiny_types){ str += " - Star Shinies: " + tostr_u_commas(m_star_shinies); str += " - Square Shinies: " + tostr_u_commas(m_square_shinies); + if (m_unknown_shinies > 0){ + str += " - Unknown Shinies: " + tostr_u_commas(m_unknown_shinies); + } }else{ - str += " - Shinies: " + tostr_u_commas(m_shinies); + str += " - Shinies: " + tostr_u_commas(m_unknown_shinies); } return str; } @@ -48,18 +51,16 @@ std::string EncounterStats::str_shinies() const{ void EncounterStats::add_non_shiny(){ m_encounters++; } -void EncounterStats::add_shiny(){ +void EncounterStats::add_unknown_shiny(){ m_encounters++; - m_shinies++; + m_unknown_shinies++; } void EncounterStats::add_star_shiny(){ m_encounters++; - m_shinies++; m_star_shinies++; } void EncounterStats::add_square_shiny(){ m_encounters++; - m_shinies++; m_square_shinies++; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h index af0d04b297..7541a8a4e1 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h @@ -29,14 +29,14 @@ class EncounterStats{ virtual std::string str_shinies() const; void add_non_shiny(); - void add_shiny(); + void add_unknown_shiny(); void add_star_shiny(); void add_square_shiny(); protected: bool m_shiny_types; uint64_t m_encounters = 0; - uint64_t m_shinies = 0; + uint64_t m_unknown_shinies = 0; uint64_t m_star_shinies = 0; uint64_t m_square_shinies = 0; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp index 0bfd8fb886..2a9a0901b0 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp @@ -31,15 +31,15 @@ bool StandardEncounterTracker::run_away(){ return true; } -bool StandardEncounterTracker::process_result(ShinyEncounterDetector::Detection detection){ +bool StandardEncounterTracker::process_result(ShinyDetection detection){ switch (detection){ - case ShinyEncounterDetector::NO_BATTLE_MENU: + case ShinyDetection::NO_BATTLE_MENU: return false; - case ShinyEncounterDetector::NOT_SHINY: + case ShinyDetection::NOT_SHINY: m_stats.add_non_shiny(); run_away(); return false; - case ShinyEncounterDetector::STAR_SHINY: + case ShinyDetection::STAR_SHINY: m_stats.add_star_shiny(); pbf_wait(m_console, 5 * TICKS_PER_SECOND); pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); @@ -48,11 +48,16 @@ bool StandardEncounterTracker::process_result(ShinyEncounterDetector::Detection return false; } return true; - case ShinyEncounterDetector::SQUARE_SHINY: + case ShinyDetection::SQUARE_SHINY: m_stats.add_square_shiny(); pbf_wait(m_console, 5 * TICKS_PER_SECOND); pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); return true; + case ShinyDetection::UNKNOWN_SHINY: + m_stats.add_unknown_shiny(); + pbf_wait(m_console, 5 * TICKS_PER_SECOND); + pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); + return true; } return false; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h index ae2277f10c..1e132f096e 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h @@ -27,7 +27,7 @@ class StandardEncounterTracker{ virtual bool run_away(); - bool process_result(ShinyEncounterDetector::Detection detection); + bool process_result(ShinyDetection detection); protected: EncounterStats& m_stats; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp index 71897a13b0..53eeed01bd 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-BerryTree +/* Shiny Hunt Autonomous - Berry Tree * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -27,6 +27,10 @@ ShinyHuntAutonomousBerryTree::ShinyHuntAutonomousBerryTree() "SerialPrograms/ShinyHuntAutonomous-BerryTree.md", "Automatically hunt for shiny berry tree " + STRING_POKEMON + " using video feedback." ) + , GO_HOME_WHEN_DONE( + "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", + true + ) , REQUIRE_SQUARE( "Require Square:
Stop only for a square shiny. Run from star shinies.", false @@ -39,6 +43,7 @@ ShinyHuntAutonomousBerryTree::ShinyHuntAutonomousBerryTree() "6 * TICKS_PER_SECOND" ) { + m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&REQUIRE_SQUARE, "REQUIRE_SQUARE"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); @@ -81,11 +86,11 @@ void ShinyHuntAutonomousBerryTree::program(SingleSwitchProgramEnvironment& env) env.logger.log("ScreenChangeDetector: Timed out.", Qt::red); stats.m_timeouts++; timed_out = true; - break;; + break; } pbf_mash_button(BUTTON_A, 10); env.console.botbase().wait_for_all_requests(); - }while (!detector.detect()); + }while (!detector.detect(env.console.video().snapshot())); pbf_mash_button(BUTTON_B, 5 * TICKS_PER_SECOND); if (timed_out){ @@ -95,20 +100,16 @@ void ShinyHuntAutonomousBerryTree::program(SingleSwitchProgramEnvironment& env) } // Detect shiny. - ShinyEncounterDetector::Detection detection; - { - ShinyEncounterDetector detector( - env.console, env.logger, - ShinyEncounterDetector::REGULAR_BATTLE, - std::chrono::seconds(30) - ); - detection = detector.detect(env); - } + ShinyDetection detection = detect_shiny_battle( + env, env.console, env.logger, + SHINY_BATTLE_REGULAR, + std::chrono::seconds(30) + ); if (tracker.process_result(detection)){ break; } - if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + if (detection == ShinyDetection::NO_BATTLE_MENU){ stats.m_timeouts++; pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); tracker.run_away(); @@ -126,6 +127,10 @@ void ShinyHuntAutonomousBerryTree::program(SingleSwitchProgramEnvironment& env) pbf_press_button(BUTTON_A, 5, 10); pbf_press_button(BUTTON_HOME, 10, SETTINGS_TO_HOME_DELAY); + if (!GO_HOME_WHEN_DONE){ + pbf_press_button(BUTTON_HOME, 10, HOME_TO_GAME_DELAY); + } + end_program_callback(); end_program_loop(); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h index bcd17ac8ee..079ca3a2e0 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-BerryTree +/* Shiny Hunt Autonomous - BerryTree * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -30,6 +30,7 @@ class ShinyHuntAutonomousBerryTree : public SingleSwitchProgram{ uint64_t m_timeouts = 0; }; + BooleanCheckBox GO_HOME_WHEN_DONE; BooleanCheckBox REQUIRE_SQUARE; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp index 6196b6fa99..131dfdb7a7 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-Fishing +/* Shiny Hunt Autonomous - Fishing * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -27,6 +27,10 @@ ShinyHuntAutonomousFishing::ShinyHuntAutonomousFishing() "SerialPrograms/ShinyHuntAutonomous-Fishing.md", "Automatically hunt for shiny fishing " + STRING_POKEMON + " using video feedback." ) + , GO_HOME_WHEN_DONE( + "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", + true + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -43,6 +47,7 @@ ShinyHuntAutonomousFishing::ShinyHuntAutonomousFishing() 1, 0, 11 ) { + m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); m_options.emplace_back(&FISH_RESPAWN_TIME, "FISH_RESPAWN_TIME"); @@ -120,20 +125,16 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co } // Detect shiny. - ShinyEncounterDetector::Detection detection; - { - ShinyEncounterDetector detector( - env.console, env.logger, - ShinyEncounterDetector::REGULAR_BATTLE, - std::chrono::seconds(30) - ); - detection = detector.detect(env); - } + ShinyDetection detection = detect_shiny_battle( + env, env.console, env.logger, + SHINY_BATTLE_REGULAR, + std::chrono::seconds(30) + ); if (tracker.process_result(detection)){ break; } - if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + if (detection == ShinyDetection::NO_BATTLE_MENU){ stats.m_timeouts++; pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); tracker.run_away(); @@ -142,7 +143,9 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co stats.log_stats(env, env.logger); - pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + if (GO_HOME_WHEN_DONE){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + } end_program_callback(); end_program_loop(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h index 09c363efa8..40e8c1829a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-Fishing +/* Shiny Hunt Autonomous - Fishing * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -8,6 +8,7 @@ #define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousFishing_H #include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/BooleanCheckBox.h" #include "CommonFramework/Options/SimpleInteger.h" #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" @@ -33,6 +34,7 @@ class ShinyHuntAutonomousFishing : public SingleSwitchProgram{ uint64_t m_unexpected_battles = 0; }; + BooleanCheckBox GO_HOME_WHEN_DONE; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; TimeExpression FISH_RESPAWN_TIME; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp index 1809390fa6..ef5f67a703 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-IoATrade +/* Shiny Hunt Autonomous - IoA Trade * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -21,10 +21,14 @@ namespace PokemonSwSh{ ShinyHuntAutonomousIoATrade::ShinyHuntAutonomousIoATrade() : SingleSwitchProgram( FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, - "Shiny Hunt Autonomous - IoATrade", + "Shiny Hunt Autonomous - IoA Trade", "SerialPrograms/ShinyHuntAutonomous-IoATrade.md", "Hunt for shiny Isle of Armor trade using video feedback." ) + , GO_HOME_WHEN_DONE( + "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", + true + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -37,6 +41,7 @@ ShinyHuntAutonomousIoATrade::ShinyHuntAutonomousIoATrade() "4 * 3600 * TICKS_PER_SECOND" ) { + m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&MASH_TO_TRADE_DELAY, "MASH_TO_TRADE_DELAY"); m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); @@ -65,7 +70,7 @@ void ShinyHuntAutonomousIoATrade::program(SingleSwitchProgramEnvironment& env) c stats.log_stats(env, env.logger); pbf_press_button(BUTTON_A, 10, 100); - pbf_press_button(BUTTON_A, 10, 50); + pbf_press_button(BUTTON_A, 10, 60); pbf_press_button(BUTTON_A, 10, 100); pbf_press_button(BUTTON_A, 10, 50); pbf_press_button(BUTTON_A, 10, POKEMON_TO_BOX_DELAY); @@ -95,7 +100,7 @@ void ShinyHuntAutonomousIoATrade::program(SingleSwitchProgramEnvironment& env) c stats.add_non_shiny(); break; case SummaryShinySymbolDetector::SHINY: - stats.add_shiny(); + stats.add_unknown_shiny(); pbf_wait(1 * TICKS_PER_SECOND); pbf_press_button(BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); goto StopProgram; @@ -116,7 +121,9 @@ void ShinyHuntAutonomousIoATrade::program(SingleSwitchProgramEnvironment& env) c StopProgram: stats.log_stats(env, env.logger); - pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + if (GO_HOME_WHEN_DONE){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + } end_program_callback(); end_program_loop(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h index ae00de385e..460f6cd0f5 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-IoATrade +/* Shiny Hunt Autonomous - IoATrade * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -8,6 +8,7 @@ #define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousIoATrade_H #include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/BooleanCheckBox.h" #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" #include "PokemonSwSh_EncounterStats.h" @@ -29,6 +30,7 @@ class ShinyHuntAutonomousIoATrade : public SingleSwitchProgram{ uint64_t m_errors = 0; }; + BooleanCheckBox GO_HOME_WHEN_DONE; SectionDivider m_advanced_options; TimeExpression MASH_TO_TRADE_DELAY; TimeExpression TOUCH_DATE_INTERVAL; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp new file mode 100644 index 0000000000..02b7c2a134 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp @@ -0,0 +1,412 @@ +/* Shiny Hunt Autonomous - Overworld + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include "Common/Clientside/PrettyPrint.h" +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/InferenceThrottler.h" +#include "PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h" +#include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" +#include "PokemonSwSh_ShinyHuntAutonomous-Overworld.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +ShinyHuntAutonomousOverworld::ShinyHuntAutonomousOverworld() + : SingleSwitchProgram( + FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, + "Shiny Hunt Autonomous - Overworld", + "SerialPrograms/ShinyHuntAutonomous-Overworld.md", + "Automatically shiny hunt overworld " + STRING_POKEMON + " with video feedback." + ) + , GO_HOME_WHEN_DONE( + "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", + true + ) + , PRIORITIZE_EXCLAMATION_POINTS( + "Prioritize Exclamation Points:
Given multiple options, prefer those with exclamation points." + " This prioritizes random grass encounters and " + STRING_POKEMON + " that flee.", + true + ) + , ENABLE_CIRCLING( + "Enable Circling:
After moving towards a " + STRING_POKEMON + ", make a circle." + " This increases the chance of encountering the " + STRING_POKEMON + " if it has moved or if the trajectory missed.", + true + ) + , MAX_MOVE_DURATION( + "Maximum Move Duration:
Do not move in the same direction for more than this long." + " If you set this too high, you may wander too far from the grassy area.", + "150" + ) + , WATCHDOG_TIMER( + "Watchdog Timer:
Reset the game if you go this long without any encounters.", + "120 * TICKS_PER_SECOND" + ) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , EXIT_BATTLE_MASH_TIME( + "Exit Battle Time:
After running, wait this long to return to overworld.", + "6 * TICKS_PER_SECOND" + ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 + ) +{ + m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); + m_options.emplace_back(&PRIORITIZE_EXCLAMATION_POINTS, "PRIORITIZE_EXCLAMATION_POINTS"); + m_options.emplace_back(&ENABLE_CIRCLING, "ENABLE_CIRCLING"); + m_options.emplace_back(&MAX_MOVE_DURATION, "MAX_MOVE_DURATION"); + m_options.emplace_back(&WATCHDOG_TIMER, "WATCHDOG_TIMER"); + m_options.emplace_back(&m_advanced_options, ""); + m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); +} + + +std::string ShinyHuntAutonomousOverworld::Stats::stats() const{ + std::string str; + str += str_encounters(); + str += " - Timeouts: " + tostr_u_commas(m_timeouts); + str += " - Unexpected Battles: " + tostr_u_commas(m_unexpected_battles); + str += " - Resets: " + tostr_u_commas(m_resets); + str += str_shinies(); + return str; +} + + + + +struct Trajectory{ + uint16_t distance_in_ticks; + uint8_t joystick_x; + uint8_t joystick_y; +}; + +const Trajectory TRAJECTORY_TABLE[8][6] = { + {{400,128, 0},{420,160, 0},{420,190, 0},{480,208, 0},{480,208, 0},{480,208, 0},}, + {{210,128, 0},{220,164, 0},{250,200, 0},{280,208, 0},{300,209, 0},{320,209, 0},}, + {{120,128, 0},{120,192, 0},{140,209, 0},{180,224, 0},{220,224, 0},{260,232, 0},}, + {{ 60,128, 0},{ 65,224, 0},{ 95,255, 32},{125,255, 48},{160,255, 48},{200,255, 48},}, + {{ 0,128,128},{ 40,255,110},{ 70,255,105},{100,255,100},{130,255, 95},{160,255, 90},}, + {{ 35,128,255},{ 45,255,255},{ 70,255,170},{ 90,255,160},{115,255,140},{140,255,128},}, + {{ 55,128,255},{ 60,192,255},{ 75,255,255},{ 90,255,192},{110,255,176},{135,255,144},}, + {{ 75,128,255},{ 80,160,255},{ 85,216,255},{ 95,255,224},{115,255,192},{130,255,176},}, +}; +Trajectory get_trajectory_int(int delta_x, int delta_y){ + delta_x = std::max(delta_x, -5); + delta_x = std::min(delta_x, +5); + delta_y = std::max(delta_y, -4); + delta_y = std::min(delta_y, +3); + + if (delta_x < 0){ + Trajectory entry = get_trajectory_int(-delta_x, delta_y); + entry.joystick_x = 256 - entry.joystick_x; + return entry; + } + + return TRAJECTORY_TABLE[delta_y + 4][delta_x]; +} +Trajectory get_trajectory_float(double delta_x, double delta_y){ + delta_x *= 10; + delta_y *= 10; + + int int_x = std::floor(delta_x); + int int_y = std::floor(delta_y); + + double frac_x = delta_x - int_x; + double frac_y = delta_y - int_y; + + Trajectory corner11 = get_trajectory_int(int_x, int_y); + Trajectory corner01 = get_trajectory_int(int_x + 1, int_y); + Trajectory corner10 = get_trajectory_int(int_x, int_y + 1); + Trajectory corner00 = get_trajectory_int(int_x + 1, int_y + 1); + + double top_x = corner11.joystick_x * (1.0 - frac_x) + corner01.joystick_x * frac_x; + double bot_x = corner10.joystick_x * (1.0 - frac_x) + corner00.joystick_x * frac_x; + double x = top_x * (1.0 - frac_y) + bot_x * frac_y; +// cout << "top_x = " << top_x << endl; +// cout << "bot_x = " << bot_x << endl; +// cout << "x = " << x << endl; + + double top_y = corner11.joystick_y * (1.0 - frac_y) + corner10.joystick_y * frac_y; + double bot_y = corner01.joystick_y * (1.0 - frac_y) + corner00.joystick_y * frac_y; + double y = top_y * (1.0 - frac_x) + bot_y * frac_x; + + double top_d = corner11.distance_in_ticks * (1.0 - frac_x) + corner01.distance_in_ticks * frac_x; + double bot_d = corner10.distance_in_ticks * (1.0 - frac_x) + corner00.distance_in_ticks * frac_x; + double d = top_d * (1.0 - frac_y) + bot_d * frac_y; + + x -= 128; + y -= 128; + double scale = std::max(std::abs(x), std::abs(y)); + if (scale == 0){ + x = 128; + y = 128; + }else{ + scale = 128 / scale; + x *= scale; + y *= scale; + } + x += 128; + y += 128; + + d = std::max(d, 0.0); + d = std::min(d, 65535.); + x = std::max(x, 0.0); + y = std::max(y, 0.0); + x = std::min(x, 255.); + y = std::min(y, 255.); + + return Trajectory{(uint16_t)d, (uint8_t)x, (uint8_t)y}; +} + + +void ShinyHuntAutonomousOverworld::move_in_circle(SingleSwitchProgramEnvironment& env, uint8_t size_ticks) const{ +// cout << "size_ticks = " << (int)size_ticks << endl; + + pbf_move_left_joystick(env.console, 0, 128, size_ticks, 0); // Correct for bias. + + size_ticks *= 2; + pbf_move_left_joystick(env.console, 128, 0, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 0, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 128, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 255, size_ticks, 0); + pbf_move_left_joystick(env.console, 128, 255, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 255, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 128, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 0, size_ticks, 0); +} +bool ShinyHuntAutonomousOverworld::find_encounter( + SingleSwitchProgramEnvironment& env, + Stats& stats, + StandardEncounterTracker& tracker +) const{ + InferenceBoxScope search_area(env.console, 0.0, 0.2, 1.0, 0.8); + StandardBattleMenuDetector battle_menu(env.console); + StartBattleDetector start_battle(env.console, std::chrono::milliseconds(0)); + + const double center_x = 0.5; + const double center_y = 0.70; + InferenceBoxScope self(env.console, Qt::cyan, center_x - 0.02, center_y - 0.05, 0.04, 0.1); + + const std::chrono::milliseconds TIMEOUT((uint64_t)WATCHDOG_TIMER * 1000 / TICKS_PER_SECOND); + + auto last = std::chrono::system_clock::now(); +// size_t c = 0; + while (true){ + std::deque boxes; + InferenceThrottler throttler(std::chrono::seconds(1)); + + std::multimap detections; + do{ + env.check_stopping(); + + // No battle for a long time. Reset the game. + auto now = std::chrono::system_clock::now(); + if (now - last > TIMEOUT){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + reset_game_from_home_with_inference( + env, env.logger, + env.console, + TOLERATE_SYSTEM_UPDATE_MENU_FAST + ); + stats.m_resets++; + return false; + } + + // Grab screenshot. + QImage screen = env.console.video().snapshot(); +// screen.save("test-" + QString::number(c++) + ".png"); + + // Check if a battle has started. + if (battle_menu.detect(screen)){ + stats.m_unexpected_battles++; + tracker.run_away(); + return false; + } + if (start_battle.detect(screen)){ + env.logger.log("Battle started!"); + return true; + } + + // Look for exclamation points and question marks. + QImage search_image = extract_box(screen, search_area); + + std::vector exclamation_marks; + std::vector question_marks; + + size_t count = find_marks( + search_image, + &exclamation_marks, + &question_marks + ); + if (count == 0){ + pbf_press_button(BUTTON_LCLICK, 5, 0); +// move_in_circle(env, 5); + env.console.botbase().wait_for_all_requests(); + boxes.clear(); + continue; + } + + boxes.clear(); + for (const PixelBox& mark : exclamation_marks){ + InferenceBox box = translate_to_parent(screen, search_area, mark); + box.color = Qt::magenta; + box.x -= box.width; + box.width *= 3; + box.height *= 2; + boxes.emplace_back(env.console, box); + + double delta_x = box.x + box.width / 2 - center_x; + double delta_y = box.y + box.height * 1.5 - center_y; +// double distance = delta_x*delta_x + delta_y*delta_y; + env.logger.log( + "Exclamation at: [" + QString::number(delta_x) + " , " + QString::number(-delta_y) + "]", + "purple" + ); + // cout << std::sqrt(distance) << endl; + Trajectory trajectory = get_trajectory_float(delta_x, delta_y); + detections.emplace(trajectory.distance_in_ticks, trajectory); + break; + } + for (const PixelBox& mark : question_marks){ + InferenceBox box = translate_to_parent(screen, search_area, mark); + box.color = Qt::magenta; + box.x -= box.width / 2; + box.width *= 2; + box.height *= 2; + boxes.emplace_back(env.console, box); + + double delta_x = box.x + box.width / 2 - center_x; + double delta_y = box.y + box.height * 1.5 - center_y; +// double distance = delta_x*delta_x + delta_y*delta_y; + env.logger.log( + "Question at: [" + QString::number(delta_x) + " , " + QString::number(-delta_y) + "]", + "purple" + ); + // cout << std::sqrt(distance) << endl; + if (!PRIORITIZE_EXCLAMATION_POINTS || exclamation_marks.empty()){ + Trajectory trajectory = get_trajectory_float(delta_x, delta_y); + detections.emplace(trajectory.distance_in_ticks, trajectory); + } + break; + } + if (!detections.empty()){ + break; + } + + }while (!throttler.end_iteration(env)); + + // Nothing was found. Rotate the view and try again. + if (detections.empty()){ + env.logger.log("Nothing found. Rotating view."); + pbf_move_right_joystick(192, 255, 50, 0); +// move_in_circle(env, 5); + env.console.botbase().wait_for_all_requests(); + continue; + } + + auto target = detections.begin(); + const Trajectory& trajectory = target->second; + + double angle = std::atan2((double)trajectory.joystick_y - 128, (double)trajectory.joystick_x - 128) * 57.295779513082320877; + env.logger.log("Found something. Distance: " + QString::number(trajectory.distance_in_ticks) + ", Direction: " + QString::number(-angle) + " degrees"); + + int duration = trajectory.distance_in_ticks + 30; + if (duration > (int)MAX_MOVE_DURATION){ + duration = MAX_MOVE_DURATION; + } + pbf_wait(50); + pbf_move_left_joystick( + trajectory.joystick_x, + trajectory.joystick_y, + (uint16_t)duration, 0 + ); + + // Circle Maneuver + if (ENABLE_CIRCLING){ + move_in_circle(env, 8); + } + + env.console.botbase().wait_for_all_requests(); +// boxes.clear(); + } +} + +void ShinyHuntAutonomousOverworld::program(SingleSwitchProgramEnvironment& env) const{ + grip_menu_connect_go_home(); + resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 200); + pbf_move_right_joystick(128, 255, TICKS_PER_SECOND, 0); + + const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; + uint32_t last_touch = system_clock(); + + Stats stats; + StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); + + // Encounter Loop + while (true){ + stats.log_stats(env, env.logger); + + // Touch the date. + if (TIME_ROLLBACK_HOURS > 0 && system_clock() - last_touch >= PERIOD){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + rollback_hours_from_home(TIME_ROLLBACK_HOURS, SETTINGS_TO_HOME_DELAY); + resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); + last_touch += PERIOD; + } + env.console.botbase().wait_for_all_requests(); + + bool battle = find_encounter(env, stats, tracker); + if (!battle){ + continue; + } + + // Detect shiny. + ShinyDetection detection = detect_shiny_battle( + env, env.console, env.logger, + SHINY_BATTLE_REGULAR, + std::chrono::seconds(30) + ); + + if (tracker.process_result(detection)){ + break; + } + if (detection == ShinyDetection::NO_BATTLE_MENU){ + stats.m_timeouts++; + pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); + tracker.run_away(); + } + } + + stats.log_stats(env, env.logger); + + if (GO_HOME_WHEN_DONE){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + } + + end_program_callback(); + end_program_loop(); +} + + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h new file mode 100644 index 0000000000..6256a2fd06 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h @@ -0,0 +1,61 @@ +/* Shiny Hunt Autonomous - Overworld + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousOverworld_H +#define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousOverworld_H + +#include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/BooleanCheckBox.h" +#include "CommonFramework/Options/SimpleInteger.h" +#include "NintendoSwitch/Options/TimeExpression.h" +#include "NintendoSwitch/Framework/SingleSwitchProgram.h" +#include "PokemonSwSh_EncounterTracker.h" +#include "PokemonSwSh_EncounterStats.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +class ShinyHuntAutonomousOverworld : public SingleSwitchProgram{ +public: + ShinyHuntAutonomousOverworld(); + + virtual void program(SingleSwitchProgramEnvironment& env) const override; + + +private: + struct Stats : public EncounterStats{ + Stats() : EncounterStats(true) {} + virtual std::string stats() const override; + uint64_t m_timeouts = 0; + uint64_t m_unexpected_battles = 0; + uint64_t m_resets = 0; + }; + + void move_in_circle(SingleSwitchProgramEnvironment& env, uint8_t size_ticks) const; + bool find_encounter( + SingleSwitchProgramEnvironment& env, + Stats& stats, + StandardEncounterTracker& tracker + ) const; + + +private: + BooleanCheckBox GO_HOME_WHEN_DONE; + BooleanCheckBox PRIORITIZE_EXCLAMATION_POINTS; + BooleanCheckBox ENABLE_CIRCLING; + TimeExpression MAX_MOVE_DURATION; + TimeExpression WATCHDOG_TIMER; + SectionDivider m_advanced_options; + TimeExpression EXIT_BATTLE_MASH_TIME; + SimpleInteger TIME_ROLLBACK_HOURS; +}; + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp index 70387fabdc..8550052fef 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-Regi +/* Shiny Hunt Autonomous - Regi * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -26,6 +26,10 @@ ShinyHuntAutonomousRegi::ShinyHuntAutonomousRegi() "SerialPrograms/ShinyHuntAutonomous-Regi.md", "Automatically hunt for shiny Regi using video feedback." ) + , GO_HOME_WHEN_DONE( + "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", + true + ) , REQUIRE_SQUARE( "Require Square:
Stop only for a square shiny. Run from star shinies.", false @@ -46,8 +50,9 @@ ShinyHuntAutonomousRegi::ShinyHuntAutonomousRegi() "4 * 3600 * TICKS_PER_SECOND" ) { - m_options.emplace_back(®I_NAME, "REGI_NAME"); + m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&REQUIRE_SQUARE, "REQUIRE_SQUARE"); + m_options.emplace_back(®I_NAME, "REGI_NAME"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); m_options.emplace_back(&TRANSITION_DELAY, "TRANSITION_DELAY"); @@ -102,20 +107,16 @@ void ShinyHuntAutonomousRegi::program(SingleSwitchProgramEnvironment& env) const env.console.botbase().wait_for_all_requests(); // Detect shiny. - ShinyEncounterDetector::Detection detection; - { - ShinyEncounterDetector detector( - env.console, env.logger, - ShinyEncounterDetector::REGULAR_BATTLE, - std::chrono::seconds(30) - ); - detection = detector.detect(env); - } + ShinyDetection detection = detect_shiny_battle( + env, env.console, env.logger, + SHINY_BATTLE_REGULAR, + std::chrono::seconds(30) + ); if (tracker.process_result(detection)){ break; } - if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + if (detection == ShinyDetection::NO_BATTLE_MENU){ pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); tracker.run_away(); error = true; @@ -124,7 +125,9 @@ void ShinyHuntAutonomousRegi::program(SingleSwitchProgramEnvironment& env) const stats.log_stats(env, env.logger); - pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + if (GO_HOME_WHEN_DONE){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + } end_program_callback(); end_program_loop(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h index 53aa5f82f5..794cf7ae1f 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-Regi +/* Shiny Hunt Autonomous - Regi * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -32,6 +32,7 @@ class ShinyHuntAutonomousRegi : public SingleSwitchProgram{ uint64_t m_light_resets = 0; }; + BooleanCheckBox GO_HOME_WHEN_DONE; RegiSelector REGI_NAME; BooleanCheckBox REQUIRE_SQUARE; SectionDivider m_advanced_options; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp index 2941bba3e0..30380b4bbd 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-Regigigas2 +/* Shiny Hunt Autonomous - Regigigas2 * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -27,6 +27,10 @@ ShinyHuntAutonomousRegigigas2::ShinyHuntAutonomousRegigigas2() "SerialPrograms/ShinyHuntAutonomous-Regigigas2.md", "Automatically hunt for shiny Regigigas using video feedback." ) + , GO_HOME_WHEN_DONE( + "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", + true + ) , REVERSAL_PP( "Reversal PP:
The amount of Reversal PP you are saved with.", 24 @@ -47,6 +51,7 @@ ShinyHuntAutonomousRegigigas2::ShinyHuntAutonomousRegigigas2() "4 * 3600 * TICKS_PER_SECOND" ) { + m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&REVERSAL_PP, "REVERSAL_PP"); m_options.emplace_back(&REQUIRE_SQUARE, "REQUIRE_SQUARE"); m_options.emplace_back(&m_advanced_options, ""); @@ -123,23 +128,24 @@ void ShinyHuntAutonomousRegigigas2::program(SingleSwitchProgramEnvironment& env) env.logger.log("Starting Regigigas Encounter: " + tostr_u_commas(stats.encounters() + 1)); - pbf_mash_button(BUTTON_A, 10 * TICKS_PER_SECOND); + pbf_mash_button(BUTTON_A, 18 * TICKS_PER_SECOND); env.console.botbase().wait_for_all_requests(); - ShinyEncounterDetector::Detection detection; { - ShinyEncounterDetector detector( - env.console, env.logger, - ShinyEncounterDetector::RAID_BATTLE, - std::chrono::seconds(30) - ); - detection = detector.detect(env); + StartBattleDetector detector(env.console, std::chrono::seconds(30)); + detector.wait(env, env.logger); } + ShinyDetection detection = detect_shiny_battle( + env, env.console, env.logger, + SHINY_BATTLE_RAID, + std::chrono::seconds(30) + ); + if (tracker.process_result(detection)){ goto StopProgram; } - if (detection == ShinyEncounterDetector::NO_BATTLE_MENU || !tracker.run_away()){ + if (detection == ShinyDetection::NO_BATTLE_MENU || !tracker.run_away()){ stats.m_timeouts++; break; } @@ -160,7 +166,9 @@ void ShinyHuntAutonomousRegigigas2::program(SingleSwitchProgramEnvironment& env) StopProgram: stats.log_stats(env, env.logger); - pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + if (GO_HOME_WHEN_DONE){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + } end_program_callback(); end_program_loop(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h index e2c73656ed..da47aa9222 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-Regigigas2 +/* Shiny Hunt Autonomous - Regigigas2 * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -49,6 +49,7 @@ class ShinyHuntAutonomousRegigigas2 : public SingleSwitchProgram{ Logger& m_logger; }; + BooleanCheckBox GO_HOME_WHEN_DONE; SimpleInteger REVERSAL_PP; BooleanCheckBox REQUIRE_SQUARE; SectionDivider m_advanced_options; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp index 9baa48e644..ce83b6f54e 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-StrongSpawn +/* Shiny Hunt Autonomous - Strong Spawn * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -27,6 +27,10 @@ ShinyHuntAutonomousStrongSpawn::ShinyHuntAutonomousStrongSpawn() "SerialPrograms/ShinyHuntAutonomous-StrongSpawn.md", "Automatically hunt for shiny strong spawns using video feedback." ) + , GO_HOME_WHEN_DONE( + "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", + true + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -39,6 +43,7 @@ ShinyHuntAutonomousStrongSpawn::ShinyHuntAutonomousStrongSpawn() 1, 0, 11 ) { + m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); @@ -87,20 +92,16 @@ void ShinyHuntAutonomousStrongSpawn::program(SingleSwitchProgramEnvironment& env env.console.botbase().wait_for_all_requests(); // Detect shiny. - ShinyEncounterDetector::Detection detection; - { - ShinyEncounterDetector detector( - env.console, env.logger, - ShinyEncounterDetector::REGULAR_BATTLE, - std::chrono::seconds(60) - ); - detection = detector.detect(env); - } + ShinyDetection detection = detect_shiny_battle( + env, env.console, env.logger, + SHINY_BATTLE_REGULAR, + std::chrono::seconds(30) + ); if (tracker.process_result(detection)){ break; } - if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + if (detection == ShinyDetection::NO_BATTLE_MENU){ stats.m_timeouts++; tracker.run_away(); } @@ -108,7 +109,9 @@ void ShinyHuntAutonomousStrongSpawn::program(SingleSwitchProgramEnvironment& env stats.log_stats(env, env.logger); - pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + if (GO_HOME_WHEN_DONE){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + } end_program_callback(); end_program_loop(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h index 655d9ddbc7..63e2b83e68 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-StrongSpawn +/* Shiny Hunt Autonomous - StrongSpawn * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -36,6 +36,7 @@ class ShinyHuntAutonomousStrongSpawn : public SingleSwitchProgram{ virtual bool run_away() override; }; + BooleanCheckBox GO_HOME_WHEN_DONE; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; SimpleInteger TIME_ROLLBACK_HOURS; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp index 623ca20ea7..3aee721d25 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-SwordsOfJustice +/* Shiny Hunt Autonomous - Swords Of Justice * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -26,6 +26,10 @@ ShinyHuntAutonomousSwordsOfJustice::ShinyHuntAutonomousSwordsOfJustice() "SerialPrograms/ShinyHuntAutonomous-SwordsOfJustice.md", "Automatically hunt for shiny Sword of Justice using video feedback." ) + , GO_HOME_WHEN_DONE( + "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", + true + ) , AIRPLANE_MODE( "Airplane Mode:
Enable if airplane mode is on.", false @@ -46,6 +50,7 @@ ShinyHuntAutonomousSwordsOfJustice::ShinyHuntAutonomousSwordsOfJustice() 1, 0, 11 ) { + m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&AIRPLANE_MODE, "AIRPLANE_MODE"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); @@ -97,20 +102,16 @@ void ShinyHuntAutonomousSwordsOfJustice::program(SingleSwitchProgramEnvironment& env.console.botbase().wait_for_all_requests(); // Detect shiny. - ShinyEncounterDetector::Detection detection; - { - ShinyEncounterDetector detector( - env.console, env.logger, - ShinyEncounterDetector::REGULAR_BATTLE, - std::chrono::seconds(30) - ); - detection = detector.detect(env); - } + ShinyDetection detection = detect_shiny_battle( + env, env.console, env.logger, + SHINY_BATTLE_REGULAR, + std::chrono::seconds(30) + ); if (tracker.process_result(detection)){ break; } - if (detection == ShinyEncounterDetector::NO_BATTLE_MENU){ + if (detection == ShinyDetection::NO_BATTLE_MENU){ stats.m_timeouts++; pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); tracker.run_away(); @@ -119,7 +120,9 @@ void ShinyHuntAutonomousSwordsOfJustice::program(SingleSwitchProgramEnvironment& stats.log_stats(env, env.logger); - pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + if (GO_HOME_WHEN_DONE){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + } end_program_callback(); end_program_loop(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h index d284768f63..2c4cfd44fe 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h @@ -1,4 +1,4 @@ -/* ShinyHuntAutonomous-SwordsOfJustice +/* Shiny Hunt Autonomous - Swords Of Justice * * From: https://github.com/PokemonAutomation/Arduino-Source * @@ -31,6 +31,7 @@ class ShinyHuntAutonomousSwordsOfJustice : public SingleSwitchProgram{ uint64_t m_timeouts = 0; }; + BooleanCheckBox GO_HOME_WHEN_DONE; BooleanCheckBox AIRPLANE_MODE; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp new file mode 100644 index 0000000000..bdf760820d --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp @@ -0,0 +1,144 @@ +/* Shiny Hunt Autonomous - Whistling + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "Common/SwitchFramework/FrameworkSettings.h" +#include "Common/SwitchFramework/Switch_PushButtons.h" +#include "Common/PokemonSwSh/PokemonSettings.h" +#include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh_EncounterTracker.h" +#include "PokemonSwSh_ShinyHuntAutonomous-Whistling.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +ShinyHuntAutonomousWhistling::ShinyHuntAutonomousWhistling() + : SingleSwitchProgram( + FeedbackType::REQUIRED, PABotBaseLevel::PABOTBASE_12KB, + "Shiny Hunt Autonomous - Whistling", + "SerialPrograms/ShinyHuntAutonomous-Whistling.md", + "Stand in one place and whistle. Shiny hunt everything that attacks you using video feedback." + ) + , GO_HOME_WHEN_DONE( + "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", + true + ) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , EXIT_BATTLE_MASH_TIME( + "Exit Battle Time:
After running, wait this long to return to overworld.", + "6 * TICKS_PER_SECOND" + ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 + ) +{ + m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); + m_options.emplace_back(&m_advanced_options, ""); + m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); +} + + +std::string ShinyHuntAutonomousWhistling::Stats::stats() const{ + std::string str; + str += str_encounters(); + str += " - Timeouts: " + tostr_u_commas(m_timeouts); + str += " - Unexpected Battles: " + tostr_u_commas(m_unexpected_battles); + str += str_shinies(); + return str; +} + +void ShinyHuntAutonomousWhistling::program(SingleSwitchProgramEnvironment& env) const{ + grip_menu_connect_go_home(); + resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 200); + + const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; + uint32_t last_touch = system_clock(); + + Stats stats; + StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); + while (true){ + stats.log_stats(env, env.logger); + + // Touch the date. + if (TIME_ROLLBACK_HOURS > 0 && system_clock() - last_touch >= PERIOD){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + rollback_hours_from_home(TIME_ROLLBACK_HOURS, SETTINGS_TO_HOME_DELAY); + resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); + last_touch += PERIOD; + } + + env.console.botbase().wait_for_all_requests(); + { + StandardBattleMenuDetector menu(env.console); + StartBattleDetector detector(env.console, std::chrono::seconds(0)); + + // Detect start of battle. + bool unexpected = false; + QImage screen; + do{ + screen = env.console.video().snapshot(); + if (menu.detect(screen)){ + env.logger.log("ScreenChangeDetector: Unexpected battle menu.", Qt::red); + stats.m_unexpected_battles++; + unexpected = true; + break; + } + pbf_mash_button(BUTTON_LCLICK, 10); + pbf_move_right_joystick(192, 128, 10, 0); + env.console.botbase().wait_for_all_requests(); + }while (!detector.detect(screen)); + + if (unexpected){ + pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); + tracker.run_away(); + continue; + } + pbf_mash_button(BUTTON_B, 5 * TICKS_PER_SECOND); + } + + // Detect shiny. + ShinyDetection detection = detect_shiny_battle( + env, env.console, env.logger, + SHINY_BATTLE_REGULAR, + std::chrono::seconds(30) + ); + + if (tracker.process_result(detection)){ + break; + } + if (detection == ShinyDetection::NO_BATTLE_MENU){ + stats.m_timeouts++; + pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); + tracker.run_away(); + } + } + + stats.log_stats(env, env.logger); + + if (GO_HOME_WHEN_DONE){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + } + + end_program_callback(); + end_program_loop(); +} + + + +} +} +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h new file mode 100644 index 0000000000..fab716842a --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h @@ -0,0 +1,44 @@ +/* Shiny Hunt Autonomous - Whistling + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousWhistling_H +#define PokemonAutomation_PokemonSwSh_ShinyHuntAutonomousWhistling_H + +#include "CommonFramework/Options/SectionDivider.h" +#include "CommonFramework/Options/BooleanCheckBox.h" +#include "CommonFramework/Options/SimpleInteger.h" +#include "NintendoSwitch/Options/TimeExpression.h" +#include "NintendoSwitch/Framework/SingleSwitchProgram.h" +#include "PokemonSwSh_EncounterStats.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +class ShinyHuntAutonomousWhistling : public SingleSwitchProgram{ +public: + ShinyHuntAutonomousWhistling(); + + virtual void program(SingleSwitchProgramEnvironment& env) const override; + +private: + struct Stats : public EncounterStats{ + Stats() : EncounterStats(true) {} + virtual std::string stats() const override; + uint64_t m_timeouts = 0; + uint64_t m_unexpected_battles = 0; + }; + + BooleanCheckBox GO_HOME_WHEN_DONE; + SectionDivider m_advanced_options; + TimeExpression EXIT_BATTLE_MASH_TIME; + SimpleInteger TIME_ROLLBACK_HOURS; +}; + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp index 376f25fb9a..c5a9f32ea2 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp @@ -19,7 +19,7 @@ namespace PokemonSwSh{ ShinyHuntUnattendedIoATrade::ShinyHuntUnattendedIoATrade() : SingleSwitchProgram( FeedbackType::NONE, PABotBaseLevel::PABOTBASE_31KB, - "Shiny Hunt Unattended - IoATrade", + "Shiny Hunt Unattended - IoA Trade", "NativePrograms/ShinyHuntUnattended-IoATrade.md", "Hunt for shiny Isle of Armor trade. Stop when a shiny is found." ) @@ -64,7 +64,7 @@ void ShinyHuntUnattendedIoATrade::program(SingleSwitchProgramEnvironment& env) c env.logger.log("Starting Trade: " + tostr_u_commas(c + 1)); pbf_press_button(BUTTON_A, 10, 100); - pbf_press_button(BUTTON_A, 10, 50); + pbf_press_button(BUTTON_A, 10, 60); pbf_press_button(BUTTON_A, 10, 100); pbf_press_button(BUTTON_A, 10, 50); pbf_press_button(BUTTON_A, 10, POKEMON_TO_BOX_DELAY); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp index b8190f2ebc..bc70ad17ba 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp @@ -5,25 +5,30 @@ */ #include +#include "Common/Clientside/PrettyPrint.h" #include "Common/SwitchFramework/FrameworkSettings.h" #include "Common/SwitchFramework/Switch_PushButtons.h" #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "CommonFramework/Inference/ImageTools.h" +#include "CommonFramework/Inference/InferenceThrottler.h" #include "CommonFramework/Inference/FillGeometry.h" #include "CommonFramework/Inference/AnomalyDetector.h" #include "CommonFramework/Inference/ColorClustering.h" -#include "CommonFramework/Inference/FloatStatAccumulator.h" +#include "CommonFramework/Inference/StatAccumulator.h" #include "CommonFramework/Inference/TimeWindowStatTracker.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h" +#include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h" #include "PokemonSwSh_StartGame.h" #include "TestProgram.h" @@ -49,12 +54,108 @@ TestProgram::TestProgram() + + + + + + + void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ BotBase& botbase = env.console; VideoFeed& feed = env.console; // start_game_from_home_with_inference(env, env.logger, env.console, true, 0, 0, true); +// std::pair coord = get_direction(-3, -1.0); +// cout << "direction = " << (int)coord.first << ", " << (int)coord.second << endl; + +// Trajectory trajectory = get_trajectory_float(.09, .09); +// cout << trajectory.distance_in_ticks << " : " << (int)trajectory.joystick_x << "," << (int)trajectory.joystick_y << endl; + + +#if 0 + QImage image("square-test.png"); +// QImage image("test-1617471750423682600-O.png"); +// QImage image = feed.snapshot(); +// image.save("square-test0.png"); + FillMatrix matrix(image); + + BrightYellowLightFilterDebug filter; + matrix.apply_filter(image, filter); + + std::vector objects; + objects = find_all_objects(matrix, 1, true); + cout << "objects = " << objects.size() << endl; + + std::deque boxes; + for (const FillGeometry& object : objects){ + if (is_square2(image, matrix, object)){ + InferenceBox box = translate_to_parent(image, InferenceBox(0, 0, 1, 1), object.box); + box.color = Qt::green; + boxes.emplace_back(feed, box); + } + } + + env.wait(std::chrono::seconds(600)); +#endif + + +#if 0 + std::deque grid; + for (size_t r = 0; r < 10; r++){ + for (size_t c = 0; c < 10; c++){ + grid.emplace_back(feed, 0.1 * c, 0.1 * r, 0.1, 0.1); + } + } + + env.wait(std::chrono::seconds(5)); + + pbf_move_left_joystick(208, 16, 500, 0); + + env.wait(std::chrono::seconds(600)); +#endif + + + +#if 0 + QImage screen("test-161.png"); + + std::vector marks; + find_marks(screen, nullptr, &marks); + + for (const PixelBox& box : marks){ + cout << box.width() << " x " << box.height() << endl; + } +#endif + + +#if 0 + FillMatrix blue_matrix(screen); + BlueFilter blue_filter; + blue_matrix.apply_filter(blue, blue_filter); + blue.save("blue.png"); +#endif + + + +#if 0 + SummaryShinySymbolDetector detector(feed, env.logger); + + detector.wait_for_detection(env); + + env.wait(std::chrono::seconds(600)); +#endif + +#if 1 + detect_shiny_battle( + env, env.console, env.logger, + SHINY_BATTLE_REGULAR, + std::chrono::seconds(60) + ); +#endif + + #if 0 StandardBattleMenuDetector detector(feed); cout << "Battle Menu = " << detector.detect(feed.snapshot()) << endl; @@ -149,22 +250,6 @@ void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ #endif -#if 0 - SummaryShinySymbolDetector detector(feed, env.logger); - - detector.wait_for_detection(env); - - env.wait(std::chrono::seconds(600)); -#endif - -#if 1 - ShinyEncounterDetector detector( - feed, env.logger, - ShinyEncounterDetector::REGULAR_BATTLE, - std::chrono::seconds(60) - ); - detector.detect(env); -#endif #if 0 From 34661afcaefae688f170d0333fcc868d27c6c241 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Wed, 14 Apr 2021 22:51:44 -0500 Subject: [PATCH 12/13] Latest code dump. --- ClientSource/Connection/PABotBaseConnection.h | 1 + .../Connection/SerialConnectionWinAPI.h | 1 + Common/Compiler.h | 40 ++ Common/Qt/Options/FossilTableOption.cpp | 8 +- Common/Qt/Options/MultiHostTableOption.cpp | 80 +-- Common/Qt/Options/MultiHostTableOption.h | 26 +- Common/SwitchFramework/Switch_PushButtons.cpp | 1 + GeneratorSource/HexGenerator.pro | 4 + .../Source/Tools/PersistentSettings.cpp | 2 +- GeneratorSource/Source/UI/MainWindow.cpp | 8 +- SerialPrograms/SerialPrograms.pro | 50 +- .../Source/CommonFramework/Globals.cpp | 5 +- .../Source/CommonFramework/Globals.h | 3 +- .../Inference/AnomalyDetector.cpp | 3 +- .../Inference/ColorClustering.cpp | 8 +- .../Inference/FillGeometry.cpp | 38 +- .../CommonFramework/Inference/FillGeometry.h | 18 +- .../CommonFramework/Inference/FillMatrix.cpp | 29 +- .../CommonFramework/Inference/FillMatrix.h | 29 +- .../CommonFramework/Inference/ImageTools.cpp | 52 +- .../Inference/InferenceTypes.h | 22 +- .../CommonFramework/Options/ConfigOption.h | 1 + .../CommonFramework/Options/RandomCode.cpp | 4 +- .../CommonFramework/Panels/RightPanel.h | 1 + .../CommonFramework/PersistentSettings.cpp | 10 +- .../CommonFramework/PersistentSettings.h | 5 +- .../Tools/MainThreadDispatcher (disabled).cpp | 32 -- .../Tools/MainThreadDispatcher (disabled).h | 63 --- .../Tools/MessageLoggerUI (disabled).cpp | 10 - .../Tools/MessageLoggerUI (disabled).h | 20 - .../Tools/ProgramEnvironment.cpp | 81 ++++ .../Tools/ProgramEnvironment.h | 88 ++-- .../CommonFramework/Tools/StatsDatabase.cpp | 214 +++++++++ .../CommonFramework/Tools/StatsDatabase.h | 77 +++ .../CommonFramework/Tools/StatsTracking.cpp | 99 ++++ .../CommonFramework/Tools/StatsTracking.h | 44 ++ .../Widgets/CameraSelector.cpp | 28 +- .../CommonFramework/Widgets/CameraSelector.h | 8 +- .../CommonFramework/Widgets/ProgramList.cpp | 7 +- .../CommonFramework/Widgets/SettingList.cpp | 2 +- .../CommonFramework/Windows/MainWindow.cpp | 20 +- .../Framework/MultiSwitchProgram.cpp | 20 +- .../Framework/MultiSwitchProgram.h | 13 +- .../Framework/MultiSwitchSystem.cpp | 6 +- .../Framework/RunnableSwitchProgram.cpp | 50 +- .../Framework/RunnableSwitchProgram.h | 10 +- .../Framework/SingleSwitchProgram.cpp | 6 +- .../Framework/SingleSwitchProgram.h | 15 +- .../Inference/PokemonSwSh_BeamSetter.cpp | 9 +- .../Inference/PokemonSwSh_FishingDetector.cpp | 10 +- .../Inference/PokemonSwSh_FishingDetector.h | 2 +- .../Inference/PokemonSwSh_MarkFinder.cpp | 270 +++++++++-- .../Inference/PokemonSwSh_MarkFinder.h | 5 - .../PokemonSwSh_StartBattleDetector.cpp | 6 +- .../PokemonSwSh_StartBattleDetector.h | 2 +- .../PokemonSwSh_ShinyEncounterDetector.cpp | 24 +- .../PokemonSwSh_ShinyEncounterDetector.h | 3 +- .../ShinyDetection/PokemonSwSh_ShinyFilters.h | 4 +- .../PokemonSwSh_ShinyTrigger.cpp | 31 +- .../PokemonSwSh_SparkleTrigger.cpp | 80 +-- .../PokemonSwSh_SparkleTrigger.h | 2 +- .../PokemonSwSh_SquareDetector.cpp | 169 ++++--- .../PokemonSwSh_SquareTrigger.cpp | 193 +------- .../PokemonSwSh_SquareTrigger.h | 8 +- .../PokemonSwSh/Options/EggStepCount.cpp | 8 + .../Source/PokemonSwSh/Options/EggStepCount.h | 8 +- .../PokemonSwSh_DateSpam-BerryFarmer.cpp | 2 +- ...emonSwSh_DateSpam-DailyHighlightFarmer.cpp | 2 +- .../PokemonSwSh_DateSpam-LotoFarmer.cpp | 2 +- .../PokemonSwSh_DateSpam-StowOnSideFarmer.cpp | 2 +- .../PokemonSwSh_DateSpam-WattFarmer.cpp | 2 +- .../DenHunting/PokemonSwSh_DaySkipperEU.cpp | 2 +- .../PokemonSwSh_DaySkipperJPN-7.8k.cpp | 4 +- .../DenHunting/PokemonSwSh_DaySkipperJPN.cpp | 2 +- .../DenHunting/PokemonSwSh_DaySkipperUS.cpp | 2 +- .../PokemonSwSh_PurpleBeamFinder.cpp | 61 ++- .../DenHunting/PokemonSwSh_PurpleBeamFinder.h | 3 + .../PokemonSwSh_EggCombinedShared.h | 2 +- .../EggPrograms/PokemonSwSh_EggFetcher2.cpp | 4 +- .../EggPrograms/PokemonSwSh_EggHelpers.h | 1 + .../PokemonSwSh_GodEggDuplication.cpp | 4 +- .../PokemonSwSh_GodEggItemDupe.cpp | 4 +- .../PokemonSwSh_AutoHost-MultiGame.cpp | 14 +- .../Hosting/PokemonSwSh_AutoHost-Rolling.cpp | 12 +- .../Hosting/PokemonSwSh_DenRoller.cpp | 2 +- .../Programs/Hosting/PokemonSwSh_DenTools.h | 1 + .../PokemonSwSh_OverworldTrajectory.cpp | 98 ++++ .../PokemonSwSh_OverworldTrajectory.h | 28 ++ .../PokemonSwSh_RaidItemFarmerOKHO.cpp | 2 +- .../Programs/PokemonSwSh_StartGame.cpp | 25 +- .../Programs/PokemonSwSh_StartGame.h | 2 - .../PokemonSwSh/Programs/ReleaseHelpers.h | 1 + .../PokemonSwSh_EncounterStats.cpp | 72 --- .../ShinyHunting/PokemonSwSh_EncounterStats.h | 48 -- .../PokemonSwSh_EncounterTracker.cpp | 2 +- .../PokemonSwSh_EncounterTracker.h | 6 +- .../PokemonSwSh_ShinyHunt-Regi.cpp | 12 +- ...emonSwSh_ShinyHuntAutonomous-BerryTree.cpp | 29 +- ...okemonSwSh_ShinyHuntAutonomous-BerryTree.h | 8 +- ...okemonSwSh_ShinyHuntAutonomous-Fishing.cpp | 39 +- .../PokemonSwSh_ShinyHuntAutonomous-Fishing.h | 10 +- ...kemonSwSh_ShinyHuntAutonomous-IoATrade.cpp | 33 +- ...PokemonSwSh_ShinyHuntAutonomous-IoATrade.h | 8 +- ...emonSwSh_ShinyHuntAutonomous-Overworld.cpp | 454 +++++++++--------- ...okemonSwSh_ShinyHuntAutonomous-Overworld.h | 30 +- .../PokemonSwSh_ShinyHuntAutonomous-Regi.cpp | 31 +- .../PokemonSwSh_ShinyHuntAutonomous-Regi.h | 8 +- ...monSwSh_ShinyHuntAutonomous-Regigigas2.cpp | 44 +- ...kemonSwSh_ShinyHuntAutonomous-Regigigas2.h | 12 +- ...onSwSh_ShinyHuntAutonomous-StrongSpawn.cpp | 35 +- ...emonSwSh_ShinyHuntAutonomous-StrongSpawn.h | 10 +- ...Sh_ShinyHuntAutonomous-SwordsOfJustice.cpp | 31 +- ...SwSh_ShinyHuntAutonomous-SwordsOfJustice.h | 8 +- ...emonSwSh_ShinyHuntAutonomous-Whistling.cpp | 36 +- ...okemonSwSh_ShinyHuntAutonomous-Whistling.h | 9 +- .../ShinyHunting/PokemonSwSh_ShinyHuntTools.h | 1 + ...kemonSwSh_ShinyHuntUnattended-IoATrade.cpp | 2 +- .../PokemonSwSh_ShinyHuntUnattended-Regi.cpp | 2 +- ...monSwSh_ShinyHuntUnattended-Regigigas2.cpp | 2 +- ...onSwSh_ShinyHuntUnattended-StrongSpawn.cpp | 2 +- ...Sh_ShinyHuntUnattended-SwordsOfJustice.cpp | 2 +- .../PokemonSwSh/Programs/TestProgram.cpp | 54 ++- .../Source/PokemonSwSh/ShinyHuntTracker.cpp | 48 ++ .../Source/PokemonSwSh/ShinyHuntTracker.h | 36 ++ 124 files changed, 2261 insertions(+), 1343 deletions(-) create mode 100644 Common/Compiler.h delete mode 100644 SerialPrograms/Source/CommonFramework/Tools/MainThreadDispatcher (disabled).cpp delete mode 100644 SerialPrograms/Source/CommonFramework/Tools/MainThreadDispatcher (disabled).h delete mode 100644 SerialPrograms/Source/CommonFramework/Tools/MessageLoggerUI (disabled).cpp delete mode 100644 SerialPrograms/Source/CommonFramework/Tools/MessageLoggerUI (disabled).h create mode 100644 SerialPrograms/Source/CommonFramework/Tools/ProgramEnvironment.cpp create mode 100644 SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.cpp create mode 100644 SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.h create mode 100644 SerialPrograms/Source/CommonFramework/Tools/StatsTracking.cpp create mode 100644 SerialPrograms/Source/CommonFramework/Tools/StatsTracking.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.h delete mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp delete mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h create mode 100644 SerialPrograms/Source/PokemonSwSh/ShinyHuntTracker.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/ShinyHuntTracker.h diff --git a/ClientSource/Connection/PABotBaseConnection.h b/ClientSource/Connection/PABotBaseConnection.h index cf65fc0bfc..c11c29c282 100644 --- a/ClientSource/Connection/PABotBaseConnection.h +++ b/ClientSource/Connection/PABotBaseConnection.h @@ -17,6 +17,7 @@ #include #include #include +#include "Common/Compiler.h" #include "Common/MessageProtocol.h" #include "ClientSource/Libraries/Compiler.h" #include "BotBase.h" diff --git a/ClientSource/Connection/SerialConnectionWinAPI.h b/ClientSource/Connection/SerialConnectionWinAPI.h index 8dc7eed8c5..4e2bb6540f 100644 --- a/ClientSource/Connection/SerialConnectionWinAPI.h +++ b/ClientSource/Connection/SerialConnectionWinAPI.h @@ -11,6 +11,7 @@ #include #include #include +#include "Common/Compiler.h" #include "Common/Clientside/Unicode.h" #include "Common/Clientside/SpinLock.h" #include "ClientSource/Libraries/Logging.h" diff --git a/Common/Compiler.h b/Common/Compiler.h new file mode 100644 index 0000000000..7a253a56f5 --- /dev/null +++ b/Common/Compiler.h @@ -0,0 +1,40 @@ +/* Compiler Specifics + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_Compiler_H +#define PokemonAutomation_Compiler_H + +#include + +namespace PokemonAutomation{ + + + +#if 0 +#elif _MSC_VER + +using ssize_t = ptrdiff_t; + +#pragma warning(disable:4100) // Unreferenced Formal Parameter + + + +#elif __GNUC__ + +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" + + +#else +#error "Unsupported Compiler." +#endif + + + + +} +#endif diff --git a/Common/Qt/Options/FossilTableOption.cpp b/Common/Qt/Options/FossilTableOption.cpp index ad33fa1fb9..c71382bfb8 100644 --- a/Common/Qt/Options/FossilTableOption.cpp +++ b/Common/Qt/Options/FossilTableOption.cpp @@ -156,20 +156,20 @@ void FossilTableOptionUI::replace_table(){ m_table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); size_t rows = m_value.m_current.size(); - m_table->setRowCount(m_value.m_current.size() + 1); + m_table->setRowCount((int)(m_value.m_current.size() + 1)); for (size_t c = 0; c < rows; c++){ const auto& item = m_value.m_current[c]; m_index_table.emplace_back(new int); - add_row(c, item, *m_index_table.back()); + add_row((int)c, item, *m_index_table.back()); } QPushButton* button = new QPushButton(m_table); button->setText("Add Row"); - m_table->setCellWidget(m_value.m_current.size(), 0, button); + m_table->setCellWidget((int)m_value.m_current.size(), 0, button); connect( button, &QPushButton::clicked, this, [&](bool){ - int index = m_index_table.size(); + int index = (int)m_index_table.size(); // Update data vector. m_value.m_current.emplace_back( diff --git a/Common/Qt/Options/MultiHostTableOption.cpp b/Common/Qt/Options/MultiHostTableOption.cpp index e94d604d85..05b559fa01 100644 --- a/Common/Qt/Options/MultiHostTableOption.cpp +++ b/Common/Qt/Options/MultiHostTableOption.cpp @@ -177,21 +177,21 @@ void MultiHostTableOptionUI::replace_table(){ size_t rows = m_value.m_current.size(); - m_table->setRowCount(m_value.m_current.size() + 1); + m_table->setRowCount((int)(m_value.m_current.size() + 1)); for (size_t c = 0; c < rows; c++){ const auto& item = m_value.m_current[c]; - m_index_table.emplace_back(new int); - add_row(c, item, *m_index_table.back()); + m_index_table.emplace_back(new size_t); + add_row((int)c, item, *m_index_table.back()); } QPushButton* button = new QPushButton(m_table); button->setText("Add Row"); - m_table->setCellWidget(m_value.m_current.size(), 0, button); + m_table->setCellWidget((int)m_value.m_current.size(), 0, button); update_table_height(); connect( button, &QPushButton::clicked, this, [&](bool){ - int index = m_index_table.size(); + size_t index = m_index_table.size(); // Update data vector. m_value.m_current.emplace_back( @@ -206,34 +206,34 @@ void MultiHostTableOptionUI::replace_table(){ const auto& item = m_value.m_current.back(); // Update index vector. - m_index_table.emplace_back(new int); + m_index_table.emplace_back(new size_t); // Update UI. - m_table->insertRow(index); + m_table->insertRow((int)index); add_row(index, item, *m_index_table.back()); update_table_height(); } ); } -void MultiHostTableOptionUI::add_row(int row, const MultiHostTableOption::GameSlot& game, int& index_ref){ +void MultiHostTableOptionUI::add_row(size_t row, const MultiHostTableOption::GameSlot& game, size_t& index_ref){ index_ref = row; - m_table->setCellWidget(row, 0, make_game_slot_box(*m_table, index_ref, game.game_slot - 1)); - m_table->setCellWidget(row, 1, make_user_slot_box(*m_table, index_ref, game.user_slot - 1)); - m_table->setCellWidget(row, 2, make_skips_box(*m_table, index_ref, game.skips)); - m_table->setCellWidget(row, 3, make_backup_save_box(*m_table, index_ref, game.backup_save)); - m_table->setCellWidget(row, 4, make_catchable_box(*m_table, index_ref, game.always_catchable)); - m_table->setCellWidget(row, 5, make_accept_FRs_box(*m_table, index_ref, game.accept_FRs)); - m_table->setCellWidget(row, 6, make_move_slot_box(*m_table, index_ref, game.move_slot)); - m_table->setCellWidget(row, 7, make_dynamax_box(*m_table, index_ref, game.dynamax)); - m_table->setCellWidget(row, 8, make_delay_box(*m_table, index_ref, game.post_raid_delay)); - m_table->setCellWidget(row, 9, make_insert_button(*m_table, index_ref)); - m_table->setCellWidget(row, 10, make_remove_button(*m_table, index_ref)); + m_table->setCellWidget((int)row, 0, make_game_slot_box(*m_table, index_ref, game.game_slot - 1)); + m_table->setCellWidget((int)row, 1, make_user_slot_box(*m_table, index_ref, game.user_slot - 1)); + m_table->setCellWidget((int)row, 2, make_skips_box(*m_table, index_ref, game.skips)); + m_table->setCellWidget((int)row, 3, make_backup_save_box(*m_table, index_ref, game.backup_save)); + m_table->setCellWidget((int)row, 4, make_catchable_box(*m_table, index_ref, game.always_catchable)); + m_table->setCellWidget((int)row, 5, make_accept_FRs_box(*m_table, index_ref, game.accept_FRs)); + m_table->setCellWidget((int)row, 6, make_move_slot_box(*m_table, index_ref, game.move_slot)); + m_table->setCellWidget((int)row, 7, make_dynamax_box(*m_table, index_ref, game.dynamax)); + m_table->setCellWidget((int)row, 8, make_delay_box(*m_table, index_ref, game.post_raid_delay)); + m_table->setCellWidget((int)row, 9, make_insert_button(*m_table, index_ref)); + m_table->setCellWidget((int)row, 10, make_remove_button(*m_table, index_ref)); } -QComboBox* MultiHostTableOptionUI::make_game_slot_box(QWidget& parent, int& row, int slot){ +QComboBox* MultiHostTableOptionUI::make_game_slot_box(QWidget& parent, size_t& row, size_t slot){ QComboBox* box = new QComboBox(&parent); box->addItem("Slot 1"); box->addItem("Slot 2"); - box->setCurrentIndex(slot); + box->setCurrentIndex((int)slot); connect( box, static_cast(&QComboBox::currentIndexChanged), this, [&](int index){ @@ -242,7 +242,7 @@ QComboBox* MultiHostTableOptionUI::make_game_slot_box(QWidget& parent, int& row, ); return box; } -QComboBox* MultiHostTableOptionUI::make_user_slot_box(QWidget& parent, int& row, int slot){ +QComboBox* MultiHostTableOptionUI::make_user_slot_box(QWidget& parent, size_t& row, size_t slot){ QComboBox* box = new QComboBox(&parent); box->addItem("User 1"); box->addItem("User 2"); @@ -252,7 +252,7 @@ QComboBox* MultiHostTableOptionUI::make_user_slot_box(QWidget& parent, int& row, box->addItem("User 6"); box->addItem("User 7"); box->addItem("User 8"); - box->setCurrentIndex(slot); + box->setCurrentIndex((int)slot); connect( box, static_cast(&QComboBox::currentIndexChanged), this, [&](int index){ @@ -261,7 +261,7 @@ QComboBox* MultiHostTableOptionUI::make_user_slot_box(QWidget& parent, int& row, ); return box; } -QLineEdit* MultiHostTableOptionUI::make_skips_box(QWidget& parent, int& row, int skips){ +QLineEdit* MultiHostTableOptionUI::make_skips_box(QWidget& parent, size_t& row, size_t skips){ QLineEdit* box = new QLineEdit(&parent); QIntValidator* validator = new QIntValidator(0, 965, box); box->setValidator(validator); @@ -284,7 +284,7 @@ QLineEdit* MultiHostTableOptionUI::make_skips_box(QWidget& parent, int& row, int ); return box; } -QWidget* MultiHostTableOptionUI::make_backup_save_box(QWidget& parent, int& row, bool backup_save){ +QWidget* MultiHostTableOptionUI::make_backup_save_box(QWidget& parent, size_t& row, bool backup_save){ QWidget* widget = new QWidget(&parent); QHBoxLayout* layout = new QHBoxLayout(widget); layout->setAlignment(Qt::AlignHCenter); @@ -300,7 +300,7 @@ QWidget* MultiHostTableOptionUI::make_backup_save_box(QWidget& parent, int& row, ); return widget; } -QWidget* MultiHostTableOptionUI::make_catchable_box(QWidget& parent, int& row, bool always_catchable){ +QWidget* MultiHostTableOptionUI::make_catchable_box(QWidget& parent, size_t& row, bool always_catchable){ QWidget* widget = new QWidget(&parent); QHBoxLayout* layout = new QHBoxLayout(widget); layout->setAlignment(Qt::AlignHCenter); @@ -316,7 +316,7 @@ QWidget* MultiHostTableOptionUI::make_catchable_box(QWidget& parent, int& row, b ); return widget; } -QWidget* MultiHostTableOptionUI::make_accept_FRs_box(QWidget& parent, int& row, bool accept_FRs){ +QWidget* MultiHostTableOptionUI::make_accept_FRs_box(QWidget& parent, size_t& row, bool accept_FRs){ QWidget* widget = new QWidget(&parent); QHBoxLayout* layout = new QHBoxLayout(widget); layout->setAlignment(Qt::AlignHCenter); @@ -332,14 +332,14 @@ QWidget* MultiHostTableOptionUI::make_accept_FRs_box(QWidget& parent, int& row, ); return widget; } -QComboBox* MultiHostTableOptionUI::make_move_slot_box(QWidget& parent, int& row, int move_slot){ +QComboBox* MultiHostTableOptionUI::make_move_slot_box(QWidget& parent, size_t& row, size_t move_slot){ QComboBox* box = new QComboBox(&parent); box->addItem("None"); box->addItem("Slot 1"); box->addItem("Slot 2"); box->addItem("Slot 3"); box->addItem("Slot 4"); - box->setCurrentIndex(move_slot); + box->setCurrentIndex((int)move_slot); connect( box, static_cast(&QComboBox::currentIndexChanged), this, [&](int index){ @@ -348,7 +348,7 @@ QComboBox* MultiHostTableOptionUI::make_move_slot_box(QWidget& parent, int& row, ); return box; } -QWidget* MultiHostTableOptionUI::make_dynamax_box(QWidget& parent, int& row, bool dynamax){ +QWidget* MultiHostTableOptionUI::make_dynamax_box(QWidget& parent, size_t& row, bool dynamax){ QWidget* widget = new QWidget(&parent); QHBoxLayout* layout = new QHBoxLayout(widget); layout->setAlignment(Qt::AlignHCenter); @@ -364,7 +364,7 @@ QWidget* MultiHostTableOptionUI::make_dynamax_box(QWidget& parent, int& row, boo ); return widget; } -QLineEdit* MultiHostTableOptionUI::make_delay_box(QWidget& parent, int& row, const QString& post_raid_delay){ +QLineEdit* MultiHostTableOptionUI::make_delay_box(QWidget& parent, size_t& row, const QString& post_raid_delay){ QLineEdit* box = new QLineEdit(&parent); box->setText(post_raid_delay); // box->setMaxLength(3); @@ -381,7 +381,7 @@ QLineEdit* MultiHostTableOptionUI::make_delay_box(QWidget& parent, int& row, con ); return box; } -QPushButton* MultiHostTableOptionUI::make_insert_button(QWidget& parent, int& row){ +QPushButton* MultiHostTableOptionUI::make_insert_button(QWidget& parent, size_t& row){ QPushButton* button = new QPushButton(&parent); QFont font; font.setBold(true); @@ -395,7 +395,7 @@ QPushButton* MultiHostTableOptionUI::make_insert_button(QWidget& parent, int& ro connect( button, &QPushButton::clicked, this, [&](bool){ - int index = row; + size_t index = row; MultiHostTableOption::GameSlot item{ 1, 1, 3, false, true, @@ -408,21 +408,21 @@ QPushButton* MultiHostTableOptionUI::make_insert_button(QWidget& parent, int& ro m_value.m_current.insert(m_value.m_current.begin() + index, item); // Update index vector. - m_index_table.insert(m_index_table.begin() + index, std::unique_ptr(new int(index))); - int& new_row = *m_index_table[index]; + m_index_table.insert(m_index_table.begin() + index, std::unique_ptr(new size_t(index))); + size_t& new_row = *m_index_table[index]; for (size_t c = 0; c < m_index_table.size(); c++){ - *m_index_table[c] = (int)c; + *m_index_table[c] = c; } // Update UI. - m_table->insertRow(index); + m_table->insertRow((int)index); add_row(index, item, new_row); update_table_height(); } ); return button; } -QPushButton* MultiHostTableOptionUI::make_remove_button(QWidget& parent, int& row){ +QPushButton* MultiHostTableOptionUI::make_remove_button(QWidget& parent, size_t& row){ QPushButton* button = new QPushButton(&parent); QFont font; font.setBold(true); @@ -432,7 +432,7 @@ QPushButton* MultiHostTableOptionUI::make_remove_button(QWidget& parent, int& ro connect( button, &QPushButton::clicked, this, [&](bool){ - int index = row; + size_t index = row; // Update data vector. m_index_table.erase(m_index_table.begin() + index); @@ -444,7 +444,7 @@ QPushButton* MultiHostTableOptionUI::make_remove_button(QWidget& parent, int& ro m_value.m_current.erase(m_value.m_current.begin() + index); // Update UI. - m_table->removeRow(index); + m_table->removeRow((int)index); update_table_height(); } ); diff --git a/Common/Qt/Options/MultiHostTableOption.h b/Common/Qt/Options/MultiHostTableOption.h index 8a44e36014..954a2388f6 100644 --- a/Common/Qt/Options/MultiHostTableOption.h +++ b/Common/Qt/Options/MultiHostTableOption.h @@ -73,25 +73,25 @@ class MultiHostTableOptionUI : public QWidget{ private: void replace_table(); - void add_row(int row, const MultiHostTableOption::GameSlot& game, int& index_ref); - QComboBox* make_game_slot_box(QWidget& parent, int& row, int slot); - QComboBox* make_user_slot_box(QWidget& parent, int& row, int slot); - QLineEdit* make_skips_box(QWidget& parent, int& row, int skips); - QWidget* make_backup_save_box(QWidget& parent, int& row, bool backup_save); - QWidget* make_catchable_box(QWidget& parent, int& row, bool always_catchable); - QWidget* make_accept_FRs_box(QWidget& parent, int& row, bool accept_FRs); - QComboBox* make_move_slot_box(QWidget& parent, int& row, int move_slot); - QWidget* make_dynamax_box(QWidget& parent, int& row, bool dynamax); - QLineEdit* make_delay_box(QWidget& parent, int& row, const QString& post_raid_delay); - QPushButton* make_insert_button(QWidget& parent, int& row); - QPushButton* make_remove_button(QWidget& parent, int& row); + void add_row(size_t row, const MultiHostTableOption::GameSlot& game, size_t& index_ref); + QComboBox* make_game_slot_box (QWidget& parent, size_t& row, size_t slot); + QComboBox* make_user_slot_box (QWidget& parent, size_t& row, size_t slot); + QLineEdit* make_skips_box (QWidget& parent, size_t& row, size_t skips); + QWidget* make_backup_save_box(QWidget& parent, size_t& row, bool backup_save); + QWidget* make_catchable_box (QWidget& parent, size_t& row, bool always_catchable); + QWidget* make_accept_FRs_box (QWidget& parent, size_t& row, bool accept_FRs); + QComboBox* make_move_slot_box (QWidget& parent, size_t& row, size_t move_slot); + QWidget* make_dynamax_box (QWidget& parent, size_t& row, bool dynamax); + QLineEdit* make_delay_box (QWidget& parent, size_t& row, const QString& post_raid_delay); + QPushButton* make_insert_button (QWidget& parent, size_t& row); + QPushButton* make_remove_button (QWidget& parent, size_t& row); void update_table_height(); private: MultiHostTableOption& m_value; QTableWidget* m_table; - std::vector> m_index_table; + std::vector> m_index_table; }; diff --git a/Common/SwitchFramework/Switch_PushButtons.cpp b/Common/SwitchFramework/Switch_PushButtons.cpp index 4a02a0dfe2..446edb1616 100644 --- a/Common/SwitchFramework/Switch_PushButtons.cpp +++ b/Common/SwitchFramework/Switch_PushButtons.cpp @@ -4,6 +4,7 @@ * */ +#include "Common/Compiler.h" #include "Common/MessageProtocol.h" #include "Common/SwitchFramework/FrameworkSettings.h" #include "Common/SwitchFramework/Switch_PushButtons.h" diff --git a/GeneratorSource/HexGenerator.pro b/GeneratorSource/HexGenerator.pro index 79b379c5dd..c668719398 100644 --- a/GeneratorSource/HexGenerator.pro +++ b/GeneratorSource/HexGenerator.pro @@ -16,6 +16,10 @@ INCLUDEPATH += Source/ # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +win32-msvc{ + QMAKE_CXXFLAGS += /std:c++latest +} + SOURCES += \ ../Common/Clientside/PrettyPrint.cpp \ ../Common/Clientside/Unicode.cpp \ diff --git a/GeneratorSource/Source/Tools/PersistentSettings.cpp b/GeneratorSource/Source/Tools/PersistentSettings.cpp index f2b2368bf0..03c4373a85 100644 --- a/GeneratorSource/Source/Tools/PersistentSettings.cpp +++ b/GeneratorSource/Source/Tools/PersistentSettings.cpp @@ -20,7 +20,7 @@ using std::endl; namespace PokemonAutomation{ -const QString VERSION = "v0.4.1"; +const QString VERSION = "v0.4.2"; const QString DISCORD = "https://discord.gg/cQ4gWxN"; const QString GITHUB_REPO = "https://github.com/PokemonAutomation/SwSh-Arduino"; diff --git a/GeneratorSource/Source/UI/MainWindow.cpp b/GeneratorSource/Source/UI/MainWindow.cpp index 4fd65e4ba3..38b1324775 100644 --- a/GeneratorSource/Source/UI/MainWindow.cpp +++ b/GeneratorSource/Source/UI/MainWindow.cpp @@ -51,7 +51,7 @@ MainWindow::MainWindow(QWidget* parent) QHBoxLayout* hbox = new QHBoxLayout(m_centralwidget); QVBoxLayout* left = new QVBoxLayout(); - hbox->addLayout(left, 2); + hbox->addLayout(left, 0); left->setAlignment(Qt::AlignTop); left->addWidget(new QLabel("Device and Program:", this)); @@ -76,11 +76,11 @@ MainWindow::MainWindow(QWidget* parent) m_settings_list->text_width() ); #else - int width = 300; +// int width = 300; #endif // cout << "Width = " << width << endl; - m_program_list->setMaximumWidth(width); - m_settings_list->setMaximumWidth(width); +// m_program_list->setMaximumWidth(width); +// m_settings_list->setMaximumWidth(width); QHBoxLayout* support = new QHBoxLayout(); left->addLayout(support); diff --git a/SerialPrograms/SerialPrograms.pro b/SerialPrograms/SerialPrograms.pro index a9fb0601be..cf25af9fc6 100644 --- a/SerialPrograms/SerialPrograms.pro +++ b/SerialPrograms/SerialPrograms.pro @@ -6,27 +6,37 @@ QT += multimediawidgets greaterThan(QT_MAJOR_VERSION, 4): QT += widgets -CONFIG += c++14 INCLUDEPATH += ../ INCLUDEPATH += Source/ CONFIG += force_debug_info -QMAKE_CXXFLAGS += -msse4.2 -QMAKE_CXXFLAGS += -Wnarrowing -QMAKE_CXXFLAGS += -Wno-unused-parameter -QMAKE_CXXFLAGS += -Wno-unused-function -QMAKE_CXXFLAGS += -Wno-missing-field-initializers - # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 -# Link Tesseract C API for Windows. -DEFINES += TESS_IMPORTS -DEFINES += WIN32 -LIBS += ../SerialPrograms/libtesseractc.lib +win32-g++{ + CONFIG += c++14 + + QMAKE_CXXFLAGS += -msse4.2 +# QMAKE_CXXFLAGS += -Wnarrowing +# QMAKE_CXXFLAGS += -Wno-unused-parameter +# QMAKE_CXXFLAGS += -Wno-unused-function +# QMAKE_CXXFLAGS += -Wno-missing-field-initializers + + DEFINES += TESS_IMPORTS + DEFINES += WIN32 + LIBS += ../SerialPrograms/libtesseractc.lib +} +win32-msvc{ + QMAKE_CXXFLAGS += /std:c++latest + + + DEFINES += TESS_IMPORTS + DEFINES += WIN32 + LIBS += ../SerialPrograms/libtesseractc.lib +} SOURCES += \ @@ -73,6 +83,9 @@ SOURCES += \ Source/CommonFramework/Panels/SettingsPanel.cpp \ Source/CommonFramework/PersistentSettings.cpp \ Source/CommonFramework/Tools/BotBaseHandle.cpp \ + Source/CommonFramework/Tools/ProgramEnvironment.cpp \ + Source/CommonFramework/Tools/StatsDatabase.cpp \ + Source/CommonFramework/Tools/StatsTracking.cpp \ Source/CommonFramework/Widgets/CameraSelector.cpp \ Source/CommonFramework/Widgets/ProgramList.cpp \ Source/CommonFramework/Widgets/SerialSelector.cpp \ @@ -119,8 +132,8 @@ SOURCES += \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_SurpriseTrade.cpp \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_TradeBot.cpp \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_TurboA.cpp \ + Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.cpp \ Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp \ - Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_MultiGameFossil.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHunt-Regi.cpp \ @@ -163,7 +176,8 @@ SOURCES += \ Source/PokemonSwSh/Programs/QoLMacros/PokemonSwSh_FastCodeEntry.cpp \ Source/PokemonSwSh/Programs/QoLMacros/PokemonSwSh_FriendSearchDisconnect.cpp \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.cpp \ - Source/PokemonSwSh/Programs/TestProgram.cpp + Source/PokemonSwSh/Programs/TestProgram.cpp \ + Source/PokemonSwSh/ShinyHuntTracker.cpp HEADERS += \ ../ClientSource/Connection/BotBase.h \ @@ -181,6 +195,7 @@ HEADERS += \ ../Common/Clientside/PrettyPrint.h \ ../Common/Clientside/SpinLock.h \ ../Common/Clientside/Unicode.h \ + ../Common/Compiler.h \ ../Common/MessageProtocol.h \ ../Common/PokemonSwSh/PokemonProgramIDs.h \ ../Common/PokemonSwSh/PokemonSettings.h \ @@ -228,9 +243,13 @@ HEADERS += \ Source/CommonFramework/Panels/RightPanel.h \ Source/CommonFramework/Panels/SettingsPanel.h \ Source/CommonFramework/PersistentSettings.h \ + Source/CommonFramework/Tesseract/capi.h \ + Source/CommonFramework/Tesseract/platform.h \ Source/CommonFramework/Tools/ConsoleHandle.h \ Source/CommonFramework/Tools/Logger.h \ Source/CommonFramework/Tools/ProgramEnvironment.h \ + Source/CommonFramework/Tools/StatsDatabase.h \ + Source/CommonFramework/Tools/StatsTracking.h \ Source/CommonFramework/Tools/VideoFeed.h \ Source/CommonFramework/Tools/BotBaseHandle.h \ Source/CommonFramework/Widgets/CameraSelector.h \ @@ -287,8 +306,8 @@ HEADERS += \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_SurpriseTrade.h \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_TradeBot.h \ Source/PokemonSwSh/Programs/BasicPrograms/PokemonSwSh_TurboA.h \ + Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.h \ Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h \ - Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_MultiGameFossil.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHunt-Regi.h \ @@ -337,7 +356,8 @@ HEADERS += \ Source/PokemonSwSh/Programs/QoLMacros/PokemonSwSh_FriendSearchDisconnect.h \ Source/PokemonSwSh/Programs/ReleaseHelpers.h \ Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.h \ - Source/PokemonSwSh/Programs/TestProgram.h + Source/PokemonSwSh/Programs/TestProgram.h \ + Source/PokemonSwSh/ShinyHuntTracker.h # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin diff --git a/SerialPrograms/Source/CommonFramework/Globals.cpp b/SerialPrograms/Source/CommonFramework/Globals.cpp index 5d984c35fc..a2ee68cc72 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.cpp +++ b/SerialPrograms/Source/CommonFramework/Globals.cpp @@ -9,8 +9,9 @@ namespace PokemonAutomation{ -const QString VERSION = "v0.5.3"; -const QString DISCORD = "https://discord.gg/cQ4gWxN"; +const QString VERSION = "v0.5.9"; +const QString DISCORD_LINK = "discord.gg/cQ4gWxN"; +const QString DISCORD_LINK_FULL = "https://" + DISCORD_LINK; const QString GITHUB_REPO = "https://github.com/PokemonAutomation/SwSh-Arduino"; const QString STRING_POKEMON = QString("Pok") + QChar(0xe9) + "mon"; diff --git a/SerialPrograms/Source/CommonFramework/Globals.h b/SerialPrograms/Source/CommonFramework/Globals.h index c27767d14c..08343b8d29 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.h +++ b/SerialPrograms/Source/CommonFramework/Globals.h @@ -13,7 +13,8 @@ namespace PokemonAutomation{ extern const QString VERSION; -extern const QString DISCORD; +extern const QString DISCORD_LINK; +extern const QString DISCORD_LINK_FULL; extern const QString GITHUB_REPO; const auto SERIAL_REFRESH_RATE = std::chrono::milliseconds(1000); diff --git a/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.cpp b/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.cpp index a766aa26e5..ef50c89690 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/AnomalyDetector.cpp @@ -5,6 +5,7 @@ */ #include +#include #include "AnomalyDetector.h" namespace PokemonAutomation{ @@ -85,7 +86,7 @@ double TimeNormalizedDeltaAnomalyDetector::push( } auto time_diff = timestamp - m_window.back().timestamp; - uint32_t millis = std::chrono::duration_cast(time_diff).count(); + uint32_t millis = (uint32_t)std::chrono::duration_cast(time_diff).count(); x /= millis; x = std::min(x, m_max_value); diff --git a/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.cpp b/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.cpp index a1b6592173..12a66df60a 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/ColorClustering.cpp @@ -51,8 +51,8 @@ double cluster_fit_2( QRgb color0, PixelEuclideanStatAccumulator& cluster0, QRgb color1, PixelEuclideanStatAccumulator& cluster1 ){ - int width = image.width(); - int height = image.height(); + pxint_t width = image.width(); + pxint_t height = image.height(); FloatPixel f0(color0); FloatPixel f1(color1); @@ -63,8 +63,8 @@ double cluster_fit_2( // image.save("points.png"); // std::fstream ss("points.txt", std::fstream::out); - for (int r = 0; r < height; r++){ - for (int c = 0; c < width; c++){ + for (pxint_t r = 0; r < height; r++){ + for (pxint_t c = 0; c < width; c++){ QRgb pixel = image.pixel(c, r); // ss << "{" << qRed(pixel) << ", " << qGreen(pixel) << ", " << qBlue(pixel) << "}," << endl; FloatPixel p0 = f0 - pixel; diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp index 9289604174..21cd8dd831 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.cpp @@ -18,20 +18,20 @@ namespace PokemonAutomation{ bool fill_geometry( FillGeometry& geometry, FillMatrix& matrix, FillMatrix::ObjectID required_existing_id, - int x, int y, bool allow_diagonal, + pxint_t x, pxint_t y, bool allow_diagonal, FillMatrix::ObjectID object_id ){ if (matrix[y][x] != required_existing_id){ return false; } - int width = matrix.width(); - int height = matrix.height(); + pxint_t width = matrix.width(); + pxint_t height = matrix.height(); - int min_x = x; - int max_x = x; - int min_y = y; - int max_y = y; + pxint_t min_x = x; + pxint_t max_x = x; + pxint_t min_y = y; + pxint_t max_y = y; size_t area = 0; uint64_t sum_x = 0; @@ -40,7 +40,7 @@ bool fill_geometry( // int startx = x; // int starty = y; - std::vector> queue; + std::vector> queue; queue.emplace_back(x, y); matrix[y][x] = object_id; // cout << " (|" << starty << "," << startx << " : " << id << " = " << matrix[starty][startx] << ")" << endl; @@ -55,7 +55,7 @@ bool fill_geometry( sum_y += y; // Load the verticals. - if (y - 1 >= 0){ + if (y > 0){ FillMatrix::ObjectID& neighbor = matrix[y - 1][x]; if (neighbor == required_existing_id){ neighbor = object_id; @@ -71,7 +71,7 @@ bool fill_geometry( max_y = std::max(max_y, y + 1); } } - if (x - 1 >= 0){ + if (x > 0){ FillMatrix::ObjectID& neighbor = matrix[y][x - 1]; if (neighbor == required_existing_id){ neighbor = object_id; @@ -93,7 +93,7 @@ bool fill_geometry( } // If all verticals are blank, try the diagonals. - if (y - 1 >= 0 && x - 1 >= 0){ + if (y > 0 && x > 0){ FillMatrix::ObjectID& neighbor = matrix[y - 1][x - 1]; if (neighbor == required_existing_id){ neighbor = object_id; @@ -102,7 +102,7 @@ bool fill_geometry( min_y = std::min(min_y, y - 1); } } - if (y - 1 >= 0 && x + 1 < width){ + if (y > 0 && x + 1 < width){ FillMatrix::ObjectID& neighbor = matrix[y - 1][x + 1]; if (neighbor == required_existing_id){ neighbor = object_id; @@ -111,7 +111,7 @@ bool fill_geometry( min_y = std::min(min_y, y - 1); } } - if (y + 1 < height && x - 1 >= 0){ + if (y + 1 < height && x > 0){ FillMatrix::ObjectID& neighbor = matrix[y + 1][x - 1]; if (neighbor == required_existing_id){ neighbor = object_id; @@ -138,8 +138,8 @@ bool fill_geometry( geometry.box.max_y = max_y + 1; geometry.id = object_id; geometry.area = area; - geometry.center_x = (int)(sum_x / area); - geometry.center_y = (int)(sum_y / area); + geometry.center_x = (pxint_t)(sum_x / area); + geometry.center_y = (pxint_t)(sum_y / area); return true; } @@ -148,12 +148,12 @@ std::vector find_all_objects( bool allow_diagonal, size_t min_area ){ - size_t width = matrix.width(); - size_t height = matrix.height(); + pxint_t width = matrix.width(); + pxint_t height = matrix.height(); std::vector objects; FillMatrix::ObjectID id = 2; - for (size_t r = 0; r < height; r++){ - for (size_t c = 0; c < width; c++){ + for (pxint_t r = 0; r < height; r++){ + for (pxint_t c = 0; c < width; c++){ FillGeometry region; if (fill_geometry(region, matrix, required_existing_id, c, r, allow_diagonal, id)){ id++; diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h index 7b21ca0a4e..e9d8821c69 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h +++ b/SerialPrograms/Source/CommonFramework/Inference/FillGeometry.h @@ -21,8 +21,20 @@ struct FillGeometry{ PixelBox box; FillMatrix::ObjectID id = 0; size_t area = 0; - int center_x = 0; - int center_y = 0; + pxint_t center_x = 0; + pxint_t center_y = 0; + + double aspect_ratio() const{ return (double)box.width() / box.height(); } + double area_ratio() const{ return (double)area / box.area();} + + bool assert_aspect_ratio(double lower_bound, double upper_bound) const{ + double ratio = aspect_ratio(); + return lower_bound <= ratio && ratio <= upper_bound; + } + bool assert_area_ratio(double lower_bound, double upper_bound) const{ + double ratio = area_ratio(); + return lower_bound <= ratio && ratio <= upper_bound; + } }; enum class MapState : uint8_t{ @@ -36,7 +48,7 @@ enum class MapState : uint8_t{ bool fill_geometry( FillGeometry& geometry, FillMatrix& matrix, FillMatrix::ObjectID required_existing_id, - int x, int y, bool allow_diagonal, + pxint_t x, pxint_t y, bool allow_diagonal, FillMatrix::ObjectID id ); std::vector find_all_objects( diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp index a00537ee98..5bcbb9c36c 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.cpp @@ -34,7 +34,7 @@ FillMatrix::FillMatrix() : m_width(0) , m_height(0) {} -FillMatrix::FillMatrix(size_t width, size_t height) +FillMatrix::FillMatrix(pxint_t width, pxint_t height) : m_width(width) , m_height(height) , m_cells(width * height) @@ -46,12 +46,23 @@ FillMatrix::FillMatrix(const QImage& image) ) {} +FillMatrix FillMatrix::extract(PixelBox box) const{ + pxint_t width = box.width(); + pxint_t height = box.height(); + FillMatrix matrix(width, height); + for (pxint_t r = 0; r < height; r++){ + for (pxint_t c = 0; c < width; c++){ + matrix[r][c] = (*this)[r + box.min_y][c + box.min_x]; + } + } + return matrix; +} FillMatrix FillMatrix::extract(PixelBox box, ObjectID id) const{ - int width = box.width(); - int height = box.height(); + pxint_t width = box.width(); + pxint_t height = box.height(); FillMatrix matrix(width, height); - for (int r = 0; r < height; r++){ - for (int c = 0; c < width; c++){ + for (pxint_t r = 0; r < height; r++){ + for (pxint_t c = 0; c < width; c++){ ObjectID cell = (*this)[r + box.min_y][c + box.min_x]; matrix[r][c] = cell == id ? 1 : 0; } @@ -62,14 +73,14 @@ FillMatrix FillMatrix::extract(PixelBox box, ObjectID id) const{ std::string FillMatrix::dump() const{ - int width = m_width; - int height = m_height; + pxint_t width = m_width; + pxint_t height = m_height; std::string str; str += "{\n"; - for (int r = 0; r < height; r++){ + for (pxint_t r = 0; r < height; r++){ str += " "; - for (int c = 0; c < width; c++){ + for (pxint_t c = 0; c < width; c++){ str += " "; str += std::to_string((int)(*this)[r][c]); } diff --git a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h index 179671cad5..c2dbb770b2 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h +++ b/SerialPrograms/Source/CommonFramework/Inference/FillMatrix.h @@ -25,14 +25,15 @@ class FillMatrix{ public: FillMatrix(); - FillMatrix(size_t width, size_t height); + FillMatrix(pxint_t width, pxint_t height); FillMatrix(const QImage& image); - size_t width () const{ return m_width; } - size_t height() const{ return m_height; } - const ObjectID* operator[](size_t row) const{ return m_cells.data() + row * m_width; } - ObjectID* operator[](size_t row) { return m_cells.data() + row * m_width; } + pxint_t width () const{ return m_width; } + pxint_t height() const{ return m_height; } + const ObjectID* operator[](pxint_t row) const{ return m_cells.data() + row * m_width; } + ObjectID* operator[](pxint_t row) { return m_cells.data() + row * m_width; } + FillMatrix extract(PixelBox box) const; FillMatrix extract(PixelBox box, ObjectID id) const; std::string dump() const; @@ -44,14 +45,14 @@ class FillMatrix{ template bool apply_filter( Image&& image, Filter& filter, - size_t y_start, size_t y_end, - size_t x_start, size_t x_end + pxint_t y_start, pxint_t y_end, + pxint_t x_start, pxint_t x_end ); private: - size_t m_width; - size_t m_height; + pxint_t m_width; + pxint_t m_height; std::vector m_cells; }; @@ -69,16 +70,16 @@ bool FillMatrix::apply_filter(Image&& image, Filter& filter){ template bool FillMatrix::apply_filter( Image&& image, Filter& filter, - size_t y_start, size_t y_end, - size_t x_start, size_t x_end + pxint_t y_start, pxint_t y_end, + pxint_t x_start, pxint_t x_end ){ - if (image.isNull() || (size_t)image.width() != m_width || (size_t)image.height() != m_height){ + if (image.isNull() || image.width() != m_width || image.height() != m_height){ return false; } ObjectID* data = m_cells.data(); - for (size_t r = y_start; r < y_end; r++){ - for (size_t c = x_start; c < x_end; c++){ + for (pxint_t r = y_start; r < y_end; r++){ + for (pxint_t c = x_start; c < x_end; c++){ filter(data[c], image, c, r); } data += m_width; diff --git a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp index d4694147eb..9b83e2e849 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp +++ b/SerialPrograms/Source/CommonFramework/Inference/ImageTools.cpp @@ -44,8 +44,8 @@ InferenceBox translate_to_parent( ){ double width = original_image.width(); double height = original_image.height(); - int box_x = (int)(width * inference_box.x + 0.5); - int box_y = (int)(height * inference_box.y + 0.5); + pxint_t box_x = (pxint_t)(width * inference_box.x + 0.5); + pxint_t box_y = (pxint_t)(height * inference_box.y + 0.5); return InferenceBox( (box_x + box.min_x) / width, (box_y + box.min_y) / height, @@ -59,10 +59,10 @@ QImage extract_box(const QImage& image, const PixelBox& box){ } QImage extract_box(const QImage& image, const InferenceBox& box){ return image.copy( - (int)(image.width() * box.x + 0.5), - (int)(image.height() * box.y + 0.5), - (int)(image.width() * box.width + 0.5), - (int)(image.height() * box.height + 0.5) + (pxint_t)(image.width() * box.x + 0.5), + (pxint_t)(image.height() * box.y + 0.5), + (pxint_t)(image.width() * box.width + 0.5), + (pxint_t)(image.height() * box.height + 0.5) ); } @@ -79,8 +79,8 @@ double image_diff(const QImage& x, const QImage& y){ double sum = 0; - int width = x.width(); - int height = x.height(); + pxint_t width = x.width(); + pxint_t height = x.height(); for (int r = 0; r < height; r++){ for (int c = 0; c < width; c++){ sum += euclidean_distance(x.pixel(c, r), y.pixel(c, r)); @@ -91,11 +91,11 @@ double image_diff(const QImage& x, const QImage& y){ } FloatPixel pixel_average(const QImage& image){ - int w = image.width(); - int h = image.height(); + pxint_t w = image.width(); + pxint_t h = image.height(); FloatPixel sum; - for (int r = 0; r < h; r++){ - for (int c = 0; c < w; c++){ + for (pxint_t r = 0; r < h; r++){ + for (pxint_t c = 0; c < w; c++){ sum += FloatPixel(image.pixel(c, r)); } } @@ -116,15 +116,15 @@ FloatPixel pixel_average_normalized(const QImage& image){ FloatPixel pixel_stddev(const QImage& image){ - int w = image.width(); - int h = image.height(); + pxint_t w = image.width(); + pxint_t h = image.height(); if (w * h <= 1){ return FloatPixel(); } FloatPixel sum; FloatPixel sqr_sum; - for (int r = 0; r < h; r++){ - for (int c = 0; c < w; c++){ + for (pxint_t r = 0; r < h; r++){ + for (pxint_t c = 0; c < w; c++){ FloatPixel p(image.pixel(c, r)); sum += p; sqr_sum += p * p; @@ -140,21 +140,21 @@ FloatPixel pixel_stddev(const QImage& image){ } ImageStats pixel_stats(const QImage& image){ - int w = image.width(); - int h = image.height(); + pxint_t w = image.width(); + pxint_t h = image.height(); if (w * h <= 1){ return ImageStats(); } FloatPixel sum; FloatPixel sqr_sum; - for (int r = 0; r < h; r++){ - for (int c = 0; c < w; c++){ + for (pxint_t r = 0; r < h; r++){ + for (pxint_t c = 0; c < w; c++){ FloatPixel p(image.pixel(c, r)); sum += p; sqr_sum += p * p; } } - size_t total = w * h; + size_t total = (size_t)w * (size_t)h; FloatPixel variance = (sqr_sum - sum*sum / total) / (total - 1); return ImageStats{ sum / total, @@ -166,15 +166,15 @@ ImageStats pixel_stats(const QImage& image){ }; } ImageStats object_stats(const QImage& image, const FillMatrix& matrix, const FillGeometry& object){ - int w = image.width(); - int h = image.height(); + pxint_t w = image.width(); + pxint_t h = image.height(); if (w * h <= 1){ return ImageStats(); } FloatPixel sum; FloatPixel sqr_sum; - for (int r = 0; r < h; r++){ - for (int c = 0; c < w; c++){ + for (pxint_t r = 0; r < h; r++){ + for (pxint_t c = 0; c < w; c++){ if (matrix[r][c] == object.id){ FloatPixel p(image.pixel(c, r)); sum += p; @@ -199,7 +199,7 @@ bool is_black(const QImage& image, double max_rgb_sum, double max_stddev_sum){ ImageStats stats = pixel_stats(image); double average = stats.average.sum(); double stddev = stats.stddev.sum(); - return average < 100 && stddev < 10; + return average <= max_rgb_sum && stddev <= max_stddev_sum; } diff --git a/SerialPrograms/Source/CommonFramework/Inference/InferenceTypes.h b/SerialPrograms/Source/CommonFramework/Inference/InferenceTypes.h index 45191d9ebe..03db10e5bf 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/InferenceTypes.h +++ b/SerialPrograms/Source/CommonFramework/Inference/InferenceTypes.h @@ -11,15 +11,18 @@ namespace PokemonAutomation{ +using pxint_t = int; -struct PixelBox{ - int min_x; - int min_y; - int max_x; - int max_y; - int width() const{ return max_x - min_x; } - int height() const{ return max_y - min_y; } +struct PixelBox{ + pxint_t min_x; + pxint_t min_y; + pxint_t max_x; + pxint_t max_y; + + pxint_t width() const{ return max_x - min_x; } + pxint_t height() const{ return max_y - min_y; } + size_t area() const{ return (size_t)width() * (size_t)height(); } }; @@ -30,6 +33,11 @@ struct InferenceBox{ double width; double height; + InferenceBox() + : color(Qt::red) + , x(0), y(0) + , width(0), height(0) + {} InferenceBox( double p_x, double p_y, double p_width, double p_height diff --git a/SerialPrograms/Source/CommonFramework/Options/ConfigOption.h b/SerialPrograms/Source/CommonFramework/Options/ConfigOption.h index 273b51268d..abc11bc0dd 100644 --- a/SerialPrograms/Source/CommonFramework/Options/ConfigOption.h +++ b/SerialPrograms/Source/CommonFramework/Options/ConfigOption.h @@ -10,6 +10,7 @@ #include #include #include +#include "Common/Compiler.h" class ConfigOptionUI; diff --git a/SerialPrograms/Source/CommonFramework/Options/RandomCode.cpp b/SerialPrograms/Source/CommonFramework/Options/RandomCode.cpp index 04d1402167..bf9b784f34 100644 --- a/SerialPrograms/Source/CommonFramework/Options/RandomCode.cpp +++ b/SerialPrograms/Source/CommonFramework/Options/RandomCode.cpp @@ -40,7 +40,7 @@ bool RaidCode::get_code(uint8_t* code) const{ }while (x >= 10); code[c] = x; } - for (uint8_t c = m_random_digits; c < m_digits; c++){ + for (size_t c = m_random_digits; c < m_digits; c++){ code[c] = code[c - 1]; } return true; @@ -107,7 +107,7 @@ RandomCodeUI::RandomCodeUI(QWidget& parent, RandomCode& value) m_box_random = new QLineEdit(QString::number(m_value.m_current.random_digits()), this); box_row->addWidget(m_box_random, 1); - m_box_random->setValidator(new QIntValidator(0, m_value.m_current.total_digits(), this)); + m_box_random->setValidator(new QIntValidator(0, (int)m_value.m_current.total_digits(), this)); } { QHBoxLayout* box_row = new QHBoxLayout(); diff --git a/SerialPrograms/Source/CommonFramework/Panels/RightPanel.h b/SerialPrograms/Source/CommonFramework/Panels/RightPanel.h index 3b3a8d4cc5..20da655437 100644 --- a/SerialPrograms/Source/CommonFramework/Panels/RightPanel.h +++ b/SerialPrograms/Source/CommonFramework/Panels/RightPanel.h @@ -9,6 +9,7 @@ #include #include +#include "Common/Compiler.h" //#include //using std::cout; diff --git a/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp b/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp index 74992da003..bc1f26457a 100644 --- a/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp +++ b/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp @@ -25,8 +25,9 @@ PersistentSettings settings; void PersistentSettings::write() const{ QJsonObject root; - root.insert("00-ConfigPath", QJsonValue(config_path)); - root.insert("01-SourcePath", QJsonValue(source_path)); +// root.insert("00-ConfigPath", QJsonValue(config_path)); +// root.insert("01-SourcePath", QJsonValue(source_path)); + root.insert("01-StatsFile", QJsonValue(stats_file)); { QJsonArray res; res.append(QJsonValue(window_size.width())); @@ -59,8 +60,9 @@ void PersistentSettings::read(){ throw StringException("Invalid settings file."); } QJsonObject root = doc.object(); - json_get_string(config_path, root, "00-ConfigPath"); - json_get_string(source_path, root, "01-SourcePath"); +// json_get_string(config_path, root, "00-ConfigPath"); +// json_get_string(source_path, root, "01-SourcePath"); + json_get_string(stats_file, root, "01-StatsFile"); { QJsonArray res = json_get_array_nothrow(root, "02-WindowSize"); if (res.size() == 2){ diff --git a/SerialPrograms/Source/CommonFramework/PersistentSettings.h b/SerialPrograms/Source/CommonFramework/PersistentSettings.h index 410b24fa84..2c9107d024 100644 --- a/SerialPrograms/Source/CommonFramework/PersistentSettings.h +++ b/SerialPrograms/Source/CommonFramework/PersistentSettings.h @@ -24,8 +24,9 @@ class PersistentSettings{ void read(); public: - QString config_path = "GeneratorConfig"; - QString source_path = "DeviceSource"; +// QString config_path = "GeneratorConfig"; +// QString source_path = "DeviceSource"; + QString stats_file = "PA-Stats.txt"; QSize window_size = QSize(960, 540); bool naughty_mode = false; bool developer_mode = false; diff --git a/SerialPrograms/Source/CommonFramework/Tools/MainThreadDispatcher (disabled).cpp b/SerialPrograms/Source/CommonFramework/Tools/MainThreadDispatcher (disabled).cpp deleted file mode 100644 index 0772221ce7..0000000000 --- a/SerialPrograms/Source/CommonFramework/Tools/MainThreadDispatcher (disabled).cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* Dispatch to Main Thread - * - * From: https://github.com/Mysticial/Pokemon-Automation-SwSh-Arduino-Scripts - * - */ - -#include -#include "MainThreadDispatcher (disabled).h" - -std::unique_ptr application; - -MainThreadDispatcher::MainThreadDispatcher() - : m_live(0) -{} -MainThreadDispatcher::~MainThreadDispatcher(){ - cout << this << " ~MainThreadDispatcher()" << " : " << m_live << endl; - while (m_live.load(std::memory_order_acquire) != 0){ - _mm_pause(); - } -} - -MainThreadDispatcher::Decrementer::Decrementer(std::atomic& live) - : m_live(live) -{ - cout << &m_live << " MainThreadDispatcher**" << " : " << m_live << endl; -} -MainThreadDispatcher::Decrementer::~Decrementer(){ - m_live.fetch_sub(1); - cout << &m_live << " MainThreadDispatcher--" << " : " << m_live << endl; -} - - diff --git a/SerialPrograms/Source/CommonFramework/Tools/MainThreadDispatcher (disabled).h b/SerialPrograms/Source/CommonFramework/Tools/MainThreadDispatcher (disabled).h deleted file mode 100644 index ed7d8c3878..0000000000 --- a/SerialPrograms/Source/CommonFramework/Tools/MainThreadDispatcher (disabled).h +++ /dev/null @@ -1,63 +0,0 @@ -/* Dispatch to Main Thread - * - * From: https://github.com/Mysticial/Pokemon-Automation-SwSh-Arduino-Scripts - * - */ - -#ifndef PokemonAutomation_MainThreadDispatcher_H -#define PokemonAutomation_MainThreadDispatcher_H - -#include -#include -#include - -#include -using std::cout; -using std::endl; - - -extern std::unique_ptr application; - - -class MainThreadDispatcher{ -public: - MainThreadDispatcher(); - ~MainThreadDispatcher(); - - template - void operator()(Lambda&& lambda){ - m_live.fetch_add(1); - cout << this << " MainThreadDispatcher++" << " : " << m_live << endl; - - try{ - bool ok = QMetaObject::invokeMethod( - application.get(), - [=, lambda = std::move(lambda)]{ - Decrementer decrementer(m_live); - lambda(); - } - ); - if (!ok){ - cout << this << " Dispatch Failed" << " : " << m_live << endl; - m_live.fetch_sub(1); - } - }catch (...){ - cout << this << " Dispatch Exception" << " : " << m_live << endl; - m_live.fetch_sub(1); - throw; - } - } - -private: - struct Decrementer{ - Decrementer(std::atomic& live); - ~Decrementer(); - std::atomic& m_live; - }; - -private: - std::atomic m_live; -}; - - -#endif diff --git a/SerialPrograms/Source/CommonFramework/Tools/MessageLoggerUI (disabled).cpp b/SerialPrograms/Source/CommonFramework/Tools/MessageLoggerUI (disabled).cpp deleted file mode 100644 index b36a0964d3..0000000000 --- a/SerialPrograms/Source/CommonFramework/Tools/MessageLoggerUI (disabled).cpp +++ /dev/null @@ -1,10 +0,0 @@ -/* Message Logger for UI - * - * From: https://github.com/Mysticial/Pokemon-Automation-SwSh-Arduino-Scripts - * - */ - -#include "ClientSource/Libraries/MessageConverter.h" -#include "MessageLoggerUI.h" - - diff --git a/SerialPrograms/Source/CommonFramework/Tools/MessageLoggerUI (disabled).h b/SerialPrograms/Source/CommonFramework/Tools/MessageLoggerUI (disabled).h deleted file mode 100644 index 20651c78e9..0000000000 --- a/SerialPrograms/Source/CommonFramework/Tools/MessageLoggerUI (disabled).h +++ /dev/null @@ -1,20 +0,0 @@ -/* Message Logger for UI - * - * From: https://github.com/Mysticial/Pokemon-Automation-SwSh-Arduino-Scripts - * - */ - -#ifndef PokemonAutomation_UiMessageLogger_H -#define PokemonAutomation_UiMessageLogger_H - -#include "ClientSource/Libraries/Logging.h" -//#include "UI/TextWindow.h" - - - - - - -#endif - - diff --git a/SerialPrograms/Source/CommonFramework/Tools/ProgramEnvironment.cpp b/SerialPrograms/Source/CommonFramework/Tools/ProgramEnvironment.cpp new file mode 100644 index 0000000000..81aeb51480 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Tools/ProgramEnvironment.cpp @@ -0,0 +1,81 @@ +/* Program Environment + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "ProgramEnvironment.h" + + +namespace PokemonAutomation{ + + +ProgramEnvironment::ProgramEnvironment( + Logger& logger, + StatsTracker* current_stats, + const StatsTracker* historical_stats +) + : m_enable_feedback(true) + , m_stopping(false) + , m_logger(logger) + , m_current_stats(current_stats) + , m_historical_stats(historical_stats) +{} + + + +void ProgramEnvironment::update_stats(){ + std::string current; + if (m_current_stats){ + current = m_current_stats->to_str(); + } + std::string historical; + if (m_historical_stats){ + historical = m_historical_stats->to_str(); + } + + if (current.empty() && historical.empty()){ + set_status(""); + return; + } + + if (!current.empty() && historical.empty()){ + QString str = QString(current.c_str()); + set_status(str); + log(str); + return; + } + if (current.empty() && !historical.empty()){ + QString str = QString(historical.c_str()); + set_status("Past Runs - " + str); + return; + } + + log(QString(current.c_str())); + + std::string str; + str += "Current Run - " + current; + str += "
"; + str += "Past Totals - " + historical; + + set_status(str.c_str()); +} + +bool ProgramEnvironment::is_stopping() const{ + return m_stopping.load(std::memory_order_acquire); +} +void ProgramEnvironment::check_stopping() const{ + if (is_stopping()){ + throw PokemonAutomation::CancelledException(); + } +} +void ProgramEnvironment::signal_stop(){ + m_stopping.store(true, std::memory_order_release); + std::lock_guard lg(m_lock); + m_cv.notify_all(); +} + + + + +} diff --git a/SerialPrograms/Source/CommonFramework/Tools/ProgramEnvironment.h b/SerialPrograms/Source/CommonFramework/Tools/ProgramEnvironment.h index 674f9925d7..1f9c0c5be2 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/ProgramEnvironment.h +++ b/SerialPrograms/Source/CommonFramework/Tools/ProgramEnvironment.h @@ -13,6 +13,8 @@ #include #include #include "ClientSource/Connection/BotBase.h" +#include "Logger.h" +#include "StatsTracking.h" //#include //using std::cout; @@ -25,42 +27,28 @@ class ProgramEnvironment : public QObject{ Q_OBJECT public: - ProgramEnvironment() - : m_enable_feedback(true) - , m_stopping(false) - {} - - bool is_stopping() const{ - return m_stopping.load(std::memory_order_acquire); - } - - void check_stopping() const{ - if (is_stopping()){ - throw PokemonAutomation::CancelledException(); - } - } + ProgramEnvironment( + Logger& logger, + StatsTracker* current_stats, + const StatsTracker* historical_stats + ); - template - void wait(Duration duration){ - check_stopping(); + template + void log(Args&&... args); + Logger& logger(){ return m_logger; } + + void update_stats(); - auto start = std::chrono::system_clock::now(); - std::unique_lock lg(m_lock); - m_cv.wait_for( - lg, duration, - [=]{ - return std::chrono::system_clock::now() - start >= duration || is_stopping(); - } - ); + template + StatsType& stats(); - check_stopping(); - } + bool is_stopping() const; + void check_stopping() const; + + template + void wait(Duration duration); - void signal_stop(){ - m_stopping.store(true, std::memory_order_release); - std::lock_guard lg(m_lock); - m_cv.notify_all(); - } + void signal_stop(); signals: @@ -72,10 +60,46 @@ class ProgramEnvironment : public QObject{ std::atomic m_stopping; std::mutex m_lock; std::condition_variable m_cv; + + Logger& m_logger; + StatsTracker* m_current_stats; + const StatsTracker* m_historical_stats; }; + +// Templates + + +template +void ProgramEnvironment::log(Args&&... args){ + m_logger.log(std::forward(args)...); +} + + +template +StatsType& ProgramEnvironment::stats(){ + return *static_cast(m_current_stats); +} + +template +void ProgramEnvironment::wait(Duration duration){ + check_stopping(); + + auto start = std::chrono::system_clock::now(); + std::unique_lock lg(m_lock); + m_cv.wait_for( + lg, duration, + [=]{ + return std::chrono::system_clock::now() - start >= duration || is_stopping(); + } + ); + + check_stopping(); +} + + } #endif diff --git a/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.cpp b/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.cpp new file mode 100644 index 0000000000..c805d405b2 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.cpp @@ -0,0 +1,214 @@ +/* Stats Database + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include +#include "ClientSource/Libraries/Logging.h" +#include "StatsDatabase.h" + +namespace PokemonAutomation{ + + + +StatLine::StatLine(const StatsTracker& tracker) + : m_time(current_time()) + , m_stats(tracker.to_str()) +{} +StatLine::StatLine(const std::string& line){ + size_t pos = line.find(" - "); + if (pos == std::string::npos){ + m_time = line; + return; + } + m_time = line.substr(0, pos); + + const char* ptr = line.c_str() + pos + 3; + + // Skip to stats. + while (true){ + char ch = *ptr; + if (ch < 32) return; + if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z')) break; + ptr++; + } + + m_stats = ptr; +} +std::string StatLine::to_str() const{ + return m_time + " - " + m_stats; +#if 0 + std::string str = m_time + " - "; + str += m_tracker + ? m_tracker->to_str() + : m_stats; + return m_time + " - " + + (m_tracker ? m_tracker->to_str() : m_stats); +#endif +} + + + + +void StatList::operator+=(const StatsTracker& tracker){ + m_list.emplace_back(tracker); +} +void StatList::operator+=(const std::string& line){ + m_list.emplace_back(line); +} +std::string StatList::to_str() const{ + std::string str; + for (const StatLine& line : m_list){ + str += line.to_str(); + str += "\r\n"; + } + return str; +} + +void StatList::aggregate(StatsTracker& tracker) const{ + for (const StatLine& line : m_list){ + tracker.parse_and_append_line(line.stats()); + } +} + + + + +#if 0 +StatList* StatSet::find(const std::string& label){ + auto iter = m_data.find(label); + return iter == m_data.end() + ? nullptr + : &iter->second; +} +#endif +StatList& StatSet::operator[](const std::string& label){ + return m_data[label]; +} + +std::string StatSet::to_str() const{ + std::string str; + for (const auto& item : m_data){ + if (item.second.size() == 0){ + continue; + } + str += "================================================================================\r\n"; + str += item.first; + str += "\r\n"; + str += "\r\n"; + str += item.second.to_str(); + str += "\r\n"; + } + return str; +} + +void StatSet::save_to_file(const QString& filepath){ + QFile file(filepath); + file.open(QIODevice::WriteOnly); + std::string data = to_str(); + file.write(data.c_str(), data.size()); +} +void StatSet::open_from_file(const QString& filepath){ + QFile file(filepath); + if (!file.open(QIODevice::ReadOnly)){ + return; + } + + std::string str = file.readAll().data(); + load_from_string(str.c_str()); +} + +bool StatSet::update_file( + const QString& filepath, + const std::string& label, + const StatsTracker& tracker +){ + QFile file(filepath); + while (!file.open(QIODevice::ReadWrite)){ + return false; + } + std::string data = file.readAll().data(); + + StatSet set; + set.load_from_string(data.c_str()); + + set[label] += tracker; + + data = set.to_str(); + file.seek(0); + file.write(data.c_str(), data.size()); + file.resize(data.size()); + + return true; +} + + +bool StatSet::get_line(std::string& line, const char*& ptr){ + line.clear(); + for (;; ptr++){ + char ch = *ptr; + if (ch == '\0'){ +// cout << line << endl; + ptr++; + return false; + } + if (ch == '\r'){ + continue; + } + if (ch == '\n'){ +// cout << line << endl; + ptr++; + return true; + } + line += ch; + } +} +void StatSet::load_from_string(const char* ptr){ + // Find first section. + while (true){ + std::string line; + if (!get_line(line, ptr)){ + return; + } + if (line[0] == '='){ + break; + } + } + + while (true){ + std::string line; + if (!get_line(line, ptr)){ + return; + } + if (line.empty()){ + continue; + } + + StatList& program = m_data[line]; + while (true){ + if (!get_line(line, ptr)){ + return; + } + if (line.empty()){ + continue; + } + if (line[0] == '='){ + break; + } + program += line; + } + } +} + + + + + + + + + +} + diff --git a/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.h b/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.h new file mode 100644 index 0000000000..2f9d6ca248 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.h @@ -0,0 +1,77 @@ +/* Stats Database + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_StatsDatabase_H +#define PokemonAutomation_StatsDatabase_H + +#include +#include "StatsTracking.h" + +namespace PokemonAutomation{ + + +class StatLine{ +public: + StatLine(const StatsTracker& tracker); + StatLine(const std::string& line); + + const std::string& stats() const{ return m_stats; } + std::string to_str() const; + +private: + std::string m_time; + std::string m_stats; +}; + + + +class StatList{ +public: + void operator+=(const StatsTracker& tracker); + void operator+=(const std::string& line); + + size_t size() const{ return m_list.size(); } + std::string to_str() const; + + const std::vector& list() const{ return m_list; } + + void aggregate(StatsTracker& tracker) const; + +private: + std::vector m_list; +}; + + + + +class StatSet{ +public: +// StatList* find(const std::string& label); + StatList& operator[](const std::string& label); + + std::string to_str() const; + + void save_to_file(const QString& filepath); + void open_from_file(const QString& filepath); + + static bool update_file( + const QString& filepath, + const std::string& label, + const StatsTracker& tracker + ); + +private: + bool get_line(std::string& line, const char*& ptr); + void load_from_string(const char* ptr); + +private: + std::map m_data; +}; + + + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.cpp b/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.cpp new file mode 100644 index 0000000000..855ef3aa24 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.cpp @@ -0,0 +1,99 @@ +/* Stats Tracking + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Clientside/PrettyPrint.h" +#include "ProgramEnvironment.h" +#include "StatsTracking.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ + + + +StatsTracker::Stat::Stat(std::string&& p_label, bool p_omit_if_zero) + : label(p_label) + , omit_if_zero(p_omit_if_zero) +{} +std::string StatsTracker::to_str() const{ + std::string str; + for (const Stat& stat : m_display_order){ + auto iter = m_stats.find(stat.label); + uint64_t count = 0; + if (iter != m_stats.end()){ + count = iter->second; + } + if (stat.omit_if_zero && count == 0){ + continue; + } + if (!str.empty()){ + str += " - "; + } + str += stat.label; + str += ": "; + str += tostr_u_commas(count); + } + return str; +} + +#if 0 +void StatsTracker::log_stats(ProgramEnvironment& env) const{ + QString str = QString(to_str().c_str()); + env.set_status(str); + env.log(str); +} +#endif + + + +void StatsTracker::parse_and_append_line(const std::string& line){ + const char* ptr = line.c_str(); + while (true){ + // Parse label. + std::string label; + while (true){ + char ch = *ptr++; + if (ch < 32) return; + if (ch == ':') break; + label += ch; + } + + // Skip to number. + while (true){ + char ch = *ptr; + if (ch < 32) return; + if ('0' <= ch && ch <= '9') break; + ptr++; + } + + // Parse number. + uint64_t count = 0; + while (true){ + char ch = *ptr++; + if (ch < 32) break; + if (ch == ',') continue; + if (!('0' <= ch && ch <= '9')) break; + count *= 10; + count += ch - '0'; + } + + cout << label << " = " << count << endl; + m_stats[label] += count; + + // Skip to next; + while (true){ + char ch = *ptr; + if (ch < 32) return; + if (('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z')) break; + ptr++; + } + } +} + + +} diff --git a/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.h b/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.h new file mode 100644 index 0000000000..1cda2babd8 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.h @@ -0,0 +1,44 @@ +/* Stats Tracking + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_StatsTracking_H +#define PokemonAutomation_StatsTracking_H + +#include +#include +#include + +namespace PokemonAutomation{ + + +class StatsTracker{ + StatsTracker(const StatsTracker&) = delete; + +public: + StatsTracker() = default; + + std::string to_str() const; +// void log_stats(ProgramEnvironment& env) const; + + void parse_and_append_line(const std::string& line); + +protected: + struct Stat{ + std::string label; + bool omit_if_zero; + Stat(std::string&& p_label, bool p_omit_if_zero = false); + }; + + std::vector m_display_order; + std::map m_stats; +}; + + + + + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.cpp b/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.cpp index a1a067182c..8a570ff7fd 100644 --- a/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.cpp +++ b/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.cpp @@ -7,6 +7,7 @@ #include #include #include +#include "Common/Compiler.h" #include "Common/Qt/StringException.h" #include "Common/Qt/QtJsonTools.h" #include "CameraSelector.h" @@ -65,6 +66,9 @@ CameraSelectorUI* CameraSelector::make_ui(QWidget& parent, QWidget& holder){ +CameraSelectorUI::~CameraSelectorUI(){ +// cout << "~CameraSelectorUI()" << endl; +} CameraSelectorUI::CameraSelectorUI(QWidget& parent, CameraSelector& value, QWidget& holder) : QWidget(&parent) , m_value(value) @@ -207,7 +211,7 @@ void CameraSelectorUI::reset_video(){ m_camera = nullptr; for (auto& item : m_pending_captures){ - item.second.done = true; + item.second.status = CaptureStatus::COMPLETED; item.second.cv.notify_all(); } } @@ -229,12 +233,13 @@ void CameraSelectorUI::reset_video(){ m_capture, &QCameraImageCapture::imageCaptured, this, [&](int id, const QImage& preview){ std::lock_guard lg(m_camera_lock); +// cout << "finish = " << id << endl; auto iter = m_pending_captures.find(id); if (iter == m_pending_captures.end()){ cout << "QCameraImageCapture::imageCaptured(): Unable to find capture id: " << id << endl; return; } - iter->second.done = true; + iter->second.status = CaptureStatus::COMPLETED; iter->second.image = preview; iter->second.cv.notify_all(); } @@ -243,12 +248,13 @@ void CameraSelectorUI::reset_video(){ m_capture, static_cast(&QCameraImageCapture::error), this, [&](int id, QCameraImageCapture::Error error, const QString& errorString){ std::lock_guard lg(m_camera_lock); +// cout << "error = " << id << endl; cout << "QCameraImageCapture::error(): " << errorString.toUtf8().data() << endl; auto iter = m_pending_captures.find(id); if (iter == m_pending_captures.end()){ return; } - iter->second.done = true; + iter->second.status = CaptureStatus::COMPLETED; iter->second.cv.notify_all(); } ); @@ -364,6 +370,8 @@ QImage CameraSelectorUI::snapshot(){ int id = m_capture->capture(); m_camera->unlock(); +// cout << "start = " << id << endl; + auto iter = m_pending_captures.emplace( std::piecewise_construct, std::forward_as_tuple(id), @@ -374,9 +382,17 @@ QImage CameraSelectorUI::snapshot(){ } PendingCapture& capture = iter.first->second; - capture.cv.wait(lg, [&]{ - return capture.done; - }); + capture.cv.wait_for( + lg, + std::chrono::milliseconds(1000), + [&]{ + return capture.status != CaptureStatus::PENDING; + } + ); + + if (capture.status != CaptureStatus::COMPLETED){ + cout << "Capture timed out." << endl; + } QImage ret = std::move(capture.image); m_pending_captures.erase(iter.first); diff --git a/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.h b/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.h index e3cfa56937..b7128c4625 100644 --- a/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.h +++ b/SerialPrograms/Source/CommonFramework/Widgets/CameraSelector.h @@ -50,6 +50,7 @@ class CameraSelector{ class CameraSelectorUI : public QWidget, public VideoFeed{ public: CameraSelectorUI(QWidget& parent, CameraSelector& value, QWidget& holder); + ~CameraSelectorUI(); VideoOverlay& overlay(){ return *m_overlay; } @@ -71,8 +72,13 @@ class CameraSelectorUI : public QWidget, public VideoFeed{ static QString aspect_ratio(const QSize& size); private: + enum class CaptureStatus{ + PENDING, + COMPLETED, + CANCELED, + }; struct PendingCapture{ - bool done = false; + CaptureStatus status = CaptureStatus::PENDING; QImage image; std::condition_variable cv; }; diff --git a/SerialPrograms/Source/CommonFramework/Widgets/ProgramList.cpp b/SerialPrograms/Source/CommonFramework/Widgets/ProgramList.cpp index 6b03cfd698..44d9e84a5f 100644 --- a/SerialPrograms/Source/CommonFramework/Widgets/ProgramList.cpp +++ b/SerialPrograms/Source/CommonFramework/Widgets/ProgramList.cpp @@ -26,11 +26,12 @@ ProgramListUI::ProgramListUI(MainWindow& parent) QFontMetrics fm(this->font()); for (const auto& item : PROGRAM_LIST()){ addItem(item->name()); - this->item(this->count() - 1)->setForeground(item->color()); + QListWidgetItem* list_item = this->item(this->count() - 1); + list_item->setForeground(item->color()); m_text_width = std::max(m_text_width, fm.width(item->name())); -// cout << m_text_width << endl; +// cout << m_text_width << " / " << list_item->sizeHint().width() << endl; } -// setMaximumWidth(m_width); +// setMaximumWidth(m_text_width); connect(this, &QListWidget::itemClicked, this, &ProgramListUI::row_selected); // connect(this, &QListWidget::currentRowChanged, this, &ProgramListUI::row_changed); diff --git a/SerialPrograms/Source/CommonFramework/Widgets/SettingList.cpp b/SerialPrograms/Source/CommonFramework/Widgets/SettingList.cpp index b4d496e2db..5b32ea1e28 100644 --- a/SerialPrograms/Source/CommonFramework/Widgets/SettingList.cpp +++ b/SerialPrograms/Source/CommonFramework/Widgets/SettingList.cpp @@ -37,7 +37,7 @@ SettingListUI::SettingListUI(MainWindow& parent) } // setMaximumWidth(m_width); - setMaximumHeight(4 + list.size() * (sizeHintForRow(0) + 2)); + setMaximumHeight(4 + (int)list.size() * (sizeHintForRow(0) + 2)); } void SettingListUI::row_selected(QListWidgetItem* item){ diff --git a/SerialPrograms/Source/CommonFramework/Windows/MainWindow.cpp b/SerialPrograms/Source/CommonFramework/Windows/MainWindow.cpp index c02a7695ee..b25f695169 100644 --- a/SerialPrograms/Source/CommonFramework/Windows/MainWindow.cpp +++ b/SerialPrograms/Source/CommonFramework/Windows/MainWindow.cpp @@ -19,6 +19,12 @@ #include "ButtonDiagram.h" #include "MainWindow.h" +//#include + +#include +using std::cout; +using std::endl; + namespace PokemonAutomation{ @@ -44,7 +50,7 @@ MainWindow::MainWindow(QWidget* parent) QHBoxLayout* hbox = new QHBoxLayout(centralwidget); QVBoxLayout* left_layout = new QVBoxLayout(); - hbox->addLayout(left_layout, 2); + hbox->addLayout(left_layout, 0); QGroupBox* program_box = new QGroupBox("Program List", centralwidget); @@ -67,10 +73,10 @@ MainWindow::MainWindow(QWidget* parent) m_settings_list->text_width() ); #else - int width = 250; +// int width = 250; #endif - program_box->setMaximumWidth(width); - setting_box->setMaximumWidth(width); +// program_box->setMaximumWidth(width); +// setting_box->setMaximumWidth(width); QGroupBox* support_box = new QGroupBox("Support (" + STRING_POKEMON + " Automation " + VERSION + ")", centralwidget); left_layout->addWidget(support_box); @@ -86,7 +92,7 @@ MainWindow::MainWindow(QWidget* parent) { QLabel* discord = new QLabel(support_box); links->addWidget(discord); - discord->setText("Discord Server"); + discord->setText("Discord: " + DISCORD_LINK + ""); discord->setTextFormat(Qt::RichText); discord->setTextInteractionFlags(Qt::TextBrowserInteraction); discord->setOpenExternalLinks(true); @@ -159,12 +165,14 @@ MainWindow::MainWindow(QWidget* parent) } MainWindow::~MainWindow(){ -// cout << "~MainWindow()" << endl; +// cout << "~MainWindow() - start" << endl; if (m_right_panel_widget != nullptr){ m_right_panel_layout->removeWidget(m_right_panel_widget); delete m_right_panel_widget; m_right_panel_widget = nullptr; } +// Sleep(1000); +// cout << "~MainWindow() - finish" << endl; } diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchProgram.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchProgram.cpp index aedd9a63f9..19bbccf173 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchProgram.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchProgram.cpp @@ -11,8 +11,13 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ -MultiSwitchProgramEnvironment::MultiSwitchProgramEnvironment(Logger& p_logger, std::vector p_switches) - : logger(p_logger) +MultiSwitchProgramEnvironment::MultiSwitchProgramEnvironment( + Logger& logger, + StatsTracker* current_stats, + const StatsTracker* historical_stats, + std::vector p_switches +) + : ProgramEnvironment(logger, current_stats, historical_stats) , dispatcher(p_switches.size()) , consoles(std::move(p_switches)) {} @@ -71,7 +76,10 @@ MultiSwitchProgramUI::MultiSwitchProgramUI(MultiSwitchProgram& factory, MainWind } MultiSwitchProgramUI::~MultiSwitchProgramUI(){ stop(); } -void MultiSwitchProgramUI::program(){ +void MultiSwitchProgramUI::program( + StatsTracker* current_stats, + const StatsTracker* historical_stats +){ MultiSwitchProgram& factory = static_cast(m_factory); std::vector switches; for (size_t c = 0; c < factory.count(); c++){ @@ -82,7 +90,11 @@ void MultiSwitchProgramUI::program(){ system.camera() ); } - MultiSwitchProgramEnvironment env(m_logger, std::move(switches)); + MultiSwitchProgramEnvironment env( + m_logger, + current_stats, historical_stats, + std::move(switches) + ); connect( this, &RunnableProgramUI::signal_cancel, &env, [&]{ diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchProgram.h b/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchProgram.h index bf77394a74..c153b4e721 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchProgram.h +++ b/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchProgram.h @@ -19,7 +19,6 @@ namespace NintendoSwitch{ class MultiSwitchProgramEnvironment : public ProgramEnvironment{ public: - Logger& logger; AsyncDispatcher dispatcher; std::vector consoles; @@ -36,7 +35,12 @@ class MultiSwitchProgramEnvironment : public ProgramEnvironment{ private: friend class MultiSwitchProgramUI; - MultiSwitchProgramEnvironment(Logger& p_logger, std::vector p_switches); + MultiSwitchProgramEnvironment( + Logger& logger, + StatsTracker* current_stats, + const StatsTracker* historical_stats, + std::vector p_switches + ); }; @@ -66,7 +70,10 @@ class MultiSwitchProgramUI final : public RunnableProgramUI{ MultiSwitchProgramUI(MultiSwitchProgram& factory, MainWindow& window); ~MultiSwitchProgramUI(); - virtual void program() override; + virtual void program( + StatsTracker* current_stats, + const StatsTracker* historical_stats + ) override; }; diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchSystem.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchSystem.cpp index 6df0cac07f..e9e342d710 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchSystem.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/MultiSwitchSystem.cpp @@ -53,7 +53,7 @@ void MultiSwitchSystemFactory::load_json(const QJsonValue& json){ return; } m_switches.clear(); - for (size_t c = 0; c < (size_t)array.size(); c++){ + for (int c = 0; c < array.size(); c++){ m_switches.emplace_back( new SwitchSystemFactory( QString("Switch ") + QString::number(c), @@ -63,7 +63,7 @@ void MultiSwitchSystemFactory::load_json(const QJsonValue& json){ ) ); } - json_get_int(m_active_switches, obj, "ActiveDevices", m_min_switches, m_max_switches); + json_get_int(m_active_switches, obj, "ActiveDevices", (int)m_min_switches, (int)m_max_switches); } QJsonValue MultiSwitchSystemFactory::to_json() const{ QJsonObject obj; @@ -124,7 +124,7 @@ MultiSwitchSystem::MultiSwitchSystem( for (size_t c = factory.m_min_switches; c <= factory.m_max_switches; c++){ box->addItem(QString::number(c)); } - box->setCurrentIndex(m_factory.m_active_switches - factory.m_min_switches); + box->setCurrentIndex((int)(m_factory.m_active_switches - factory.m_min_switches)); connect( box, static_cast(&QComboBox::currentIndexChanged), diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp index 6f68974a91..58604c26f4 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp @@ -13,6 +13,8 @@ #include "Common/Qt/StringException.h" #include "Common/Qt/QtJsonTools.h" #include "ClientSource/Connection/PABotBase.h" +#include "CommonFramework/Tools/StatsDatabase.h" +#include "CommonFramework/PersistentSettings.h" #include "CommonFramework/Windows/MainWindow.h" #include "RunnableSwitchProgram.h" @@ -328,14 +330,41 @@ void RunnableProgramUI::set_status(QString status){ } } +void RunnableProgramUI::show_stats_warning() const{ + QMessageBox box; + box.critical( + nullptr, + "Error", + "Unable to update stats file. You will need to do this manually." + ); +} + void RunnableProgramUI::run_program(){ if (m_state.load(std::memory_order_acquire) != ProgramState::RUNNING){ return; } + RunnableProgram& factory = static_cast(m_factory); + std::unique_ptr current_stats = factory.make_stats(); + std::unique_ptr historial_stats; + + std::string program_name = m_name.toUtf8().data(); + + // Aggregate historical stats. + if (current_stats){ + StatSet stat_sets; + stat_sets.open_from_file(settings.stats_file); + StatList& list = stat_sets[program_name]; + if (list.size() != 0){ + historial_stats = factory.make_stats(); + list.aggregate(*historial_stats); + } + } + + try{ m_logger.log("Starting Program: " + m_name + ""); - program(); + program(current_stats.get(), historial_stats.get()); m_setup->wait_for_all_requests(); m_logger.log("Ending Program..."); }catch (PokemonAutomation::CancelledException&){ @@ -344,10 +373,29 @@ void RunnableProgramUI::run_program(){ signal_error(str); } + + // Update historical stats. + if (current_stats){ + bool ok = StatSet::update_file(settings.stats_file, program_name, *current_stats); + if (ok){ + m_logger.log("Stats successfully saved!", "Blue"); + }else{ + m_logger.log("Unable to save stats.", "Red"); + QMetaObject::invokeMethod( + this, + "show_stats_warning", + Qt::AutoConnection + ); +// show_stats_warning(); + } + } + + m_logger.log("Entering STOPPED state."); m_state.store(ProgramState::STOPPED, std::memory_order_release); signal_reset(); m_logger.log("Now in STOPPED state."); +// cout << "Now in STOPPED state." << endl; } BotBase& RunnableProgramUI::sanitize_botbase(BotBase* botbase){ diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h index bd0c4f088f..026f65ff10 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h +++ b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h @@ -11,6 +11,7 @@ #include #include #include "CommonFramework/Globals.h" +#include "CommonFramework/Tools/StatsTracking.h" #include "CommonFramework/Options/ConfigOption.h" #include "CommonFramework/Panels/RightPanel.h" #include "SwitchSetup.h" @@ -40,6 +41,7 @@ class RunnableProgram : public RightPanel{ bool is_valid() const; void restore_defaults(); + virtual std::unique_ptr make_stats() const{ return nullptr; } virtual QWidget* make_ui(MainWindow& window) override; protected: @@ -73,7 +75,10 @@ class RunnableProgramUI : public RightPanelUI{ private: void on_stop(); void reset_connections(); - virtual void program(){} + virtual void program( + StatsTracker* current_stats, + const StatsTracker* historical_stats + ){} void run_program(); @@ -82,6 +87,9 @@ class RunnableProgramUI : public RightPanelUI{ void signal_error(QString message); void signal_reset(); +public slots: + void show_stats_warning() const; + protected: static BotBase& sanitize_botbase(BotBase* botbase); diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/SingleSwitchProgram.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/SingleSwitchProgram.cpp index 3e394b6a9b..861a7b7537 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/SingleSwitchProgram.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/SingleSwitchProgram.cpp @@ -41,10 +41,14 @@ SingleSwitchProgramUI::SingleSwitchProgramUI(SingleSwitchProgram& factory, MainW SingleSwitchProgramUI::~SingleSwitchProgramUI(){ stop(); } -void SingleSwitchProgramUI::program(){ +void SingleSwitchProgramUI::program( + StatsTracker* current_stats, + const StatsTracker* historical_stats +){ SwitchSystem* system = static_cast(m_setup); SingleSwitchProgramEnvironment env( m_logger, + current_stats, historical_stats, sanitize_botbase(system->botbase()), system->camera() ); diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/SingleSwitchProgram.h b/SerialPrograms/Source/NintendoSwitch/Framework/SingleSwitchProgram.h index 825e2719df..b4e6d71718 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/SingleSwitchProgram.h +++ b/SerialPrograms/Source/NintendoSwitch/Framework/SingleSwitchProgram.h @@ -18,14 +18,18 @@ namespace NintendoSwitch{ class SingleSwitchProgramEnvironment : public ProgramEnvironment{ public: - Logger& logger; ConsoleHandle console; private: friend class SingleSwitchProgramUI; template - SingleSwitchProgramEnvironment(Logger& p_logger, Args&&... args) - : logger(p_logger) + SingleSwitchProgramEnvironment( + Logger& logger, + StatsTracker* current_stats, + const StatsTracker* historical_stats, + Args&&... args + ) + : ProgramEnvironment(logger, current_stats, historical_stats) , console(0, std::forward(args)...) {} }; @@ -53,7 +57,10 @@ class SingleSwitchProgramUI final : public RunnableProgramUI{ SingleSwitchProgramUI(SingleSwitchProgram& factory, MainWindow& window); ~SingleSwitchProgramUI(); - virtual void program() override; + virtual void program( + StatsTracker* current_stats, + const StatsTracker* historical_stats + ) override; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp index 1770e96c68..5e08521983 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_BeamSetter.cpp @@ -12,9 +12,9 @@ #include "CommonFramework/Inference/InferenceThrottler.h" #include "PokemonSwSh_BeamSetter.h" -//#include -//using std::cout; -//using std::endl; +#include +using std::cout; +using std::endl; namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -106,7 +106,8 @@ BeamSetter::Detection BeamSetter::run( min_stddev = stddev; min_stddev_index = c; } - if (stddev < 10 && current_values[c].sum() > 500){ +// cout << c << " : stddev = " << stddev << ", sum = " << current_values[c].sum() << endl; + if (stddev < 50 && current_values[c].sum() > 500){ size_t& count = purple_detections[c]; count++; } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp index 5b0730aece..a2bd4625ed 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.cpp @@ -46,7 +46,7 @@ FishingDetector::Detection FishingDetector::detect_now(){ : Detection::HOOKED; } FishingDetector::Detection FishingDetector::wait_for_detection( - ProgramEnvironment& env, Logger& logger, + ProgramEnvironment& env, std::chrono::seconds timeout ){ InferenceThrottler throttler(timeout); @@ -58,18 +58,18 @@ FishingDetector::Detection FishingDetector::wait_for_detection( case Detection::NO_DETECTION: break; case Detection::HOOKED: - logger.log("FishEncounterDetector: Detected hook!", "purple"); + env.log("FishEncounterDetector: Detected hook!", "purple"); return detection; case Detection::MISSED: - logger.log("FishEncounterDetector: Missed a hook.", "red"); + env.log("FishEncounterDetector: Missed a hook.", "red"); return detection; case Detection::BATTLE_MENU: - logger.log("FishEncounterDetector: Expected battle menu.", "red"); + env.log("FishEncounterDetector: Expected battle menu.", "red"); return detection; } if (throttler.end_iteration(env)){ - logger.log("FishEncounterDetector: Timed out.", "red"); + env.log("FishEncounterDetector: Timed out.", "red"); return Detection::NO_DETECTION; } } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h index f69b6f8f94..5cc4dc61e0 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h @@ -36,7 +36,7 @@ class FishingDetector{ Detection detect_now(); Detection wait_for_detection( - ProgramEnvironment& env, Logger& logger, + ProgramEnvironment& env, std::chrono::seconds timeout = std::chrono::seconds(12) ); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.cpp index f6a248799d..684e444f7d 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.cpp @@ -18,31 +18,98 @@ namespace PokemonSwSh{ -void WhiteFilter::operator()(FillMatrix::ObjectID& cell, const QImage& image, size_t x, size_t y){ +#if 0 +struct WhiteFilter{ + size_t count = 0; + + void operator()(FillMatrix::ObjectID& cell, const QImage& image, pxint_t x, pxint_t y); +}; +void WhiteFilter::operator()(FillMatrix::ObjectID& cell, const QImage& image, pxint_t x, pxint_t y){ QRgb pixel = image.pixel(x, y); - int set = (pixel & 0x00808080) == 0x00808080 ? 1 : 0; -// if (!set){ -// image.setPixel(x, y, 0); -// } + if ((pixel & 0x00808080) != 0x00808080){ + cell = 0; + return; + } + + unsigned r = qRed(pixel); + unsigned g = qGreen(pixel); + unsigned b = qBlue(pixel); + unsigned average = (r + g + b) / 3; + r -= average; + g -= average; + b -= average; + r *= r; + g *= g; + b *= b; + unsigned variation = r + g + b; + + int set = variation < 2000 ? 1 : 0; cell = set; count += set; } +struct WhiteFilter2{ + size_t count = 0; + void operator()(FillMatrix::ObjectID& cell, QImage& image, pxint_t x, pxint_t y); +}; +void WhiteFilter2::operator()(FillMatrix::ObjectID& cell, QImage& image, pxint_t x, pxint_t y){ + QRgb pixel = image.pixel(x, y); + if ((pixel & 0x00808080) != 0x00808080){ + cell = 0; + image.setPixel(x, y, 0); + return; + } -struct WhiteFilter2{ + unsigned r = qRed(pixel); + unsigned g = qGreen(pixel); + unsigned b = qBlue(pixel); + unsigned average = (r + g + b) / 3; + r -= average; + g -= average; + b -= average; + r *= r; + g *= g; + b *= b; + unsigned variation = r + g + b; + + int set = variation < 1000 ? 1 : 0; + if (!set){ + image.setPixel(x, y, 0); + } + cell = set; + count += set; +} +#endif + + + +struct BrightFilter{ size_t count = 0; - void operator()(FillMatrix::ObjectID& cell, QImage& image, size_t x, size_t y){ + void operator()(FillMatrix::ObjectID& cell, const QImage& image, pxint_t x, pxint_t y){ QRgb pixel = image.pixel(x, y); - int set = (pixel & 0x00808080) == 0x00808080 ? 1 : 0; - if (!set){ - image.setPixel(x, y, 0); + if ((pixel & 0x00808080) != 0x00808080){ + cell = 0; + }else{ + cell = 1; + count++; } - cell = set; - count += set; } }; +struct BrightFilterDebug{ + size_t count = 0; + void operator()(FillMatrix::ObjectID& cell, QImage& image, pxint_t x, pxint_t y){ + QRgb pixel = image.pixel(x, y); + if ((pixel & 0x00808080) != 0x00808080){ + cell = 0; + image.setPixel(x, y, 0); + }else{ + cell = 1; + count++; + } + } +}; @@ -50,14 +117,13 @@ bool is_exclamation_mark( const FillGeometry& top, const FillGeometry& bot ){ +// cout << "asdf" << endl; // Make sure dimensions are reasonable. - double top_aspect_ratio = (double)top.box.width() / top.box.height(); - if (0.4 > top_aspect_ratio || top_aspect_ratio > 0.6){ - return false; - } - double bot_aspect_ratio = (double)bot.box.width() / bot.box.height(); - if (0.5 > bot_aspect_ratio || bot_aspect_ratio > 2.0){ +// if (!top.assert_aspect_ratio(0.3, 0.7)){ +// return false; +// } + if (!bot.assert_aspect_ratio(0.5, 2.0)){ return false; } @@ -66,6 +132,13 @@ bool is_exclamation_mark( // cout << bot.box.width() << " x " << bot.box.height() << endl; // cout << "check 1" << endl; +// if (top.area < 100 || !top.assert_area_ratio(0.65, 0.75)){ +// return false; +// } + if (!bot.assert_area_ratio(0.80, 1.0)){ +// cout << "Bad bot area." << endl; + return false; + } // if (top.area > 8 * bot.area){ // cout << "Ratio too large: " << top.area << " / " << bot.area << endl; @@ -88,8 +161,11 @@ bool is_exclamation_mark( } // Make sure horizontal alignment is reasonable. - int mid = (top.box.min_x + top.box.max_x) / 2; - if (std::abs(bot.box.max_x - mid) * 3 > top.box.width()){ + pxint_t mid = (top.box.min_x + top.box.max_x) >> 1; + pxint_t diff = bot.box.max_x > mid + ? bot.box.max_x - mid + : mid - bot.box.max_x; + if (diff * 3 > top.box.width()){ // cout << "Bad horizontal alignment." << endl; return false; } @@ -99,6 +175,12 @@ bool is_exclamation_mark( return false; } +#if 0 + cout << "area = " << top.area << " / " << bot.area << endl; + cout << "top = " << top.box.width() << " x " << top.box.height() << ", " << top.area_ratio() << endl; + cout << "bot = " << bot.box.width() << " x " << bot.box.height() << ", " << bot.area_ratio() << endl; +#endif + return true; } @@ -106,9 +188,13 @@ bool is_question_mark( const FillGeometry& top, const FillGeometry& bot ){ - // Make sure dimensions are reasonable. - double top_aspect_ratio = (double)top.box.width() / top.box.height(); - if (0.8 > top_aspect_ratio || top_aspect_ratio > 1.2){ +// // Make sure dimensions are reasonable. +// double top_aspect_ratio = (double)top.box.width() / top.box.height(); +// if (0.8 > top_aspect_ratio || top_aspect_ratio > 1.2){ +// return false; +// } + + if (!bot.assert_aspect_ratio(0.5, 2.0)){ return false; } @@ -118,10 +204,12 @@ bool is_question_mark( } // Verify that the top part of the question is between 5-10x the area of the bottom. - if (top.area > 10 * bot.area){ - return false; - } +// if (top.area > 10 * bot.area){ +// cout << "bad 0" << endl; +// return false; +// } if (top.area < 5 * bot.area){ +// cout << "bad 1" << endl; return false; } @@ -145,17 +233,23 @@ bool is_question_mark( // cout << top.box.width() << " x " << top.box.height() << " : " << top.area << " / " << top.box.width() * top.box.height() << endl; +#if 0 + cout << "area = " << top.area << " / " << bot.area << endl; + cout << "top = " << top.box.width() << " x " << top.box.height() << ", " << top.area_ratio() << endl; + cout << "bot = " << bot.box.width() << " x " << bot.box.height() << ", " << bot.area_ratio() << endl; +#endif + return true; } size_t find_exclamation_marks( - const QImage& image, const FillMatrix& matrix, + QImage image, const FillMatrix& matrix, const FillGeometry& object, std::vector& marks ){ FillMatrix search_matrix(image); - for (int r = 0; r < image.height(); r++){ - for (int c = 0; c < image.width(); c++){ + for (pxint_t r = 0; r < image.height(); r++){ + for (pxint_t c = 0; c < image.width(); c++){ if (matrix[r][c] != 1){ search_matrix[r][c] = 0; continue; @@ -163,17 +257,49 @@ size_t find_exclamation_marks( QRgb pixel = image.pixel(c, r); int red = qRed(pixel); int green = qGreen(pixel); - search_matrix[r][c] = red >= 96 && green <= 160 ? 1 : 0; +// cout << red << ", " << green << endl; + FillMatrix::ObjectID cell = red >= 96 && green <= 160 ? 1 : 0; + if (cell == 0){ + image.setPixel(c, r, 0); + } + search_matrix[r][c] = cell; } } - std::vector objects = find_all_objects(search_matrix, 1, false, 4); - if (objects.size() > 5){ +#if 0 + static int c = 0; + cout << "running: " << c << endl; + image.save("exclamation-filter-" + QString::number(c++) + ".png"); + if (c != 5){ + return 0; + } + if (matrix.width() * matrix.height() < 10000){ + cout << search_matrix.dump() << endl; + } +#endif + + + std::vector objects = find_all_objects(search_matrix, 1, false, 10); +// cout << "objects = " << objects.size() << endl; + if (objects.size() > 100){ +// cout << "objects.size() = " << objects.size() << endl; +// static int c = 0; +// image.save("test-" + QString::number(c++) + ".png"); return 0; } size_t count = 0; for (const FillGeometry& top : objects){ + // Throw out obvious bad top candidates. + if (top.area < 50 || !top.assert_area_ratio(0.55, 0.80)){ +// cout << "Bad top area: area = " << top.area << ", ratio = " << top.area_ratio() << endl; + continue; + } + if (!top.assert_aspect_ratio(0.3, 0.7)){ +// cout << "Bad top aspect ratio: " << top.aspect_ratio() << endl; + continue; + } + // cout << top.box.width() << " x " << top.box.height() << endl; for (const FillGeometry& bot : objects){ if (!is_exclamation_mark(top, bot)){ @@ -185,11 +311,28 @@ size_t find_exclamation_marks( top.box.max_x + object.box.min_x, bot.box.max_y + object.box.min_y }; + +// FillMatrix submatrix = matrix.extract(PixelBox{top.box.min_x, top.box.min_y, top.box.max_x, bot.box.max_y}); +// cout << submatrix.dump() << endl; + marks.emplace_back(box); count++; } } +#if 0 + if (count != 0){ + static int c = 0; + const PixelBox& box = marks.back(); + image.copy( + box.min_x, + box.min_y, + box.width(), box.height() + ).save("detection-" + QString::number(c++) + ".png"); + cout << "found" << endl; + } +#endif + return count; } size_t find_question_marks( @@ -210,6 +353,18 @@ size_t find_question_marks( } } +#if 0 + static int c = 0; + cout << "running: " << c << endl; + image.save("question-filter-" + QString::number(c++) + ".png"); + if (c != 3){ + return 0; + } + if (matrix.width() * matrix.height() < 10000){ + cout << search_matrix.dump() << endl; + } +#endif + std::vector objects = find_all_objects(search_matrix, 1, false, 4); if (objects.size() > 5){ return 0; @@ -218,6 +373,15 @@ size_t find_question_marks( size_t count = 0; for (const FillGeometry& top : objects){ // cout << top.box.width() << " x " << top.box.height() << endl; + if (top.area < 50 || !top.assert_area_ratio(0.40, 0.55)){ +// cout << "Bad top area: area = " << top.area << ", ratio = " << top.area_ratio() << endl; + continue; + } + if (!top.assert_aspect_ratio(0.65, 1.0)){ +// cout << "Bad top aspect ratio: " << top.aspect_ratio() << endl; + continue; + } + for (const FillGeometry& bot : objects){ if (!is_question_mark(top, bot)){ continue; @@ -242,19 +406,19 @@ size_t find_marks( std::vector* exclamation_marks, std::vector* question_marks ){ - int width = object.box.width(); - int height = object.box.height(); + pxint_t width = object.box.width(); + pxint_t height = object.box.height(); // if (object.area > 10000){ // cout << "too big: " << object.area << endl; // return 0; // } - // Find the holes. +// cout << "area = " << object.area << endl; FillMatrix edge_matrix = matrix.extract(object.box, object.id); // Invert the cells. - for (int r = 0; r < height; r++){ - for (int c = 0; c < width; c++){ + for (pxint_t r = 0; r < height; r++){ + for (pxint_t c = 0; c < width; c++){ FillMatrix::ObjectID cell = edge_matrix[r][c]; cell = cell ? 0 : 1; edge_matrix[r][c] = cell; @@ -263,7 +427,7 @@ size_t find_marks( { // Fill in the edges. size_t edge_area = 0; - for (int c = 0; c < width; c++){ + for (pxint_t c = 0; c < width; c++){ FillGeometry body; if (fill_geometry(body, edge_matrix, 1, c, 0, false, 2)){ edge_area += body.area; @@ -272,7 +436,7 @@ size_t find_marks( edge_area += body.area; } } - for (int r = 0; r < height; r++){ + for (pxint_t r = 0; r < height; r++){ FillGeometry body; if (fill_geometry(body, edge_matrix, 1, 0, r, false, 2)){ edge_area += body.area; @@ -294,10 +458,10 @@ size_t find_marks( count += find_question_marks(subimage, edge_matrix, object, *question_marks); } - static size_t id = 0; - if (count != 0){ -// subimage.save("mark-" + QString::number(id++) + ".png"); - } +// static size_t id = 0; +// if (count != 0){ +// subimage.save("detection-" + QString::number(id++) + ".png"); +// } return count; } @@ -307,25 +471,35 @@ size_t find_marks( std::vector* exclamation_marks, std::vector* question_marks ){ -#if 1 +#if 0 FillMatrix matrix(image); - WhiteFilter filter; + BrightFilter filter; matrix.apply_filter(image, filter); #else QImage copy = image; FillMatrix matrix(image); - WhiteFilter2 filter; + BrightFilterDebug filter; matrix.apply_filter(copy, filter); - copy.save("white_filter.png"); +// copy.save("white_filter.png"); #endif size_t count = 0; - std::vector white_boxes = find_all_objects(matrix, true, 100); + std::vector white_boxes = find_all_objects(matrix, 1, true, 100); +// cout << "white_boxes = " << white_boxes.size() << endl; for (const FillGeometry& white_box : white_boxes){ count += find_marks(image, matrix, white_box, exclamation_marks, question_marks); } +#if 0 + if (count > 0 || true){ + static int c = 0; + image.save("white_filter-" + QString::number(c) + "-O.png"); + copy.save("white_filter-" + QString::number(c) + "-P.png"); + c++; + } +#endif + return count; } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h index 97561511f1..1b3778d035 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h @@ -18,11 +18,6 @@ namespace PokemonSwSh{ -struct WhiteFilter{ - size_t count = 0; - - void operator()(FillMatrix::ObjectID& cell, const QImage& image, size_t x, size_t y); -}; diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp index d6db307d32..66299066df 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp @@ -77,19 +77,19 @@ bool StartBattleDetector::detect(const QImage& screen){ } return dialog; } -bool StartBattleDetector::wait(ProgramEnvironment& env, Logger& logger){ +bool StartBattleDetector::wait(ProgramEnvironment& env){ InferenceThrottler throttler(m_timeout, std::chrono::milliseconds(50)); while (true){ env.check_stopping(); QImage screen = m_feed.snapshot(); if (detect(screen)){ - logger.log("StartBattleDetector: Detected start of battle!", "purple"); + env.log("StartBattleDetector: Detected start of battle!", "purple"); return true; } if (throttler.end_iteration(env)){ - logger.log("StartBattleDetector: Timed out.", "red"); + env.log("StartBattleDetector: Timed out.", "red"); return false; } } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h index aee0fa2d36..682bd46052 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h @@ -30,7 +30,7 @@ class StartBattleDetector{ bool has_timed_out() const; bool detect(const QImage& screen); - bool wait(ProgramEnvironment& env, Logger& logger); + bool wait(ProgramEnvironment& env); private: VideoFeed& m_feed; diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp index 402c8f01e6..6321044602 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.cpp @@ -157,8 +157,11 @@ void ShinyEncounterDetector::push( QImage shiny_box = extract_box(screen, m_shiny_box); +// auto time0 = std::chrono::system_clock::now(); ShinyImageDetection signatures; signatures.accumulate(shiny_box, timestamp.time_since_epoch().count(), &m_logger); +// auto time1 = std::chrono::system_clock::now(); +// cout << std::chrono::duration_cast(time1 - time0).count() << endl; ShinyImageAlpha frame_alpha = signatures.alpha(); m_image_alpha.max(frame_alpha); @@ -169,7 +172,7 @@ void ShinyEncounterDetector::push( if (frame_alpha.shiny > 0){ - if (frame_alpha.shiny > m_detection_threshold){ + if (frame_alpha.shiny >= m_detection_threshold){ m_logger.log( "ShinyDetector: alpha = " + QString::number(frame_alpha.shiny) + " / " + QString::number(m_image_alpha.shiny) + " (threshold exceeded)", "blue" @@ -208,7 +211,7 @@ void ShinyEncounterDetector::push( ShinyDetection ShinyEncounterDetector::results() const{ double alpha = m_image_alpha.shiny; if (m_dialog_trigger){ - alpha += 1.0; + alpha += 1.5; } m_logger.log( "ShinyDetector: Overall Alpha = " + QString::number(alpha) + @@ -238,8 +241,7 @@ ShinyDetection ShinyEncounterDetector::results() const{ ShinyDetection detect_shiny_battle( - ProgramEnvironment& env, - VideoFeed& feed, Logger& logger, + ProgramEnvironment& env, VideoFeed& feed, const ShinyDetectionBattle& battle_settings, std::chrono::seconds timeout, double detection_threshold @@ -255,7 +257,7 @@ ShinyDetection detect_shiny_battle( StatAccumulatorI32 throttle_stats; StandardBattleMenuDetector menu(feed); - ShinyEncounterDetector detector(feed, logger, battle_settings, detection_threshold); + ShinyEncounterDetector detector(feed, env.logger(), battle_settings, detection_threshold); bool no_detection = false; @@ -273,7 +275,7 @@ ShinyDetection detect_shiny_battle( auto timestamp = time1; if (menu.detect(screen)){ - logger.log("ShinyDetector: Battle menu found!", "purple"); + env.log("ShinyDetector: Battle menu found!", "purple"); break; } auto time2 = std::chrono::system_clock::now(); @@ -295,13 +297,13 @@ ShinyDetection detect_shiny_battle( throttle_stats += std::chrono::duration_cast(time4 - time3).count(); } - logger.log("Diagnostics: Screenshot:\r\n" + capture_stats.dump(), Qt::magenta); - logger.log("Diagnostics: Menu Detection:\r\n" + menu_stats.dump(), Qt::magenta); - logger.log("Diagnostics: Inference:\r\n" + inference_stats.dump(), Qt::magenta); - logger.log("Diagnostics: Throttle:\r\n" + throttle_stats.dump(), Qt::magenta); + env.log("Diagnostics: Screenshot: " + capture_stats.dump(), Qt::magenta); + env.log("Diagnostics: Menu Detection: " + menu_stats.dump(), Qt::magenta); + env.log("Diagnostics: Inference: " + inference_stats.dump(), Qt::magenta); + env.log("Diagnostics: Throttle: " + throttle_stats.dump(), Qt::magenta); if (no_detection){ - logger.log("ShinyDetector: Battle menu not found after timeout.", "red"); + env.log("ShinyDetector: Battle menu not found after timeout.", "red"); return ShinyDetection::NO_BATTLE_MENU; } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h index b8c1cb5b6a..04c5d6bfa7 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h @@ -33,8 +33,7 @@ extern const ShinyDetectionBattle SHINY_BATTLE_RAID; ShinyDetection detect_shiny_battle( - ProgramEnvironment& env, - VideoFeed& feed, Logger& logger, + ProgramEnvironment& env, VideoFeed& feed, const ShinyDetectionBattle& battle_settings, std::chrono::seconds timeout, double detection_threshold = 2.0 diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h index 1cd6dd3343..89603aed52 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h @@ -18,7 +18,7 @@ namespace PokemonSwSh{ struct BrightYellowLightFilter{ size_t count = 0; - void operator()(FillMatrix::ObjectID& cell, const QImage& image, size_t x, size_t y){ + void operator()(FillMatrix::ObjectID& cell, const QImage& image, int x, int y){ QRgb pixel = image.pixel(x, y); // int set = qRed(pixel) > 160 && qGreen(pixel) > 160 && qBlue(pixel) > 128; int set = (pixel & 0x00c0c080) == 0x00c0c080 ? 1 : 0; @@ -30,7 +30,7 @@ struct BrightYellowLightFilter{ struct BrightYellowLightFilterDebug{ size_t count = 0; - void operator()(FillMatrix::ObjectID& cell, QImage& image, size_t x, size_t y){ + void operator()(FillMatrix::ObjectID& cell, QImage& image, int x, int y){ QRgb pixel = image.pixel(x, y); // int set = qRed(pixel) > 160 && qGreen(pixel) > 160 && qBlue(pixel) > 128; int set = (pixel & 0x00c0c080) == 0x00c0c080 ? 1 : 0; diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp index 5cdafb0ad1..5a58d4e91d 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyTrigger.cpp @@ -5,6 +5,7 @@ */ #include +#include "Common/Compiler.h" #include "CommonFramework/Inference/FillMatrix.h" #include "CommonFramework/Inference/FillGeometry.h" #include "PokemonSwSh_ShinyFilters.h" @@ -33,17 +34,33 @@ void ShinyImageDetection::accumulate( const QImage& image, uint64_t frame_counter, Logger* logger ){ +// auto time0 = std::chrono::system_clock::now(); + if (image.isNull()){ + return; + } +// auto time1 = std::chrono::system_clock::now(); +// cout << "Image Check: " << std::chrono::duration_cast(time1 - time0).count() << endl; + +// auto time0 = std::chrono::system_clock::now(); FillMatrix matrix(image); +// auto time1 = std::chrono::system_clock::now(); +// cout << "Matrix: " << std::chrono::duration_cast(time1 - time0).count() << endl; +// auto time0 = std::chrono::system_clock::now(); BrightYellowLightFilter filter; matrix.apply_filter(image, filter); +// auto time1 = std::chrono::system_clock::now(); +// cout << "Filter: " << std::chrono::duration_cast(time1 - time0).count() << endl; if (filter.count * 2 > (size_t)image.width() * image.height()){ return; } +// auto time0 = std::chrono::system_clock::now(); std::vector objects; objects = find_all_objects(matrix, 1, true); +// auto time1 = std::chrono::system_clock::now(); +// cout << "FillGeometry: " << std::chrono::duration_cast(time1 - time0).count() << endl; #if 0 QImage debug = image; @@ -58,6 +75,7 @@ void ShinyImageDetection::accumulate( // cout << "objects = " << objects.size() << endl; +// auto time2 = std::chrono::system_clock::now(); // bool detection = false; // int c = 0; for (FillGeometry& object : objects){ @@ -71,6 +89,7 @@ void ShinyImageDetection::accumulate( SparkleDetector detector(matrix, object); #if 1 +// auto time4 = std::chrono::system_clock::now(); if (detector.is_ball()){ // detection = true; this->balls.emplace_back(object.box); @@ -91,9 +110,11 @@ void ShinyImageDetection::accumulate( logger->log(str, "purple"); } } +// auto time5 = std::chrono::system_clock::now(); +// cout << std::chrono::duration_cast(time4 - time5).count() << endl; #endif #if 1 -// auto time0 = std::chrono::system_clock::now(); +// auto time6 = std::chrono::system_clock::now(); if (is_square2(image, matrix, object)){ // detection = true; this->squares.emplace_back(object.box); @@ -104,12 +125,14 @@ void ShinyImageDetection::accumulate( logger->log(str, "purple"); } } -// auto time1 = std::chrono::system_clock::now(); +// auto time7 = std::chrono::system_clock::now(); +// cout << std::chrono::duration_cast(time6 - time7).count() << endl; // if (time1 - time0 > std::chrono::milliseconds(20)){ // image.save("slow-inference.png"); // } #endif #if 1 +// auto time0 = std::chrono::system_clock::now(); if (is_square_beam(matrix, object, 0.5)){ // detection = true; this->lines.emplace_back(object.box); @@ -121,8 +144,12 @@ void ShinyImageDetection::accumulate( logger->log(str, "purple"); } } +// auto time1 = std::chrono::system_clock::now(); +// cout << std::chrono::duration_cast(time1 - time0).count() << endl; #endif } +// auto time3 = std::chrono::system_clock::now(); +// cout << "Objects: " << std::chrono::duration_cast(time3 - time2).count() << endl; // cout << "objects = " << objects.size() << endl; #if 0 diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp index f3474a50d9..8d8a64122a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.cpp @@ -6,6 +6,7 @@ #include #include +#include "Common/Compiler.h" #include "PokemonSwSh_ShinyFilters.h" #include "PokemonSwSh_SparkleTrigger.h" @@ -22,31 +23,36 @@ const double SparkleDetector::ANGLE_TOLERANCE_DEGREES = 10.; SparkleDetector::SparkleDetector( const FillMatrix& matrix, - const FillGeometry& box, + const FillGeometry& object, double percentile ) - : m_box(box) + : m_box(object) , m_percentile(percentile) { - if (box.area < 20){ + if (object.area < 20){ return; } - if (box.box.width() * box.box.height() > 10000){ + if (object.area > 10000){ +// cout << "box.area = " << object.area << endl; + return; + } + if (object.box.width() * object.box.height() > 10000){ // return; } + // Copy the relevant portion of the matrix and keep only the current object. - m_matrix = matrix.extract(box.box, box.id); + m_matrix = matrix.extract(object.box, object.id); // Sort all pixels by distance from center. - int center_x = box.center_x - box.box.min_x; - int center_y = box.center_y - box.box.min_y; + pxint_t center_x = object.center_x - object.box.min_x; + pxint_t center_y = object.center_y - object.box.min_y; std::map distances; - for (size_t r = 0; r < m_matrix.height(); r++){ - for (size_t c = 0; c < m_matrix.width(); c++){ + for (pxint_t r = 0; r < m_matrix.height(); r++){ + for (pxint_t c = 0; c < m_matrix.width(); c++){ if (m_matrix[r][c] != 0){ - int dist_x = (int)c - center_x; - int dist_y = (int)r - center_y; + pxint_t dist_x = c - center_x; + pxint_t dist_y = r - center_y; uint64_t distance_sqr = (uint64_t)dist_x*dist_x + (uint64_t)dist_y*dist_y; distances[distance_sqr]++; } @@ -54,8 +60,8 @@ SparkleDetector::SparkleDetector( } // Filter out the bottom 80%. - int stop = (int)(0.85 * box.area); - int count = 0; + size_t stop = (size_t)(0.85 * object.area); + size_t count = 0; uint64_t distance = 0; for (auto& item : distances){ count += item.second; @@ -65,10 +71,10 @@ SparkleDetector::SparkleDetector( } } m_radius_sqr = distance; - for (size_t r = 0; r < m_matrix.height(); r++){ - for (size_t c = 0; c < m_matrix.width(); c++){ - int dist_x = (int)c - center_x; - int dist_y = (int)r - center_y; + for (pxint_t r = 0; r < m_matrix.height(); r++){ + for (pxint_t c = 0; c < m_matrix.width(); c++){ + pxint_t dist_x = c - center_x; + pxint_t dist_y = r - center_y; uint64_t distance_sqr = (uint64_t)dist_x*dist_x + (uint64_t)dist_y*dist_y; if (distance_sqr < distance){ m_matrix[r][c] = 0; @@ -78,8 +84,8 @@ SparkleDetector::SparkleDetector( // Find new regions. FillMatrix::ObjectID id = 2; - for (size_t r = 0; r < m_matrix.height(); r++){ - for (size_t c = 0; c < m_matrix.width(); c++){ + for (pxint_t r = 0; r < m_matrix.height(); r++){ + for (pxint_t c = 0; c < m_matrix.width(); c++){ FillGeometry region; if (!fill_geometry(region, m_matrix, 1, c, r, true, id)){ continue; @@ -88,7 +94,7 @@ SparkleDetector::SparkleDetector( // if (region.area < 4){ // continue; // } - m_regions.emplace(-(ssize_t)region.area, region); + m_regions.emplace(-(int64_t)region.area, region); // cout << region.center_x << ", " << region.center_y << " : " << region.area << endl; } } @@ -113,21 +119,21 @@ bool SparkleDetector::is_ball(){ } // Make sure area is less than 1/2 of the box. - int width = m_box.box.width(); - int height = m_box.box.height(); + pxint_t width = m_box.box.width(); + pxint_t height = m_box.box.height(); // cout << "area = " << m_box.area * 2 << ", box = " << (uint64_t)width * height << endl; if (m_box.area * 2 > (uint64_t)width * height){ return false; } // Compute angles for the 4 largest regions. - int center_x = m_box.center_x - m_box.box.min_x; - int center_y = m_box.center_y - m_box.box.min_y; + pxint_t center_x = m_box.center_x - m_box.box.min_x; + pxint_t center_y = m_box.center_y - m_box.box.min_y; std::set angles; size_t c = 0; for (const auto& region : m_regions){ - int x = region.second.center_x - center_x; - int y = region.second.center_y - center_y; + pxint_t x = region.second.center_x - center_x; + pxint_t y = region.second.center_y - center_y; double angle = std::atan2(y, x) * 57.29577951308232; if (angle < 0){ angle += 360; @@ -162,15 +168,15 @@ bool SparkleDetector::is_ball(){ } // Verify that all live pixels are near the diagonals. - for (size_t r = 0; r < m_matrix.height(); r++){ - for (size_t c = 0; c < m_matrix.width(); c++){ + for (pxint_t r = 0; r < m_matrix.height(); r++){ + for (pxint_t c = 0; c < m_matrix.width(); c++){ if (m_matrix[r][c] == 0){ continue; } double distance = std::sqrt(center_x*center_x + center_y*center_y); - int dist_x = std::abs((int)c - center_x); - int dist_y = std::abs((int)r - center_y); - int dist = std::abs(dist_y - dist_x); + pxint_t dist_x = std::abs(c - center_x); + pxint_t dist_y = std::abs(r - center_y); + pxint_t dist = std::abs(dist_y - dist_x); if (dist > 2 + distance / 5.){ return false; } @@ -185,8 +191,8 @@ bool SparkleDetector::is_star(){ return false; } - int width = m_box.box.width(); - int height = m_box.box.height(); + pxint_t width = m_box.box.width(); + pxint_t height = m_box.box.height(); // Make sure dimentions are roughly square-ish. if (width > 2 * height || height > 2 * width){ @@ -194,13 +200,13 @@ bool SparkleDetector::is_star(){ } // Compute angles for the 5 largest regions. - int center_x = m_box.center_x - m_box.box.min_x; - int center_y = m_box.center_y - m_box.box.min_y; + pxint_t center_x = m_box.center_x - m_box.box.min_x; + pxint_t center_y = m_box.center_y - m_box.box.min_y; std::set angles; size_t c = 0; for (const auto& region : m_regions){ - int x = region.second.center_x - center_x; - int y = region.second.center_y - center_y; + pxint_t x = region.second.center_x - center_x; + pxint_t y = region.second.center_y - center_y; double angle = std::atan2(y, x) * 57.29577951308232; if (angle < 0){ angle += 360; diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h index ab6744edc7..7808e9db3c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h @@ -34,7 +34,7 @@ class SparkleDetector{ FillMatrix m_matrix; double m_percentile; - std::multimap m_regions; + std::multimap m_regions; uint64_t m_radius_sqr; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp index 2deb1d1d7a..44105e0602 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp @@ -5,6 +5,7 @@ */ #include +#include "Common/Compiler.h" #include "PokemonSwSh_SquareDetector.h" #include @@ -18,17 +19,28 @@ namespace PokemonSwSh{ struct Point{ - int x; - int y; + pxint_t x; + pxint_t y; double distance; double angle; bool remove; }; +void dump_matrix( + FillMatrix submatrix, + pxint_t center_x, pxint_t center_y, + const std::vector& points +){ + submatrix[center_y][center_x] = 9; + for (const Point& item : points){ + submatrix[item.y][item.x] = 9; + } + cout << submatrix.dump() << endl; +} template void dump_matrix( FillMatrix submatrix, - int center_x, int center_y, + pxint_t center_x, pxint_t center_y, const MapType& points_by_distance ){ submatrix[center_y][center_x] = 9; @@ -47,16 +59,16 @@ bool check_hole( size_t& background_area, FillMatrix& submatrix, size_t object_area, - int object_center_x, - int object_center_y + pxint_t object_center_x, + pxint_t object_center_y ){ - int width = submatrix.width(); - int height = submatrix.height(); + pxint_t width = submatrix.width(); + pxint_t height = submatrix.height(); size_t box_area = (size_t)width * height; // Fill in the edges. size_t edge_area = 0; - for (int c = 0; c < width; c++){ + for (pxint_t c = 0; c < width; c++){ FillGeometry body; if (fill_geometry(body, submatrix, 1, c, 0, false, 2)){ edge_area += body.area; @@ -65,7 +77,7 @@ bool check_hole( edge_area += body.area; } } - for (int r = 0; r < height; r++){ + for (pxint_t r = 0; r < height; r++){ FillGeometry body; if (fill_geometry(body, submatrix, 1, 0, r, false, 2)){ edge_area += body.area; @@ -110,60 +122,60 @@ bool check_hole( // std::vector get_furthest_points( const FillMatrix& submatrix, - int center_x, int center_y + pxint_t center_x, pxint_t center_y ){ - int width = submatrix.width(); - int height = submatrix.height(); + pxint_t width = submatrix.width(); + pxint_t height = submatrix.height(); // cout << "width = " << width << endl; // cout << "height = " << height << endl; // Keep points that are locally furthest away from the center. std::vector points; - for (int r = 0; r < height; r++){ - for (int c = 0; c < width; c++){ + for (pxint_t r = 0; r < height; r++){ + for (pxint_t c = 0; c < width; c++){ FillMatrix::ObjectID cell = submatrix[r][c]; if (cell != 0){ continue; } - int my_dx2 = c - center_x; - int my_dy2 = r - center_y; + pxint_t my_dx2 = c - center_x; + pxint_t my_dy2 = r - center_y; my_dx2 *= my_dx2; my_dy2 *= my_dy2; { - int dx2 = c + 1 - center_x; + pxint_t dx2 = c + 1 - center_x; dx2 *= dx2; if (dx2 > my_dx2 && c + 1 < width && submatrix[r][c + 1] == 0){ continue; } } { - int dx2 = c - 1 - center_x; + pxint_t dx2 = c - 1 - center_x; dx2 *= dx2; if (dx2 > my_dx2 && c > 0 && submatrix[r][c - 1] == 0){ continue; } } { - int dy2 = r + 1 - center_y; + pxint_t dy2 = r + 1 - center_y; dy2 *= dy2; if (dy2 > my_dy2 && r + 1 < height && submatrix[r + 1][c] == 0){ continue; } } { - int dy2 = r - 1 - center_y; + pxint_t dy2 = r - 1 - center_y; dy2 *= dy2; if (dy2 > my_dy2 && r > 0 && submatrix[r - 1][c] == 0){ continue; } } - int my_distance = my_dx2 + my_dy2; + pxint_t my_distance = my_dx2 + my_dy2; { - int dx2 = c + 1 - center_x; - int dy2 = r + 1 - center_y; + pxint_t dx2 = c + 1 - center_x; + pxint_t dy2 = r + 1 - center_y; dx2 *= dx2; dy2 *= dy2; if (dx2 + dy2 > my_distance && c + 1 < width && r + 1 < height && submatrix[r + 1][c + 1] == 0){ @@ -171,8 +183,8 @@ std::vector get_furthest_points( } } { - int dx2 = c - 1 - center_x; - int dy2 = r + 1 - center_y; + pxint_t dx2 = c - 1 - center_x; + pxint_t dy2 = r + 1 - center_y; dx2 *= dx2; dy2 *= dy2; if (dx2 + dy2 > my_distance && c > 0 && r + 1 < height && submatrix[r + 1][c - 1] == 0){ @@ -180,8 +192,8 @@ std::vector get_furthest_points( } } { - int dx2 = c + 1 - center_x; - int dy2 = r - 1 - center_y; + pxint_t dx2 = c + 1 - center_x; + pxint_t dy2 = r - 1 - center_y; dx2 *= dx2; dy2 *= dy2; if (dx2 + dy2 > my_distance && c + 1 < width && r > 0 && submatrix[r - 1][c + 1] == 0){ @@ -189,8 +201,8 @@ std::vector get_furthest_points( } } { - int dx2 = c - 1 - center_x; - int dy2 = r - 1 - center_y; + pxint_t dx2 = c - 1 - center_x; + pxint_t dy2 = r - 1 - center_y; dx2 *= dx2; dy2 *= dy2; if (dx2 + dy2 > my_distance && c > 0 && r > 0 && submatrix[r - 1][c - 1] == 0){ @@ -218,6 +230,10 @@ void merge_nearby_points(std::map& points, double min_distance_di const int DISTANCE_THRESHOLD = 5 * 5; bool changed; do{ + if (points.size() < 4){ + return; + } + // Iterate adjacent points and merge points that are close together. changed = false; auto iter = points.begin(); @@ -226,9 +242,9 @@ void merge_nearby_points(std::map& points, double min_distance_di for (; iter != points.end(); ++iter){ Point* current = &iter->second; - int dx = current->x - last->x; - int dy = current->y - last->y; - int distance = dx*dx + dy*dy; + pxint_t dx = current->x - last->x; + pxint_t dy = current->y - last->y; + uint64_t distance = (int64_t)dx*dx + (int64_t)dy*dy; if (distance < DISTANCE_THRESHOLD){ if (current->distance + min_distance_diff < last->distance){ current->remove = true; @@ -243,9 +259,9 @@ void merge_nearby_points(std::map& points, double min_distance_di } { Point* current = &points.begin()->second; - int dx = current->x - last->x; - int dy = current->y - last->y; - int distance = dx*dx + dy*dy; + pxint_t dx = current->x - last->x; + pxint_t dy = current->y - last->y; + uint64_t distance = (int64_t)dx*dx + (int64_t)dy*dy; if (distance < DISTANCE_THRESHOLD){ if (current->distance + min_distance_diff < last->distance){ current->remove = true; @@ -260,12 +276,12 @@ void merge_nearby_points(std::map& points, double min_distance_di } // Remove points marked for removal. - for (iter = points.begin(); iter != points.end(); ++iter){ + for (iter = points.begin(); iter != points.end();){ if (iter->second.remove){ // cout << "remove" << endl; - points.erase(iter); + iter = points.erase(iter); }else{ - iter->second.remove = false; + ++iter; } } // cout << "asdf" << endl; @@ -288,9 +304,9 @@ double normalize_angle_0_360(double angle){ double area_of_triangle( - int x0, int y0, - int x1, int y1, - int x2, int y2 + pxint_t x0, pxint_t y0, + pxint_t x1, pxint_t y1, + pxint_t x2, pxint_t y2 ){ return ((double)(x0 - x2) * (y1 - y0) - (double)(x0 - x1) * (y2 - y0)) * 0.5; } @@ -302,32 +318,35 @@ double area_of_triangle( // The returned map is keyed by the angle of the point to the center of the // object. This angle is relative to "base_angle" and reduced [0, 360). // -std::multimap> get_edge_points( +std::multimap> get_edge_points( const FillMatrix& submatrix, - int center_x, int center_y, double base_angle, + pxint_t center_x, pxint_t center_y, double base_angle, FillMatrix::ObjectID object, FillMatrix::ObjectID background ){ - int width = submatrix.width(); - int height = submatrix.height(); + pxint_t width = submatrix.width(); + pxint_t height = submatrix.height(); - std::multimap> ret; - for (int r = 0; r < height; r++){ - for (int c = 0; c < width; c++){ + std::multimap> ret; + for (pxint_t r = 0; r < height; r++){ + for (pxint_t c = 0; c < width; c++){ if (submatrix[r][c] != object){ continue; } if ( - (c > 0 && submatrix[r][c - 1] == background) || + (c > 0 && submatrix[r][c - 1] == background) || (c + 1 < width && submatrix[r][c + 1] == background) || - (r > 0 && submatrix[r - 1][c] == background) || + (r > 0 && submatrix[r - 1][c] == background) || (r + 1 < width && submatrix[r + 1][c] == background) ){ - double angle = std::atan2(r - center_y, c - center_x) * 57.295779513082320877; + double angle = std::atan2( + r - center_y, + c - center_x + ) * 57.295779513082320877; angle = normalize_angle_0_360(angle - base_angle); ret.emplace( angle, - std::pair{c, r} + std::pair{c, r} ); } } @@ -345,9 +364,9 @@ std::multimap> get_edge_points( // [point_hi_x, point_hi_y] // double sum_squares_from_line( - const std::multimap>& points_by_angle, - double point_lo_angle, int point_lo_x, int point_lo_y, - double point_hi_angle, int point_hi_x, int point_hi_y + const std::multimap>& points_by_angle, + double point_lo_angle, pxint_t point_lo_x, pxint_t point_lo_y, + double point_hi_angle, pxint_t point_hi_x, pxint_t point_hi_y ){ // cout << "Point 0: angle = " << point_lo_angle << ", [" << point_lo_x << "," << point_lo_y << "]" << endl; // cout << "Point 1: angle = " << point_hi_angle << ", [" << point_hi_x << "," << point_hi_y << "]" << endl; @@ -370,8 +389,8 @@ double sum_squares_from_line( // cout << "slope = " << slope << endl; double inverse = 1 / (slope*slope + 1); for (; iter0 != iter1; ++iter0){ - int dx = iter0->second.first - point_lo_x; - int dy = iter0->second.second - point_lo_y; + pxint_t dx = iter0->second.first - point_lo_x; + pxint_t dy = iter0->second.second - point_lo_y; double top = slope * dx - dy; double distance_sqr = top * top * inverse; // cout << " angle = " << iter0->first @@ -400,26 +419,26 @@ bool is_square2( // static int c = 0; // image.copy(object.box.min_x, object.box.min_y, object.box.width(), object.box.height()).save("item-" + QString::number(++c) + ".png"); - int width = object.box.width(); - int height = object.box.height(); + pxint_t width = object.box.width(); + pxint_t height = object.box.height(); // cout << "width = " << width << endl; // cout << "height = " << height << endl; if (width < 10 || height < 10){ return false; } - if (object.area * 10 > matrix.height() * matrix.width()){ + if (object.area * 10 > (size_t)matrix.height() * matrix.width()){ // cout << "Bad area." << endl; return false; } // size_t box_area = (size_t)width * height; FillMatrix submatrix = matrix.extract(object.box, object.id); - int center_x = object.center_x - object.box.min_x; - int center_y = object.center_y - object.box.min_y; + pxint_t center_x = object.center_x - object.box.min_x; + pxint_t center_y = object.center_y - object.box.min_y; // Invert the cells. - for (int r = 0; r < height; r++){ - for (int c = 0; c < width; c++){ + for (pxint_t r = 0; r < height; r++){ + for (pxint_t c = 0; c < width; c++){ FillMatrix::ObjectID cell = submatrix[r][c]; cell = cell ? 0 : 1; submatrix[r][c] = cell; @@ -427,6 +446,9 @@ bool is_square2( } // cout << submatrix.dump() << endl; +// static int c = 0; +// image.copy(object.box.min_x, object.box.min_y, object.box.width(), object.box.height()).save("item-" + QString::number(++c) + ".png"); + // Check the hole size_t background_area; if (!check_hole(background_area, submatrix, object.area, center_x, center_y)){ @@ -454,7 +476,6 @@ bool is_square2( } } } - const Point& far_corner0 = *best_point; // dump_matrix(submatrix, center_x, center_y, corner_candidates); @@ -476,13 +497,21 @@ bool is_square2( points_by_angle[angle] = point; // cout << "distance = " << point.first << ": " << point.second.x << ", " << point.second.y << endl; } + if (points_by_angle.size() < 4){ + return false; + } + Point far_corner0 = *best_point; corner_candidates.clear(); // Merge points that are close together. merge_nearby_points(points_by_angle); + if (points_by_angle.size() < 4){ + return false; + } // dump_matrix(submatrix, center_x, center_y, points_by_angle); +// cout << "points = " << points_by_angle.size() << endl; // Look for a point opposite to the furthest. @@ -503,13 +532,14 @@ bool is_square2( } } if (best_opposite < 160){ -// cout << "bad angle" << endl; +// cout << "bad angle = " << best_opposite << endl; return false; } } +// cout << "qwer" << endl; - const Point& far_corner1 = *best_point; + Point far_corner1 = *best_point; double far_corner1_angle = normalize_angle_0_360(far_corner1.angle - far_corner0.angle); @@ -533,11 +563,14 @@ bool is_square2( // Build array of all edge pixels. - std::multimap> edge_pixels = get_edge_points( + std::multimap> edge_pixels = get_edge_points( submatrix, center_x, center_y, far_corner0.angle, 0, 2 ); + if (edge_pixels.empty()){ + return false; + } // dump_matrix(submatrix, center_x, center_y, points_by_angle); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp index cdb375c039..c42d9dec26 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.cpp @@ -16,7 +16,7 @@ namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonSwSh{ -const double SQUARE_ANGLE_TOLERANCE_DEGREES = 30; +//const double SQUARE_ANGLE_TOLERANCE_DEGREES = 30; @@ -25,12 +25,12 @@ bool is_square_beam( const FillGeometry& object, double min_length ){ - int width = object.box.width(); - int height = object.box.height(); + pxint_t width = object.box.width(); + pxint_t height = object.box.height(); if (width < 5 || height < 5){ return false; } - int min_pixel_width = (int)(matrix.width() * min_length); + pxint_t min_pixel_width = (pxint_t)(matrix.width() * min_length); if (width < min_pixel_width){ return false; } @@ -43,15 +43,15 @@ bool is_square_beam( // Make sure it's actually a line. - std::vector heights(width); + std::vector heights(width); bool row_ok = false; // cout << "{"; - for (int r = object.box.min_y; r < object.box.max_y; r++){ + for (pxint_t r = object.box.min_y; r < object.box.max_y; r++){ // cout << " {"; - int length = 0; - for (int c = object.box.min_x; c < object.box.max_x; c++){ + pxint_t length = 0; + for (pxint_t c = object.box.min_x; c < object.box.max_x; c++){ // cout << matrix[r][c] << ", "; - int ok = matrix[r][c] == object.id ? 1 : 0; + pxint_t ok = matrix[r][c] == object.id ? 1 : 0; length += ok; heights[c - object.box.min_x] += ok; } @@ -66,10 +66,10 @@ bool is_square_beam( } // Make sure the bulge is not on the edge. - int thickest = 0; - int thickest_column = 0; - for (int c = 0; c < width; c++){ - int h = heights[c]; + pxint_t thickest = 0; + pxint_t thickest_column = 0; + for (pxint_t c = 0; c < width; c++){ + pxint_t h = heights[c]; // cout << h << ", "; if (thickest < h){ thickest = h; @@ -78,8 +78,8 @@ bool is_square_beam( } // cout << "}" << endl; // cout << "thickest = " << thickest << endl; - int diff_left = thickest_column; - int diff_right = width - thickest_column; + pxint_t diff_left = thickest_column; + pxint_t diff_right = width - thickest_column; // cout << diff_left << " : " << diff_right << endl; if (diff_left * 10 < diff_right){ // cout << "line is off-center" << endl; @@ -96,169 +96,6 @@ bool is_square_beam( } -// -// The old square detector. Deprecated. -// -#if 0 -bool is_square( - const FillMatrix& matrix, - const FillGeometry& object -){ - int width = object.box.width(); - int height = object.box.height(); - if (width < 5 || height < 5){ -// cout << "too small" << endl; - return false; - } - if (object.area * 10 > matrix.height() * matrix.width()){ - return false; - } -// cout << "asdf" << endl; - size_t box_area = (size_t)width * height; -// if (object.area * 2 > box_area){ -// return false; -// } -// return true; - -// cout << "qwer" << endl; - - FillMatrix submatrix = matrix.extract(object.box, object.id); - int center_x = object.center_x - object.box.min_x; - int center_y = object.center_y - object.box.min_y; - - // Invert the cells. - for (int r = 0; r < height; r++){ - for (int c = 0; c < width; c++){ - FillMatrix::ObjectID cell = submatrix[r][c]; - cell = cell ? 0 : 1; - submatrix[r][c] = cell; - } - } - - // Check the hole - { - // Fill in the edges. - size_t edge_area = 0; - for (int c = 0; c < width; c++){ - FillGeometry body; - if (fill_geometry(body, submatrix, 1, c, 0, false, 2)){ - edge_area += body.area; - } - if (fill_geometry(body, submatrix, 1, c, height - 1, false, 2)){ - edge_area += body.area; - } - } - for (int r = 0; r < height; r++){ - FillGeometry body; - if (fill_geometry(body, submatrix, 1, 0, r, false, 2)){ - edge_area += body.area; - } - if (fill_geometry(body, submatrix, 1, width - 1, r, false, 2)){ - edge_area += body.area; - } - } - - // The center of the object should be in the hole. - if (submatrix[center_y][center_x] != 1){ -// cout << "no hole in center" << endl; - return false; - } - - size_t hole_area = box_area - edge_area - object.area; - -// cout << "box = " << box_area << ", edge = " << edge_area << ", body = " << object.area -// << ", hole = " << hole_area << endl; - - if (hole_area < 20 || edge_area * 3 > box_area * 2){ - return false; - } - } - -// cout << submatrix.dump() << endl; - - - // Check vertex angles. - { - submatrix = matrix.extract(object.box, object.id); - - // Clear all but the edges. - for (int r = 1; r < height - 1; r++){ - for (int c = 1; c < width - 1; c++){ - submatrix[r][c] = 0; - } - } -// cout << submatrix.dump() << endl; - - std::vector regions = find_all_objects(submatrix, 1, false, 1); -// cout << "regions = " << regions.size() << endl; - - // Get furthest points. - std::vector> points; - for (const auto& region : regions){ - if (region.box.height() == 1){ - if (region.box.min_x <= center_x){ - points.emplace_back(region.box.min_x, region.center_y); - } - if (region.box.max_x > center_x){ - points.emplace_back(region.box.max_x, region.center_y); - } - }else{ - if (region.box.min_y <= center_y){ - points.emplace_back(region.center_x, region.box.min_y); - } - if (region.box.max_y > center_y){ - points.emplace_back(region.center_x, region.box.max_y); - } - } - } - - // Compute angles. - std::set angles; - for (const auto& point : points){ - int x = point.first - center_x; - int y = point.second - center_y; - double angle = std::atan2(y, x) * 57.29577951308232; - if (angle < 0){ - angle += 360; - } - angles.insert(angle); -// cout << "angle = " << angle << ", [" << x << "," << y << "]" << endl; - } - - // Verify angles. - std::map angle_sets; - while (!angles.empty()){ - double pivot = *angles.begin(); - int& high = angle_sets[pivot] = false; - for (auto angle : angles){ - double n = angle - pivot; - double q = std::round(n / 180.); - double r = n - 180*q; - if (std::abs(r) < SQUARE_ANGLE_TOLERANCE_DEGREES){ - angles.erase(angle); - if (n > 100){ - high = 1; - } - } - } - } - - int sum = 0; - for (const auto& angle : angle_sets){ -// cout << "Angle Set: " << angle.first << ":" << angle.second << endl; - sum += angle.second; - } -// cout << "sum = " << sum << endl; - if (sum < 2){ - return false; - } - } - -// cout << "square" << endl; - return true; -} -#endif - } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h index 7e73bcce9c..aa8f376c6a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h @@ -22,10 +22,10 @@ bool is_square_beam( ); -bool is_square( - const FillMatrix& matrix, - const FillGeometry& object -); +//bool is_square( +// const FillMatrix& matrix, +// const FillGeometry& object +//); } diff --git a/SerialPrograms/Source/PokemonSwSh/Options/EggStepCount.cpp b/SerialPrograms/Source/PokemonSwSh/Options/EggStepCount.cpp index 9d33bd4263..962166a7ef 100644 --- a/SerialPrograms/Source/PokemonSwSh/Options/EggStepCount.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Options/EggStepCount.cpp @@ -9,6 +9,9 @@ #include "Common/Clientside/PrettyPrint.h" #include "EggStepCount.h" +namespace PokemonAutomation{ + + const std::vector STEP_COUNTS{ 1280, 2560, @@ -73,3 +76,8 @@ void EggStepCountUI::restore_defaults(){ m_value.restore_defaults(); m_box->setCurrentIndex((int)m_value.m_current); } + + + +} + diff --git a/SerialPrograms/Source/PokemonSwSh/Options/EggStepCount.h b/SerialPrograms/Source/PokemonSwSh/Options/EggStepCount.h index a8437d62b2..572a0c4f4b 100644 --- a/SerialPrograms/Source/PokemonSwSh/Options/EggStepCount.h +++ b/SerialPrograms/Source/PokemonSwSh/Options/EggStepCount.h @@ -12,6 +12,8 @@ #include #include "CommonFramework/Options/ConfigOption.h" +namespace PokemonAutomation{ + extern const std::vector STEP_COUNTS; class EggStepCount : public ConfigOption{ @@ -30,8 +32,8 @@ class EggStepCount : public ConfigOption{ private: friend class EggStepCountUI; - const uint16_t m_default; - uint16_t m_current; + const size_t m_default; + size_t m_current; }; @@ -47,6 +49,6 @@ class EggStepCountUI : public ConfigOptionUI, public QWidget{ }; - +} #endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-BerryFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-BerryFarmer.cpp index 1e333e042b..9973a4e687 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-BerryFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-BerryFarmer.cpp @@ -41,7 +41,7 @@ void BerryFarmer::program(SingleSwitchProgramEnvironment& env) const{ uint8_t year = MAX_YEAR; uint16_t save_count = 0; for (uint32_t c = 0; c < SKIPS; c++){ - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(&year); pbf_mash_button(BUTTON_B, 90); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-DailyHighlightFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-DailyHighlightFarmer.cpp index a7ae0b1d7a..a320b693f9 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-DailyHighlightFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-DailyHighlightFarmer.cpp @@ -41,7 +41,7 @@ void DailyHighlightFarmer::program(SingleSwitchProgramEnvironment& env) const{ uint8_t year = MAX_YEAR; uint16_t save_count = 0; for (uint32_t c = 0; c < SKIPS; c++){ - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(&year); pbf_mash_button(BUTTON_B, 90); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-LotoFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-LotoFarmer.cpp index ea124ec277..1a3c805832 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-LotoFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-LotoFarmer.cpp @@ -40,7 +40,7 @@ void LotoFarmer::program(SingleSwitchProgramEnvironment& env) const{ uint8_t year = MAX_YEAR; for (uint32_t c = 0; c < SKIPS; c++){ - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(&year); pbf_mash_button(BUTTON_B, 90); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-StowOnSideFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-StowOnSideFarmer.cpp index 86162efb70..768379ca7a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-StowOnSideFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-StowOnSideFarmer.cpp @@ -41,7 +41,7 @@ void StowOnSideFarmer::program(SingleSwitchProgramEnvironment& env) const{ uint8_t year = MAX_YEAR; uint16_t save_count = 0; for (uint32_t c = 0; c < SKIPS; c++){ - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(&year); pbf_mash_button(BUTTON_B, 90); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-WattFarmer.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-WattFarmer.cpp index 8083936aa9..d763728690 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-WattFarmer.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DateSpamFarmers/PokemonSwSh_DateSpam-WattFarmer.cpp @@ -41,7 +41,7 @@ void WattFarmer::program(SingleSwitchProgramEnvironment& env) const{ uint8_t year = MAX_YEAR; uint16_t save_count = 0; for (uint32_t c = 0; c < SKIPS; c++){ - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); home_roll_date_enter_game_autorollback(&year); pbf_mash_button(BUTTON_B, 90); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperEU.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperEU.cpp index f86f18fcff..4ebc131fbb 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperEU.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperEU.cpp @@ -68,7 +68,7 @@ void DaySkipperEU::program(SingleSwitchProgramEnvironment& env) const{ correct_count++; year++; remaining_skips--; - env.logger.log("Skips Remaining: " + tostr_u_commas(remaining_skips)); + env.log("Skips Remaining: " + tostr_u_commas(remaining_skips)); if (year >= 60){ if (real_life_year <= 36){ diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN-7.8k.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN-7.8k.cpp index 0db96d86bb..83d10f1c0a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN-7.8k.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN-7.8k.cpp @@ -141,8 +141,8 @@ void DaySkipperJPN7p8k::program(SingleSwitchProgramEnvironment& env) const{ if (date_increment_day(&date, true)){ remaining_skips--; correct_count++; - env.logger.log("Expected Date: " + QDate(date.year + 2000, date.month, date.day).toString("yyyy/MM/dd")); - env.logger.log("Skips Remaining: " + tostr_u_commas(remaining_skips)); + env.log("Expected Date: " + QDate(date.year + 2000, date.month, date.day).toString("yyyy/MM/dd")); + env.log("Skips Remaining: " + tostr_u_commas(remaining_skips)); } if (CORRECTION_SKIPS != 0 && correct_count == CORRECTION_SKIPS){ correct_count = 0; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN.cpp index 49f15b420e..3fcd63f814 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperJPN.cpp @@ -60,7 +60,7 @@ void DaySkipperJPN::program(SingleSwitchProgramEnvironment& env) const{ correct_count++; day++; remaining_skips--; - env.logger.log("Skips Remaining: " + tostr_u_commas(remaining_skips)); + env.log("Skips Remaining: " + tostr_u_commas(remaining_skips)); } if (CORRECTION_SKIPS != 0 && correct_count == CORRECTION_SKIPS){ correct_count = 0; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperUS.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperUS.cpp index 5d9190e8ee..49068715e6 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperUS.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_DaySkipperUS.cpp @@ -68,7 +68,7 @@ void DaySkipperUS::program(SingleSwitchProgramEnvironment& env) const{ correct_count++; year++; remaining_skips--; - env.logger.log("Skips Remaining: " + tostr_u_commas(remaining_skips)); + env.log("Skips Remaining: " + tostr_u_commas(remaining_skips)); if (year >= 60){ if (real_life_year <= 36){ diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp index c601e23d8f..e29eeb3cdb 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.cpp @@ -9,6 +9,7 @@ #include "Common/SwitchFramework/Switch_PushButtons.h" #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "CommonFramework/Tools/StatsTracking.h" #include "PokemonSwSh/Inference/PokemonSwSh_BeamSetter.h" #include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" #include "PokemonSwSh_PurpleBeamFinder.h" @@ -43,6 +44,35 @@ PurpleBeamFinder::PurpleBeamFinder() m_options.emplace_back(&TIMEOUT_DELAY, "TIMEOUT_DELAY"); } + + + +struct PurpleBeamFinder::Stats : public StatsTracker{ + Stats() + : attempts(m_stats["Attempts"]) + , timeouts(m_stats["Timeouts"]) + , red_detected(m_stats["Red Detected"]) + , red_presumed(m_stats["Red Presumed"]) + , purple(m_stats["Purple"]) + { + m_display_order.emplace_back(Stat("Attempts")); + m_display_order.emplace_back(Stat("Timeouts")); + m_display_order.emplace_back(Stat("Red Detected")); + m_display_order.emplace_back(Stat("Red Presumed")); + m_display_order.emplace_back(Stat("Purple")); + } + uint64_t& attempts; + uint64_t& timeouts; + uint64_t& red_detected; + uint64_t& red_presumed; + uint64_t& purple; +}; +std::unique_ptr PurpleBeamFinder::make_stats() const{ + return std::unique_ptr(new Stats()); +} + + + void PurpleBeamFinder::program(SingleSwitchProgramEnvironment& env) const{ grip_menu_connect_go_home(); @@ -51,11 +81,7 @@ void PurpleBeamFinder::program(SingleSwitchProgramEnvironment& env) const{ env.console.botbase().wait_for_all_requests(); - uint64_t attempts = 0; - uint64_t red_detected = 0; - uint64_t red_assumed = 0; - uint64_t timed_out = 0; - uint64_t purple = 0; + Stats& stats = env.stats(); bool exit = false; @@ -70,42 +96,33 @@ void PurpleBeamFinder::program(SingleSwitchProgramEnvironment& env) const{ BeamSetter::Detection detection; { - BeamSetter setter(env.console, env.logger); + BeamSetter setter(env.console, env.logger()); detection = setter.run(env, env.console, DETECTION_THRESHOLD, TIMEOUT_DELAY); - attempts++; + stats.attempts++; } switch (detection){ case BeamSetter::NO_DETECTION: - timed_out++; + stats.timeouts++; break; case BeamSetter::RED_DETECTED: - red_detected++; + stats.red_detected++; break; case BeamSetter::RED_ASSUMED: - red_assumed++; + stats.red_presumed++; break; case BeamSetter::PURPLE: - purple++; + stats.purple++; exit = true; break; } - { - std::string status = "Attempts: " + tostr_u_commas(attempts); - status += " - Timeouts: " + tostr_u_commas(timed_out); - status += " - Red Detected: " + tostr_u_commas(red_detected); - status += " - Red Presumed: " + tostr_u_commas(red_assumed); - status += " - Purple: " + tostr_u_commas(purple); - QString str = status.c_str(); - env.set_status(str); - env.logger.log("" + str + ""); - } + env.update_stats(); if (exit){ break; } pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); reset_game_from_home_with_inference( - env, env.logger, env.console, + env, env.console, TOLERATE_SYSTEM_UPDATE_MENU_SLOW ); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.h b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.h index 5d7673c9f7..8e77f6925a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/DenHunting/PokemonSwSh_PurpleBeamFinder.h @@ -20,9 +20,12 @@ class PurpleBeamFinder : public SingleSwitchProgram{ public: PurpleBeamFinder(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: + struct Stats; + BooleanCheckBox EXTRA_LINE; FloatingPoint DETECTION_THRESHOLD; TimeExpression TIMEOUT_DELAY; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombinedShared.h b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombinedShared.h index 3d7d78121b..f0bd142f70 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombinedShared.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombinedShared.h @@ -232,7 +232,7 @@ struct EggCombinedSession{ for (uint8_t column = 0; column < 6; column++){ // Touch the date. if (TOUCH_DATE_INTERVAL > 0 && system_clock() - last_touch >= TOUCH_DATE_INTERVAL){ - env.logger.log("Touching date to prevent rollover."); + env.log("Touching date to prevent rollover."); pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); touch_date_from_home(SETTINGS_TO_HOME_DELAY); resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggFetcher2.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggFetcher2.cpp index 9cc7fc4c37..c55129f8c8 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggFetcher2.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggFetcher2.cpp @@ -40,7 +40,7 @@ void EggFetcher2::run_eggfetcher(SingleSwitchProgramEnvironment& env, bool depos // 1st Fetch: Get into position. { - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); fly_home_collect_egg(true); collect_egg_mash_out(deposit_automatically); @@ -52,7 +52,7 @@ void EggFetcher2::run_eggfetcher(SingleSwitchProgramEnvironment& env, bool depos // Now we are in steady state. for (; c < attempts; c++){ - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); eggfetcher_loop(); collect_egg(); collect_egg_mash_out(deposit_automatically); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggHelpers.h b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggHelpers.h index d4c497f8ca..5611a2c992 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggHelpers.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggHelpers.h @@ -7,6 +7,7 @@ #ifndef PokemonAutomation_PokemonSwSh_EggHelpers_H #define PokemonAutomation_PokemonSwSh_EggHelpers_H +#include "Common/Compiler.h" #include "Common/SwitchFramework/Switch_PushButtons.h" #include "Common/PokemonSwSh/PokemonSettings.h" #include "NintendoSwitch/FixedInterval.h" diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_GodEggDuplication.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_GodEggDuplication.cpp index 331a4f6980..a8cabc5736 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_GodEggDuplication.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_GodEggDuplication.cpp @@ -73,7 +73,7 @@ void GodEggDuplication::run_program(SingleSwitchProgramEnvironment& env, uint16_ // 1st Fetch: Get into position. { - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); fly_home_collect_egg(true); collect_godegg(party_slot++); if (party_slot >= items){ @@ -88,7 +88,7 @@ void GodEggDuplication::run_program(SingleSwitchProgramEnvironment& env, uint16_ // Now we are in steady state. for (; c < attempts; c++){ - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); eggfetcher_loop(); collect_egg(); collect_godegg(party_slot++); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_GodEggItemDupe.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_GodEggItemDupe.cpp index 107bd7be70..c4f6d7128d 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_GodEggItemDupe.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_GodEggItemDupe.cpp @@ -119,7 +119,7 @@ void GodEggItemDupe::run_program(SingleSwitchProgramEnvironment& env, uint16_t a // 1st Fetch: Get into position. { - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); fly_home_collect_egg(true); collect_godegg(party_slot++, true, false); if (party_slot >= items){ @@ -134,7 +134,7 @@ void GodEggItemDupe::run_program(SingleSwitchProgramEnvironment& env, uint16_t a // Now we are in steady state. for (; c < attempts; c++){ - env.logger.log("Fetch Attempts: " + tostr_u_commas(c)); + env.log("Fetch Attempts: " + tostr_u_commas(c)); eggfetcher_loop(); collect_egg(); collect_godegg(party_slot++, false, false); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.cpp index 68e3447234..19057cd537 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-MultiGame.cpp @@ -102,7 +102,7 @@ void AutoHostMultiGame::run_autohost( for (size_t c = 0; c < 8; c++){ str[c] = code[c] + '0'; } - env.logger.log("Next Raid Code: " + std::string(str, sizeof(str))); + env.log("Next Raid Code: " + std::string(str, sizeof(str))); pbf_press_button(BUTTON_PLUS, 5, 145); enter_digits(8, code); pbf_wait(180); @@ -112,7 +112,7 @@ void AutoHostMultiGame::run_autohost( // Accept friend requests while we wait. raid_lobby_wait( - env.console, env.logger, + env.console, env.logger(), HOST_ONLINE, accept_FR_slot, lobby_wait_delay @@ -129,11 +129,11 @@ void AutoHostMultiGame::run_autohost( pbf_mash_button(BUTTON_A, 3 * TICKS_PER_SECOND); env.console.botbase().wait_for_all_requests(); - BlackScreenDetector black_screen(env.console, env.logger); + BlackScreenDetector black_screen(env.console, env.logger()); uint32_t now = start; while (now - start < RAID_START_TO_EXIT_DELAY){ if (black_screen.black_is_over()){ - env.logger.log("Raid has Started!", "blue"); + env.log("Raid has Started!", "blue"); break; } pbf_mash_button(BUTTON_A, TICKS_PER_SECOND); @@ -193,14 +193,14 @@ void AutoHostMultiGame::program(SingleSwitchProgramEnvironment& env) const{ uint32_t raids = 0; bool game_slot_flipped = false; while (true){ - env.logger.log("Beginning from start of game list."); + env.log("Beginning from start of game list."); for (uint8_t index = 0; index < GAME_LIST.size(); index++){ const MultiHostTable::GameSlot& game = GAME_LIST[index]; // if (game.user_slot == 0){ // break; // } - env.logger.log("Raids Completed: " + tostr_u_commas(raids++)); + env.log("Raids Completed: " + tostr_u_commas(raids++)); // Start game. rollback_date_from_home(game.skips); @@ -223,7 +223,7 @@ void AutoHostMultiGame::program(SingleSwitchProgramEnvironment& env) const{ break; } start_game_from_home_with_inference( - env, env.logger, env.console, + env, env.console, TOLERATE_SYSTEM_UPDATE_MENU_SLOW, game_slot, game.user_slot, diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp index c7afc00f97..23f07b6ce8 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp @@ -122,7 +122,7 @@ void AutoHostRolling::program(SingleSwitchProgramEnvironment& env) const{ char first = true; for (uint32_t raids = 0;; raids++){ - env.logger.log("Raids Completed: " + tostr_u_commas(raids)); + env.log("Raids Completed: " + tostr_u_commas(raids)); roll_den(ENTER_ONLINE_DEN_DELAY, OPEN_ONLINE_DEN_LOBBY_DELAY, SKIPS, CATCHABILITY); @@ -144,7 +144,7 @@ void AutoHostRolling::program(SingleSwitchProgramEnvironment& env) const{ for (size_t c = 0; c < 8; c++){ str[c] = code[c] + '0'; } - env.logger.log("Next Raid Code: " + std::string(str, sizeof(str))); + env.log("Next Raid Code: " + std::string(str, sizeof(str))); pbf_press_button(BUTTON_PLUS, 5, 145); enter_digits(8, code); pbf_wait(180); @@ -154,7 +154,7 @@ void AutoHostRolling::program(SingleSwitchProgramEnvironment& env) const{ // Accept friend requests while we wait. raid_lobby_wait( - env.console, env.logger, + env.console, env.logger(), HOST_ONLINE, FRIEND_ACCEPT_USER_SLOT, lobby_wait_delay @@ -171,11 +171,11 @@ void AutoHostRolling::program(SingleSwitchProgramEnvironment& env) const{ pbf_mash_button(BUTTON_A, 3 * TICKS_PER_SECOND); env.console.botbase().wait_for_all_requests(); - BlackScreenDetector black_screen(env.console, env.logger); + BlackScreenDetector black_screen(env.console, env.logger()); uint32_t now = start; while (now - start < RAID_START_TO_EXIT_DELAY){ if (black_screen.black_is_over()){ - env.logger.log("Raid has Started!", "blue"); + env.log("Raid has Started!", "blue"); break; } pbf_mash_button(BUTTON_A, TICKS_PER_SECOND); @@ -215,7 +215,7 @@ void AutoHostRolling::program(SingleSwitchProgramEnvironment& env) const{ rollback_date_from_home(SKIPS); start_game_from_home_with_inference( - env, env.logger, env.console, + env, env.console, TOLERATE_SYSTEM_UPDATE_MENU_SLOW, 0, 0, BACKUP_SAVE diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp index 0becc2d760..98e19f4ba0 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenRoller.cpp @@ -62,7 +62,7 @@ void DenRoller::program(SingleSwitchProgramEnvironment& env) const{ rollback_date_from_home(SKIPS); // reset_game_from_home(TOLERATE_SYSTEM_UPDATE_MENU_SLOW); reset_game_from_home_with_inference( - env, env.logger, env.console, + env, env.console, TOLERATE_SYSTEM_UPDATE_MENU_SLOW ); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenTools.h b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenTools.h index 33b47cbb85..9ab3b88883 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenTools.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_DenTools.h @@ -7,6 +7,7 @@ #ifndef PokemonAutomation_PokemonSwSh_DenTools_H #define PokemonAutomation_PokemonSwSh_DenTools_H +#include "Common/Compiler.h" #include "Common/SwitchFramework/FrameworkSettings.h" #include "Common/SwitchFramework/Switch_PushButtons.h" #include "Common/PokemonSwSh/PokemonSettings.h" diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.cpp new file mode 100644 index 0000000000..21a2f78e6d --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.cpp @@ -0,0 +1,98 @@ +/* Overworld Trajectory + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include +#include "PokemonSwSh_OverworldTrajectory.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + + +const Trajectory TRAJECTORY_TABLE[8][6] = { + {{400,128, 0},{420,160, 0},{420,190, 0},{480,208, 0},{480,208, 0},{480,208, 0},}, + {{210,128, 0},{220,164, 0},{250,200, 0},{280,208, 0},{300,209, 0},{320,209, 0},}, + {{120,128, 0},{120,192, 0},{140,209, 0},{180,224, 0},{220,224, 0},{260,232, 0},}, + {{ 60,128, 0},{ 65,224, 0},{ 95,255, 32},{125,255, 48},{160,255, 48},{200,255, 48},}, + {{ 0,128,128},{ 40,255,110},{ 70,255,105},{100,255,100},{130,255, 95},{160,255, 90},}, + {{ 35,128,255},{ 45,255,255},{ 70,255,170},{ 90,255,160},{115,255,140},{140,255,128},}, + {{ 55,128,255},{ 60,192,255},{ 75,255,255},{ 90,255,192},{110,255,176},{135,255,144},}, + {{ 75,128,255},{ 80,160,255},{ 85,216,255},{ 95,255,224},{115,255,192},{130,255,176},}, +}; +Trajectory get_trajectory_int(int delta_x, int delta_y){ + delta_x = std::max(delta_x, -5); + delta_x = std::min(delta_x, +5); + delta_y = std::max(delta_y, -4); + delta_y = std::min(delta_y, +3); + + if (delta_x < 0){ + Trajectory entry = get_trajectory_int(-delta_x, delta_y); + entry.joystick_x = 256 - entry.joystick_x; + return entry; + } + + return TRAJECTORY_TABLE[delta_y + 4][delta_x]; +} +Trajectory get_trajectory_float(double delta_x, double delta_y){ + delta_x *= 10; + delta_y *= 10; + + int int_x = std::floor(delta_x); + int int_y = std::floor(delta_y); + + double frac_x = delta_x - int_x; + double frac_y = delta_y - int_y; + + Trajectory corner11 = get_trajectory_int(int_x, int_y); + Trajectory corner01 = get_trajectory_int(int_x + 1, int_y); + Trajectory corner10 = get_trajectory_int(int_x, int_y + 1); + Trajectory corner00 = get_trajectory_int(int_x + 1, int_y + 1); + + double top_x = corner11.joystick_x * (1.0 - frac_x) + corner01.joystick_x * frac_x; + double bot_x = corner10.joystick_x * (1.0 - frac_x) + corner00.joystick_x * frac_x; + double x = top_x * (1.0 - frac_y) + bot_x * frac_y; +// cout << "top_x = " << top_x << endl; +// cout << "bot_x = " << bot_x << endl; +// cout << "x = " << x << endl; + + double top_y = corner11.joystick_y * (1.0 - frac_y) + corner10.joystick_y * frac_y; + double bot_y = corner01.joystick_y * (1.0 - frac_y) + corner00.joystick_y * frac_y; + double y = top_y * (1.0 - frac_x) + bot_y * frac_x; + + double top_d = corner11.distance_in_ticks * (1.0 - frac_x) + corner01.distance_in_ticks * frac_x; + double bot_d = corner10.distance_in_ticks * (1.0 - frac_x) + corner00.distance_in_ticks * frac_x; + double d = top_d * (1.0 - frac_y) + bot_d * frac_y; + + x -= 128; + y -= 128; + double scale = std::max(std::abs(x), std::abs(y)); + if (scale == 0){ + x = 128; + y = 128; + }else{ + scale = 128 / scale; + x *= scale; + y *= scale; + } + x += 128; + y += 128; + + d = std::max(d, 0.0); + d = std::min(d, 65535.); + x = std::max(x, 0.0); + y = std::max(y, 0.0); + x = std::min(x, 255.); + y = std::min(y, 255.); + + return Trajectory{(uint16_t)d, (uint8_t)x, (uint8_t)y}; +} + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.h b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.h new file mode 100644 index 0000000000..071cd4451d --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.h @@ -0,0 +1,28 @@ +/* Overworld Trajectory + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_OverworldTrajectory_H +#define PokemonAutomation_PokemonSwSh_OverworldTrajectory_H + +#include + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + +struct Trajectory{ + uint16_t distance_in_ticks; + uint8_t joystick_x; + uint8_t joystick_y; +}; + +Trajectory get_trajectory_float(double delta_x, double delta_y); + + +} +} +} +#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_RaidItemFarmerOKHO.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_RaidItemFarmerOKHO.cpp index c6f29ff688..ea54c6c291 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_RaidItemFarmerOKHO.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_RaidItemFarmerOKHO.cpp @@ -95,7 +95,7 @@ void RaidItemFarmerOHKO::program(MultiSwitchProgramEnvironment& env) const{ for (uint32_t raids = 0;; raids++){ - env.logger.log("Raids Completed: " + tostr_u_commas(raids)); + env.log("Raids Completed: " + tostr_u_commas(raids)); host.wait_for_all_requests(); env.run_in_parallel( diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp index 36d8c51443..d3f464fd61 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.cpp @@ -19,7 +19,6 @@ namespace PokemonSwSh{ void enter_loading_game( ProgramEnvironment& env, - Logger& logger, ConsoleHandle& console, bool backup_save, uint16_t post_wait_time @@ -39,14 +38,14 @@ void enter_loading_game( QImage screen = console.video().snapshot(); if (screen.isNull()){ - logger.log("enter_loading_game(): Screenshot failed.", "purple"); + env.log("enter_loading_game(): Screenshot failed.", "purple"); throttler.set_period(std::chrono::milliseconds(1000)); }else{ bool black0 = is_black(extract_box(screen, box0)); bool black1 = is_black(extract_box(screen, box1)); if (black0 && black1){ if (!black_found){ - logger.log("start_game_with_inference(): Game load started.", "purple"); + env.log("start_game_with_inference(): Game load started.", "purple"); } black_found = true; }else if (black_found){ @@ -55,13 +54,13 @@ void enter_loading_game( } if (throttler.end_iteration(env)){ - logger.log("enter_loading_game(): Game load timed out. Proceeding with default start delay.", "red"); + env.log("enter_loading_game(): Game load timed out. Proceeding with default start delay.", "red"); break; } } } - logger.log("enter_loading_game(): Game Loaded. Entering game...", "purple"); + env.log("enter_loading_game(): Game Loaded. Entering game...", "purple"); enter_game(backup_save, ENTER_GAME_MASH, 0); console.botbase().wait_for_all_requests(); @@ -79,13 +78,13 @@ void enter_loading_game( QImage screen = console.video().snapshot(); if (screen.isNull()){ - logger.log("enter_loading_game(): Screenshot failed.", "purple"); + env.log("enter_loading_game(): Screenshot failed.", "purple"); throttler.set_period(std::chrono::milliseconds(1000)); }else{ bool black = is_black(extract_box(screen, box)); if (black){ if (!black_found){ - logger.log("enter_loading_game(): Game entry started.", "purple"); + env.log("enter_loading_game(): Game entry started.", "purple"); } black_found = true; }else if (black_found){ @@ -94,12 +93,12 @@ void enter_loading_game( } if (throttler.end_iteration(env)){ - logger.log("enter_loading_game(): Game entry timed out. Proceeding with default start delay.", "red"); + env.log("enter_loading_game(): Game entry timed out. Proceeding with default start delay.", "red"); break; } } } - logger.log("start_game_with_inference(): Game started.", "purple"); + env.log("start_game_with_inference(): Game started.", "purple"); if (post_wait_time != 0){ pbf_wait(post_wait_time); @@ -108,7 +107,6 @@ void enter_loading_game( void start_game_from_home_with_inference( ProgramEnvironment& env, - Logger& logger, ConsoleHandle& console, bool tolerate_update_menu, uint8_t game_slot, @@ -159,12 +157,11 @@ void start_game_from_home_with_inference( console.botbase().wait_for_all_requests(); // Wait for game to load. - enter_loading_game(env, logger, console, backup_save, post_wait_time); + enter_loading_game(env, console, backup_save, post_wait_time); } void reset_game_from_home_with_inference( ProgramEnvironment& env, - Logger& logger, ConsoleHandle& console, bool tolerate_update_menu, uint16_t post_wait_time @@ -172,7 +169,7 @@ void reset_game_from_home_with_inference( if (START_GAME_REQUIRES_INTERNET || tolerate_update_menu){ close_game(); start_game_from_home_with_inference( - env, logger, console, tolerate_update_menu, 0, 0, false, post_wait_time + env, console, tolerate_update_menu, 0, 0, false, post_wait_time ); return; } @@ -181,7 +178,7 @@ void reset_game_from_home_with_inference( console.botbase().wait_for_all_requests(); // Wait for game to load. - enter_loading_game(env, logger, console, false, post_wait_time); + enter_loading_game(env, console, false, post_wait_time); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h index e492e3f1b2..73f4512bdf 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_StartGame.h @@ -21,7 +21,6 @@ namespace PokemonSwSh{ // If "user_slot" is zero, it uses whatever the cursor is on. void start_game_from_home_with_inference( ProgramEnvironment& env, - Logger& logger, ConsoleHandle& console, bool tolerate_update_menu, uint8_t game_slot = 0, @@ -32,7 +31,6 @@ void start_game_from_home_with_inference( void reset_game_from_home_with_inference( ProgramEnvironment& env, - Logger& logger, ConsoleHandle& console, bool tolerate_update_menu, uint16_t post_wait_time = 1 * TICKS_PER_SECOND diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ReleaseHelpers.h b/SerialPrograms/Source/PokemonSwSh/Programs/ReleaseHelpers.h index 9dfef193d5..8eda8472ff 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ReleaseHelpers.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ReleaseHelpers.h @@ -7,6 +7,7 @@ #ifndef PokemonAutomation_PokemonSwSh_ReleaseHelpers_H #define PokemonAutomation_PokemonSwSh_ReleaseHelpers_H +#include "Common/Compiler.h" #include "Common/PokemonSwSh/PokemonSwShMisc.h" #include "NintendoSwitch/FixedInterval.h" diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp deleted file mode 100644 index 37b635ff30..0000000000 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* Encounter Stats - * - * From: https://github.com/PokemonAutomation/Arduino-Source - * - */ - -#include "Common/Clientside/PrettyPrint.h" -#include "PokemonSwSh_EncounterStats.h" - -namespace PokemonAutomation{ -namespace NintendoSwitch{ -namespace PokemonSwSh{ - - -EncounterStats::EncounterStats(bool shiny_types) - : m_shiny_types(shiny_types) -{} - -void EncounterStats::log_stats(ProgramEnvironment& env, Logger& logger) const{ - QString str = QString(stats().c_str()); - env.set_status(str); - logger.log("" + str + ""); -} -std::string EncounterStats::stats() const{ - std::string str; - str += str_encounters(); - str += str_shinies(); - return str; -} -std::string EncounterStats::str_encounters() const{ - std::string str; - str += "Encounters: " + tostr_u_commas(m_encounters); - return str; -} -std::string EncounterStats::str_shinies() const{ - std::string str; - if (m_shiny_types){ - str += " - Star Shinies: " + tostr_u_commas(m_star_shinies); - str += " - Square Shinies: " + tostr_u_commas(m_square_shinies); - if (m_unknown_shinies > 0){ - str += " - Unknown Shinies: " + tostr_u_commas(m_unknown_shinies); - } - }else{ - str += " - Shinies: " + tostr_u_commas(m_unknown_shinies); - } - return str; -} - - - -void EncounterStats::add_non_shiny(){ - m_encounters++; -} -void EncounterStats::add_unknown_shiny(){ - m_encounters++; - m_unknown_shinies++; -} -void EncounterStats::add_star_shiny(){ - m_encounters++; - m_star_shinies++; -} -void EncounterStats::add_square_shiny(){ - m_encounters++; - m_square_shinies++; -} - - - -} -} -} - diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h deleted file mode 100644 index 7541a8a4e1..0000000000 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterStats.h +++ /dev/null @@ -1,48 +0,0 @@ -/* Encounter Stats - * - * From: https://github.com/PokemonAutomation/Arduino-Source - * - */ - -#ifndef PokemonAutomation_PokemonSwSh_EncounterStats_H -#define PokemonAutomation_PokemonSwSh_EncounterStats_H - -#include "CommonFramework/Tools/Logger.h" -#include "CommonFramework/Tools/ProgramEnvironment.h" - -namespace PokemonAutomation{ -namespace NintendoSwitch{ -namespace PokemonSwSh{ - - -class EncounterStats{ -public: - EncounterStats(bool shiny_types); - virtual ~EncounterStats() = default; - - void log_stats(ProgramEnvironment& env, Logger& logger) const; - virtual std::string stats() const; - - uint64_t encounters() const{ return m_encounters; } - - virtual std::string str_encounters() const; - virtual std::string str_shinies() const; - - void add_non_shiny(); - void add_unknown_shiny(); - void add_star_shiny(); - void add_square_shiny(); - -protected: - bool m_shiny_types; - uint64_t m_encounters = 0; - uint64_t m_unknown_shinies = 0; - uint64_t m_star_shinies = 0; - uint64_t m_square_shinies = 0; -}; - - -} -} -} -#endif diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp index 2a9a0901b0..97e9802893 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp @@ -13,7 +13,7 @@ namespace PokemonSwSh{ StandardEncounterTracker::StandardEncounterTracker( - EncounterStats& stats, + ShinyHuntTracker& stats, ConsoleHandle& console, bool require_square, uint16_t exit_battle_time diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h index 1e132f096e..5e9327e682 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h @@ -8,8 +8,8 @@ #define PokemonAutomation_PokemonSwSh_EncounterTracker_H #include "CommonFramework/Tools/ConsoleHandle.h" +#include "PokemonSwSh/ShinyHuntTracker.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" -#include "PokemonSwSh_EncounterStats.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -19,7 +19,7 @@ namespace PokemonSwSh{ class StandardEncounterTracker{ public: StandardEncounterTracker( - EncounterStats& stats, + ShinyHuntTracker& stats, ConsoleHandle& console, bool require_square, uint16_t exit_battle_time @@ -30,7 +30,7 @@ class StandardEncounterTracker{ bool process_result(ShinyDetection detection); protected: - EncounterStats& m_stats; + ShinyHuntTracker& m_stats; ConsoleHandle& m_console; bool m_require_square; uint16_t m_exit_battle_time; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHunt-Regi.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHunt-Regi.cpp index fcb2034e51..5890469323 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHunt-Regi.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHunt-Regi.cpp @@ -18,7 +18,7 @@ void move_to_corner( bool correction, uint16_t TRANSITION_DELAY ){ if (correction){ - env.logger.log("Performing auto-correction."); + env.log("Performing auto-correction."); // Move down to building exit and exit. pbf_move_left_joystick(env.console, 128, 255, 500, TRANSITION_DELAY); pbf_move_left_joystick(env.console, 128, 255, 300, TRANSITION_DELAY); @@ -97,23 +97,23 @@ void run_regi_light_puzzle( switch (regi){ case RegiGolem::Regirock: regirock(env.console); - env.logger.log("Starting Regirock Encounter: " + tostr_u_commas(encounter + 1)); + env.log("Starting Regirock Encounter: " + tostr_u_commas(encounter + 1)); break; case RegiGolem::Regice: regice(env.console); - env.logger.log("Starting Regice Encounter: " + tostr_u_commas(encounter + 1)); + env.log("Starting Regice Encounter: " + tostr_u_commas(encounter + 1)); break; case RegiGolem::Registeel: registeel(env.console); - env.logger.log("Starting Registeel Encounter: " + tostr_u_commas(encounter + 1)); + env.log("Starting Registeel Encounter: " + tostr_u_commas(encounter + 1)); break; case RegiGolem::Regieleki: regieleki(env.console); - env.logger.log("Starting Regieleki Encounter: " + tostr_u_commas(encounter + 1)); + env.log("Starting Regieleki Encounter: " + tostr_u_commas(encounter + 1)); break; case RegiGolem::Regidrago: regidrago(env.console); - env.logger.log("Starting Regidrago Encounter: " + tostr_u_commas(encounter + 1)); + env.log("Starting Regidrago Encounter: " + tostr_u_commas(encounter + 1)); break; } } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp index 53eeed01bd..069784d295 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp @@ -51,26 +51,31 @@ ShinyHuntAutonomousBerryTree::ShinyHuntAutonomousBerryTree() +struct ShinyHuntAutonomousBerryTree::Stats : public ShinyHuntTracker{ + Stats() + : ShinyHuntTracker(true) + , m_timeouts(m_stats["Timeouts"]) + { + m_display_order.insert(m_display_order.begin() + 1, Stat("Timeouts")); + } + uint64_t& m_timeouts; +}; +std::unique_ptr ShinyHuntAutonomousBerryTree::make_stats() const{ + return std::unique_ptr(new Stats()); +} -std::string ShinyHuntAutonomousBerryTree::Stats::stats() const{ - std::string str; - str += str_encounters(); - str += " - Timeouts: " + tostr_u_commas(m_timeouts); - str += str_shinies(); - return str; -} void ShinyHuntAutonomousBerryTree::program(SingleSwitchProgramEnvironment& env) const{ grip_menu_connect_go_home(); - Stats stats; + Stats& stats = env.stats(); StandardEncounterTracker tracker(stats, env.console, REQUIRE_SQUARE, EXIT_BATTLE_MASH_TIME); uint8_t year = MAX_YEAR; while (true){ - stats.log_stats(env, env.logger); + env.update_stats(); home_roll_date_enter_game_autorollback(&year); pbf_mash_button(BUTTON_B, 90); @@ -83,7 +88,7 @@ void ShinyHuntAutonomousBerryTree::program(SingleSwitchProgramEnvironment& env) bool timed_out = false; do{ if (detector.has_timed_out()){ - env.logger.log("ScreenChangeDetector: Timed out.", Qt::red); + env.log("ScreenChangeDetector: Timed out.", Qt::red); stats.m_timeouts++; timed_out = true; break; @@ -101,7 +106,7 @@ void ShinyHuntAutonomousBerryTree::program(SingleSwitchProgramEnvironment& env) // Detect shiny. ShinyDetection detection = detect_shiny_battle( - env, env.console, env.logger, + env, env.console, SHINY_BATTLE_REGULAR, std::chrono::seconds(30) ); @@ -118,7 +123,7 @@ void ShinyHuntAutonomousBerryTree::program(SingleSwitchProgramEnvironment& env) pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_FAST); } - stats.log_stats(env, env.logger); + env.update_stats(); pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h index 079ca3a2e0..160db21019 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h @@ -11,7 +11,6 @@ #include "CommonFramework/Options/BooleanCheckBox.h" #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" -#include "PokemonSwSh_EncounterStats.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -21,14 +20,11 @@ class ShinyHuntAutonomousBerryTree : public SingleSwitchProgram{ public: ShinyHuntAutonomousBerryTree(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: - struct Stats : public EncounterStats{ - Stats() : EncounterStats(true) {} - virtual std::string stats() const override; - uint64_t m_timeouts = 0; - }; + struct Stats; BooleanCheckBox GO_HOME_WHEN_DONE; BooleanCheckBox REQUIRE_SQUARE; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp index 131dfdb7a7..728e2e2855 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/ShinyHuntTracker.h" #include "PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" #include "PokemonSwSh_EncounterTracker.h" @@ -56,17 +57,27 @@ ShinyHuntAutonomousFishing::ShinyHuntAutonomousFishing() - -std::string ShinyHuntAutonomousFishing::Stats::stats() const{ - std::string str; - str += str_encounters(); - str += " - Misses: " + tostr_u_commas(m_misses); - str += " - Timeouts: " + tostr_u_commas(m_timeouts); - str += " - Unexpected Battles: " + tostr_u_commas(m_unexpected_battles); - str += str_shinies(); - return str; +struct ShinyHuntAutonomousFishing::Stats : public ShinyHuntTracker{ + Stats() + : ShinyHuntTracker(true) + , m_misses(m_stats["Misses"]) + , m_timeouts(m_stats["Timeouts"]) + , m_unexpected_battles(m_stats["Unexpected Battles"]) + { + m_display_order.insert(m_display_order.begin() + 1, Stat("Misses")); + m_display_order.insert(m_display_order.begin() + 2, Stat("Timeouts")); + m_display_order.insert(m_display_order.begin() + 3, Stat("Unexpected Battles")); + } + uint64_t& m_misses; + uint64_t& m_timeouts; + uint64_t& m_unexpected_battles; +}; +std::unique_ptr ShinyHuntAutonomousFishing::make_stats() const{ + return std::unique_ptr(new Stats()); } + + void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) const{ grip_menu_connect_go_home(); resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); @@ -74,11 +85,11 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; uint32_t last_touch = system_clock(); - Stats stats; + Stats& stats = env.stats(); StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); while (true){ - stats.log_stats(env, env.logger); + env.update_stats(); // Touch the date. if (TIME_ROLLBACK_HOURS > 0 && system_clock() - last_touch >= PERIOD){ @@ -97,7 +108,7 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co pbf_press_button(BUTTON_A, 10, 10); pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); env.console.botbase().wait_for_all_requests(); - FishingDetector::Detection detection = detector.wait_for_detection(env, env.logger); + FishingDetector::Detection detection = detector.wait_for_detection(env); switch (detection){ case FishingDetector::NO_DETECTION: stats.m_timeouts++; @@ -126,7 +137,7 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co // Detect shiny. ShinyDetection detection = detect_shiny_battle( - env, env.console, env.logger, + env, env.console, SHINY_BATTLE_REGULAR, std::chrono::seconds(30) ); @@ -141,7 +152,7 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co } } - stats.log_stats(env, env.logger); + env.update_stats(); if (GO_HOME_WHEN_DONE){ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h index 40e8c1829a..a364aae517 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h @@ -12,7 +12,6 @@ #include "CommonFramework/Options/SimpleInteger.h" #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" -#include "PokemonSwSh_EncounterStats.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -23,16 +22,11 @@ class ShinyHuntAutonomousFishing : public SingleSwitchProgram{ public: ShinyHuntAutonomousFishing(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: - struct Stats : public EncounterStats{ - Stats() : EncounterStats(true) {} - virtual std::string stats() const override; - uint64_t m_misses = 0; - uint64_t m_timeouts = 0; - uint64_t m_unexpected_battles = 0; - }; + struct Stats; BooleanCheckBox GO_HOME_WHEN_DONE; SectionDivider m_advanced_options; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp index ef5f67a703..20e75a767c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "PokemonSwSh/ShinyHuntTracker.h" #include "PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h" #include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" #include "PokemonSwSh_ShinyHuntAutonomous-IoATrade.h" @@ -49,25 +50,33 @@ ShinyHuntAutonomousIoATrade::ShinyHuntAutonomousIoATrade() -std::string ShinyHuntAutonomousIoATrade::Stats::stats() const{ - std::string str; - str += "Trades: " + tostr_u_commas(m_encounters); - str += " - Errors: " + tostr_u_commas(m_errors); - str += str_shinies(); - return str; + +struct ShinyHuntAutonomousIoATrade::Stats : public ShinyHuntTracker{ + Stats() + : ShinyHuntTracker(false) + , m_errors(m_stats["Errors"]) + { + m_display_order.insert(m_display_order.begin() + 1, Stat("Errors")); + } + uint64_t& m_errors; +}; +std::unique_ptr ShinyHuntAutonomousIoATrade::make_stats() const{ + return std::unique_ptr(new Stats()); } + + void ShinyHuntAutonomousIoATrade::program(SingleSwitchProgramEnvironment& env) const{ grip_menu_connect_go_home(); resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 500); uint32_t last_touch = system_clock() - TOUCH_DATE_INTERVAL; - Stats stats; + Stats& stats = env.stats(); while (true){ - stats.log_stats(env, env.logger); + env.update_stats(); pbf_press_button(BUTTON_A, 10, 100); pbf_press_button(BUTTON_A, 10, 60); @@ -89,7 +98,7 @@ void ShinyHuntAutonomousIoATrade::program(SingleSwitchProgramEnvironment& env) c SummaryShinySymbolDetector::Detection detection; { - SummaryShinySymbolDetector detector(env.console, env.logger); + SummaryShinySymbolDetector detector(env.console, env.logger()); detection = detector.wait_for_detection(env); } switch (detection){ @@ -108,18 +117,18 @@ void ShinyHuntAutonomousIoATrade::program(SingleSwitchProgramEnvironment& env) c pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); if (TOUCH_DATE_INTERVAL > 0 && system_clock() - last_touch >= TOUCH_DATE_INTERVAL){ - env.logger.log("Touching date to prevent rollover."); + env.log("Touching date to prevent rollover."); touch_date_from_home(SETTINGS_TO_HOME_DELAY); last_touch += TOUCH_DATE_INTERVAL; } reset_game_from_home_with_inference( - env, env.logger, env.console, + env, env.console, TOLERATE_SYSTEM_UPDATE_MENU_FAST ); } StopProgram: - stats.log_stats(env, env.logger); + env.update_stats(); if (GO_HOME_WHEN_DONE){ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h index 460f6cd0f5..91f719484f 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h @@ -11,7 +11,6 @@ #include "CommonFramework/Options/BooleanCheckBox.h" #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" -#include "PokemonSwSh_EncounterStats.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -21,14 +20,11 @@ class ShinyHuntAutonomousIoATrade : public SingleSwitchProgram{ public: ShinyHuntAutonomousIoATrade(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: - struct Stats : public EncounterStats{ - Stats() : EncounterStats(false) {} - virtual std::string stats() const override; - uint64_t m_errors = 0; - }; + struct Stats; BooleanCheckBox GO_HOME_WHEN_DONE; SectionDivider m_advanced_options; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp index 02b7c2a134..8f0d977d67 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp @@ -13,11 +13,13 @@ #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" #include "CommonFramework/Inference/ImageTools.h" #include "CommonFramework/Inference/InferenceThrottler.h" +#include "PokemonSwSh/ShinyHuntTracker.h" #include "PokemonSwSh/Inference/PokemonSwSh_MarkFinder.h" #include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" #include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" +#include "PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.h" #include "PokemonSwSh_ShinyHuntAutonomous-Overworld.h" namespace PokemonAutomation{ @@ -41,15 +43,20 @@ ShinyHuntAutonomousOverworld::ShinyHuntAutonomousOverworld() " This prioritizes random grass encounters and " + STRING_POKEMON + " that flee.", true ) - , ENABLE_CIRCLING( - "Enable Circling:
After moving towards a " + STRING_POKEMON + ", make a circle." + , TARGET_CIRCLING( + "Target Circling:
After moving towards a " + STRING_POKEMON + ", make a circle." " This increases the chance of encountering the " + STRING_POKEMON + " if it has moved or if the trajectory missed.", true ) + , LOCAL_CIRCLING( + "Local Circling:
If nothing is found after this many whistles, run in a circle." + " Set this to zero to disable this feature.", + 3, 0, 10 + ) , MAX_MOVE_DURATION( "Maximum Move Duration:
Do not move in the same direction for more than this long." " If you set this too high, you may wander too far from the grassy area.", - "150" + "200" ) , WATCHDOG_TIMER( "Watchdog Timer:
Reset the game if you go this long without any encounters.", @@ -69,7 +76,8 @@ ShinyHuntAutonomousOverworld::ShinyHuntAutonomousOverworld() { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&PRIORITIZE_EXCLAMATION_POINTS, "PRIORITIZE_EXCLAMATION_POINTS"); - m_options.emplace_back(&ENABLE_CIRCLING, "ENABLE_CIRCLING"); + m_options.emplace_back(&TARGET_CIRCLING, "ENABLE_CIRCLING"); + m_options.emplace_back(&LOCAL_CIRCLING, "LOCAL_CIRCLING"); m_options.emplace_back(&MAX_MOVE_DURATION, "MAX_MOVE_DURATION"); m_options.emplace_back(&WATCHDOG_TIMER, "WATCHDOG_TIMER"); m_options.emplace_back(&m_advanced_options, ""); @@ -78,273 +86,271 @@ ShinyHuntAutonomousOverworld::ShinyHuntAutonomousOverworld() } -std::string ShinyHuntAutonomousOverworld::Stats::stats() const{ - std::string str; - str += str_encounters(); - str += " - Timeouts: " + tostr_u_commas(m_timeouts); - str += " - Unexpected Battles: " + tostr_u_commas(m_unexpected_battles); - str += " - Resets: " + tostr_u_commas(m_resets); - str += str_shinies(); - return str; -} +struct ShinyHuntAutonomousOverworld::Stats : public ShinyHuntTracker{ + Stats() + : ShinyHuntTracker(true) + , m_errors(m_stats["Errors"]) + , m_resets(m_stats["Resets"]) + { + m_display_order.insert(m_display_order.begin() + 1, Stat("Errors")); + m_display_order.insert(m_display_order.begin() + 2, Stat("Resets")); + } + uint64_t& m_errors; + uint64_t& m_resets; +}; +std::unique_ptr ShinyHuntAutonomousOverworld::make_stats() const{ + return std::unique_ptr(new Stats()); +} -struct Trajectory{ - uint16_t distance_in_ticks; - uint8_t joystick_x; - uint8_t joystick_y; -}; -const Trajectory TRAJECTORY_TABLE[8][6] = { - {{400,128, 0},{420,160, 0},{420,190, 0},{480,208, 0},{480,208, 0},{480,208, 0},}, - {{210,128, 0},{220,164, 0},{250,200, 0},{280,208, 0},{300,209, 0},{320,209, 0},}, - {{120,128, 0},{120,192, 0},{140,209, 0},{180,224, 0},{220,224, 0},{260,232, 0},}, - {{ 60,128, 0},{ 65,224, 0},{ 95,255, 32},{125,255, 48},{160,255, 48},{200,255, 48},}, - {{ 0,128,128},{ 40,255,110},{ 70,255,105},{100,255,100},{130,255, 95},{160,255, 90},}, - {{ 35,128,255},{ 45,255,255},{ 70,255,170},{ 90,255,160},{115,255,140},{140,255,128},}, - {{ 55,128,255},{ 60,192,255},{ 75,255,255},{ 90,255,192},{110,255,176},{135,255,144},}, - {{ 75,128,255},{ 80,160,255},{ 85,216,255},{ 95,255,224},{115,255,192},{130,255,176},}, -}; -Trajectory get_trajectory_int(int delta_x, int delta_y){ - delta_x = std::max(delta_x, -5); - delta_x = std::min(delta_x, +5); - delta_y = std::max(delta_y, -4); - delta_y = std::min(delta_y, +3); - - if (delta_x < 0){ - Trajectory entry = get_trajectory_int(-delta_x, delta_y); - entry.joystick_x = 256 - entry.joystick_x; - return entry; - } +void ShinyHuntAutonomousOverworld::move_in_circle( + SingleSwitchProgramEnvironment& env, + uint8_t size_ticks, + uint8_t current_direction_x, + uint8_t current_direction_y +) const{ +// cout << "size_ticks = " << (int)size_ticks << endl; - return TRAJECTORY_TABLE[delta_y + 4][delta_x]; -} -Trajectory get_trajectory_float(double delta_x, double delta_y){ - delta_x *= 10; - delta_y *= 10; - - int int_x = std::floor(delta_x); - int int_y = std::floor(delta_y); - - double frac_x = delta_x - int_x; - double frac_y = delta_y - int_y; - - Trajectory corner11 = get_trajectory_int(int_x, int_y); - Trajectory corner01 = get_trajectory_int(int_x + 1, int_y); - Trajectory corner10 = get_trajectory_int(int_x, int_y + 1); - Trajectory corner00 = get_trajectory_int(int_x + 1, int_y + 1); - - double top_x = corner11.joystick_x * (1.0 - frac_x) + corner01.joystick_x * frac_x; - double bot_x = corner10.joystick_x * (1.0 - frac_x) + corner00.joystick_x * frac_x; - double x = top_x * (1.0 - frac_y) + bot_x * frac_y; -// cout << "top_x = " << top_x << endl; -// cout << "bot_x = " << bot_x << endl; -// cout << "x = " << x << endl; - - double top_y = corner11.joystick_y * (1.0 - frac_y) + corner10.joystick_y * frac_y; - double bot_y = corner01.joystick_y * (1.0 - frac_y) + corner00.joystick_y * frac_y; - double y = top_y * (1.0 - frac_x) + bot_y * frac_x; - - double top_d = corner11.distance_in_ticks * (1.0 - frac_x) + corner01.distance_in_ticks * frac_x; - double bot_d = corner10.distance_in_ticks * (1.0 - frac_x) + corner00.distance_in_ticks * frac_x; - double d = top_d * (1.0 - frac_y) + bot_d * frac_y; - - x -= 128; - y -= 128; - double scale = std::max(std::abs(x), std::abs(y)); - if (scale == 0){ - x = 128; - y = 128; + if (current_direction_x <= 128){ + pbf_move_left_joystick(env.console, 0, 128, size_ticks, 0); // Correct for bias. + pbf_move_left_joystick(env.console, 128, 0, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 0, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 128, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 255, size_ticks, 0); + pbf_move_left_joystick(env.console, 128, 255, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 255, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 128, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 0, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 128, size_ticks, 0); // Correct for bias. }else{ - scale = 128 / scale; - x *= scale; - y *= scale; + pbf_move_left_joystick(env.console, 255, 128, size_ticks, 0); // Correct for bias. + pbf_move_left_joystick(env.console, 128, 0, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 0, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 128, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 255, size_ticks, 0); + pbf_move_left_joystick(env.console, 128, 255, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 255, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 128, size_ticks, 0); + pbf_move_left_joystick(env.console, 255, 0, size_ticks, 0); + pbf_move_left_joystick(env.console, 0, 128, size_ticks, 0); // Correct for bias. } - x += 128; - y += 128; +} - d = std::max(d, 0.0); - d = std::min(d, 65535.); - x = std::max(x, 0.0); - y = std::max(y, 0.0); - x = std::min(x, 255.); - y = std::min(y, 255.); - return Trajectory{(uint16_t)d, (uint8_t)x, (uint8_t)y}; -} +ShinyHuntAutonomousOverworld::WatchResult ShinyHuntAutonomousOverworld::whistle_and_watch( + SingleSwitchProgramEnvironment& env, + std::vector& exclamations, + std::vector& questions +) const{ + StandardBattleMenuDetector battle_menu(env.console); + StartBattleDetector start_battle(env.console, std::chrono::milliseconds(0)); + InferenceBoxScope search_area(env.console, 0.0, 0.2, 1.0, 0.8); + const double center_x = 0.5; + const double center_y = 0.70; + InferenceBoxScope self(env.console, Qt::cyan, center_x - 0.02, center_y - 0.05, 0.04, 0.1); -void ShinyHuntAutonomousOverworld::move_in_circle(SingleSwitchProgramEnvironment& env, uint8_t size_ticks) const{ -// cout << "size_ticks = " << (int)size_ticks << endl; - pbf_move_left_joystick(env.console, 0, 128, size_ticks, 0); // Correct for bias. - - size_ticks *= 2; - pbf_move_left_joystick(env.console, 128, 0, size_ticks, 0); - pbf_move_left_joystick(env.console, 255, 0, size_ticks, 0); - pbf_move_left_joystick(env.console, 255, 128, size_ticks, 0); - pbf_move_left_joystick(env.console, 255, 255, size_ticks, 0); - pbf_move_left_joystick(env.console, 128, 255, size_ticks, 0); - pbf_move_left_joystick(env.console, 0, 255, size_ticks, 0); - pbf_move_left_joystick(env.console, 0, 128, size_ticks, 0); - pbf_move_left_joystick(env.console, 0, 0, size_ticks, 0); + // Whistle + pbf_press_button(env.console, BUTTON_LCLICK, 5, 0); + + std::deque detection_boxes; + + size_t count = 0; + + InferenceThrottler throttler(std::chrono::milliseconds(1000), std::chrono::milliseconds(50)); + while (true){ + env.check_stopping(); + + QImage screen = env.console.video().snapshot(); + + // Check if a battle has started. + if (battle_menu.detect(screen)){ + return WatchResult::BATTLE_MENU; + } + if (start_battle.detect(screen)){ + return WatchResult::BATTLE_START; + } + + // Look for exclamation points and question marks. + QImage search_image = extract_box(screen, search_area); + + std::vector exclamation_marks; + std::vector question_marks; + count += find_marks( + search_image, + &exclamation_marks, + &question_marks + ); + + detection_boxes.clear(); + for (const PixelBox& mark : exclamation_marks){ + InferenceBox box = translate_to_parent(screen, search_area, mark); + box.color = Qt::magenta; + box.x -= box.width; + box.width *= 3; + box.height *= 2; + detection_boxes.emplace_back(env.console, box); + exclamations.emplace_back(box); + } + for (const PixelBox& mark : question_marks){ + InferenceBox box = translate_to_parent(screen, search_area, mark); + box.color = Qt::magenta; + box.x -= box.width / 2; + box.width *= 2; + box.height *= 2; + detection_boxes.emplace_back(env.console, box); + questions.emplace_back(box); + } + + if (throttler.end_iteration(env)){ + return WatchResult::TIMEOUT; + } + } } + + bool ShinyHuntAutonomousOverworld::find_encounter( SingleSwitchProgramEnvironment& env, Stats& stats, StandardEncounterTracker& tracker ) const{ - InferenceBoxScope search_area(env.console, 0.0, 0.2, 1.0, 0.8); - StandardBattleMenuDetector battle_menu(env.console); - StartBattleDetector start_battle(env.console, std::chrono::milliseconds(0)); - const double center_x = 0.5; const double center_y = 0.70; InferenceBoxScope self(env.console, Qt::cyan, center_x - 0.02, center_y - 0.05, 0.04, 0.1); const std::chrono::milliseconds TIMEOUT((uint64_t)WATCHDOG_TIMER * 1000 / TICKS_PER_SECOND); + size_t nothing_found_counter = 0; + auto last = std::chrono::system_clock::now(); -// size_t c = 0; while (true){ - std::deque boxes; - InferenceThrottler throttler(std::chrono::seconds(1)); - - std::multimap detections; - do{ - env.check_stopping(); - - // No battle for a long time. Reset the game. - auto now = std::chrono::system_clock::now(); - if (now - last > TIMEOUT){ - pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); - reset_game_from_home_with_inference( - env, env.logger, - env.console, - TOLERATE_SYSTEM_UPDATE_MENU_FAST - ); - stats.m_resets++; - return false; - } + // No battle for a long time. Reset the game. + auto now = std::chrono::system_clock::now(); + if (now - last > TIMEOUT){ + pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); + reset_game_from_home_with_inference( + env, env.console, + TOLERATE_SYSTEM_UPDATE_MENU_FAST + ); + stats.m_resets++; + return false; + } - // Grab screenshot. - QImage screen = env.console.video().snapshot(); -// screen.save("test-" + QString::number(c++) + ".png"); + std::vector exclamation_marks; + std::vector question_marks; + WatchResult result = whistle_and_watch( + env, + exclamation_marks, + question_marks + ); + switch (result){ + case WatchResult::BATTLE_MENU: + stats.m_errors++; + tracker.run_away(); + return false; + case WatchResult::BATTLE_START: + env.log("Battle started!"); + return true; + default:; + } - // Check if a battle has started. - if (battle_menu.detect(screen)){ - stats.m_unexpected_battles++; - tracker.run_away(); - return false; - } - if (start_battle.detect(screen)){ - env.logger.log("Battle started!"); - return true; + // Nothing was found. Rotate the view and try again. + if (exclamation_marks.empty() && question_marks.empty()){ + env.log("Nothing found. Rotating view."); + nothing_found_counter++; + if (LOCAL_CIRCLING != 0 && nothing_found_counter >= LOCAL_CIRCLING){ + move_in_circle(env, 32, 0, 0); + nothing_found_counter = 0; + }else{ + pbf_move_right_joystick(192, 255, 50, 70); } + env.console.botbase().wait_for_all_requests(); + continue; + } - // Look for exclamation points and question marks. - QImage search_image = extract_box(screen, search_area); + nothing_found_counter = 0; - std::vector exclamation_marks; - std::vector question_marks; + std::multimap> exclamations; + for (const InferenceBox& box : exclamation_marks){ + double delta_x = box.x + box.width / 2 - center_x; + double delta_y = box.y + box.height * 1.5 - center_y; + env.log( + "Exclamation at: [" + QString::number(delta_x) + " , " + QString::number(-delta_y) + "]", + "purple" + ); + Trajectory trajectory = get_trajectory_float(delta_x, delta_y); + exclamations.emplace(trajectory.distance_in_ticks, std::pair(trajectory, box)); + break; + } - size_t count = find_marks( - search_image, - &exclamation_marks, - &question_marks + std::multimap> questions; + for (const InferenceBox& box : question_marks){ + double delta_x = box.x + box.width / 2 - center_x; + double delta_y = box.y + box.height * 1.5 - center_y; + env.log( + "Question at: [" + QString::number(delta_x) + " , " + QString::number(-delta_y) + "]", + "purple" ); - if (count == 0){ - pbf_press_button(BUTTON_LCLICK, 5, 0); -// move_in_circle(env, 5); - env.console.botbase().wait_for_all_requests(); - boxes.clear(); - continue; - } + Trajectory trajectory = get_trajectory_float(delta_x, delta_y); + questions.emplace(trajectory.distance_in_ticks, std::pair(trajectory, box)); + break; + } - boxes.clear(); - for (const PixelBox& mark : exclamation_marks){ - InferenceBox box = translate_to_parent(screen, search_area, mark); - box.color = Qt::magenta; - box.x -= box.width; - box.width *= 3; - box.height *= 2; - boxes.emplace_back(env.console, box); - - double delta_x = box.x + box.width / 2 - center_x; - double delta_y = box.y + box.height * 1.5 - center_y; -// double distance = delta_x*delta_x + delta_y*delta_y; - env.logger.log( - "Exclamation at: [" + QString::number(delta_x) + " , " + QString::number(-delta_y) + "]", - "purple" - ); - // cout << std::sqrt(distance) << endl; - Trajectory trajectory = get_trajectory_float(delta_x, delta_y); - detections.emplace(trajectory.distance_in_ticks, trajectory); - break; - } - for (const PixelBox& mark : question_marks){ - InferenceBox box = translate_to_parent(screen, search_area, mark); - box.color = Qt::magenta; - box.x -= box.width / 2; - box.width *= 2; - box.height *= 2; - boxes.emplace_back(env.console, box); - - double delta_x = box.x + box.width / 2 - center_x; - double delta_y = box.y + box.height * 1.5 - center_y; -// double distance = delta_x*delta_x + delta_y*delta_y; - env.logger.log( - "Question at: [" + QString::number(delta_x) + " , " + QString::number(-delta_y) + "]", - "purple" - ); - // cout << std::sqrt(distance) << endl; - if (!PRIORITIZE_EXCLAMATION_POINTS || exclamation_marks.empty()){ - Trajectory trajectory = get_trajectory_float(delta_x, delta_y); - detections.emplace(trajectory.distance_in_ticks, trajectory); - } - break; - } - if (!detections.empty()){ - break; - } - }while (!throttler.end_iteration(env)); + // Pick a target. + std::pair target; + target.first.distance_in_ticks = (uint16_t)0 - 1; - // Nothing was found. Rotate the view and try again. - if (detections.empty()){ - env.logger.log("Nothing found. Rotating view."); - pbf_move_right_joystick(192, 255, 50, 0); -// move_in_circle(env, 5); - env.console.botbase().wait_for_all_requests(); - continue; + if (PRIORITIZE_EXCLAMATION_POINTS && !exclamations.empty()){ + questions.clear(); + } + if (!exclamations.empty() && target.first.distance_in_ticks > exclamations.begin()->first){ + target = exclamations.begin()->second; + } + if (!questions.empty() && target.first.distance_in_ticks > questions.begin()->first){ + target = questions.begin()->second; } - auto target = detections.begin(); - const Trajectory& trajectory = target->second; + target.second.color = Qt::yellow; + InferenceBoxScope target_box(env.console, target.second); - double angle = std::atan2((double)trajectory.joystick_y - 128, (double)trajectory.joystick_x - 128) * 57.295779513082320877; - env.logger.log("Found something. Distance: " + QString::number(trajectory.distance_in_ticks) + ", Direction: " + QString::number(-angle) + " degrees"); + double angle = std::atan2( + (double)target.first.joystick_y - 128, + (double)target.first.joystick_x - 128 + ) * 57.295779513082320877; + env.log( + "Found something. Distance: " + QString::number(target.first.distance_in_ticks) + + ", Direction: " + QString::number(-angle) + " degrees" + ); - int duration = trajectory.distance_in_ticks + 30; + + + // Move towards target. + + int duration = target.first.distance_in_ticks + 30; if (duration > (int)MAX_MOVE_DURATION){ duration = MAX_MOVE_DURATION; } - pbf_wait(50); pbf_move_left_joystick( - trajectory.joystick_x, - trajectory.joystick_y, + target.first.joystick_x, + target.first.joystick_y, (uint16_t)duration, 0 ); // Circle Maneuver - if (ENABLE_CIRCLING){ - move_in_circle(env, 8); + if (TARGET_CIRCLING){ + move_in_circle( + env, 16, + target.first.joystick_x, + target.first.joystick_y + ); } env.console.botbase().wait_for_all_requests(); -// boxes.clear(); } } @@ -356,12 +362,12 @@ void ShinyHuntAutonomousOverworld::program(SingleSwitchProgramEnvironment& env) const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; uint32_t last_touch = system_clock(); - Stats stats; + Stats& stats = env.stats(); StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); // Encounter Loop while (true){ - stats.log_stats(env, env.logger); + env.update_stats(); // Touch the date. if (TIME_ROLLBACK_HOURS > 0 && system_clock() - last_touch >= PERIOD){ @@ -379,7 +385,7 @@ void ShinyHuntAutonomousOverworld::program(SingleSwitchProgramEnvironment& env) // Detect shiny. ShinyDetection detection = detect_shiny_battle( - env, env.console, env.logger, + env, env.console, SHINY_BATTLE_REGULAR, std::chrono::seconds(30) ); @@ -388,13 +394,13 @@ void ShinyHuntAutonomousOverworld::program(SingleSwitchProgramEnvironment& env) break; } if (detection == ShinyDetection::NO_BATTLE_MENU){ - stats.m_timeouts++; + stats.m_errors++; pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); tracker.run_away(); } } - stats.log_stats(env, env.logger); + env.update_stats(); if (GO_HOME_WHEN_DONE){ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h index 6256a2fd06..da270e61e2 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h @@ -13,7 +13,6 @@ #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" #include "PokemonSwSh_EncounterTracker.h" -#include "PokemonSwSh_EncounterStats.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -23,19 +22,31 @@ class ShinyHuntAutonomousOverworld : public SingleSwitchProgram{ public: ShinyHuntAutonomousOverworld(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: - struct Stats : public EncounterStats{ - Stats() : EncounterStats(true) {} - virtual std::string stats() const override; - uint64_t m_timeouts = 0; - uint64_t m_unexpected_battles = 0; - uint64_t m_resets = 0; + struct Stats; + + void move_in_circle( + SingleSwitchProgramEnvironment& env, + uint8_t size_ticks, + uint8_t current_direction_x, + uint8_t current_direction_y + ) const; + + enum WatchResult{ + TIMEOUT, + BATTLE_START, + BATTLE_MENU, }; + WatchResult whistle_and_watch( + SingleSwitchProgramEnvironment& env, + std::vector& exclamations, + std::vector& questions + ) const; - void move_in_circle(SingleSwitchProgramEnvironment& env, uint8_t size_ticks) const; bool find_encounter( SingleSwitchProgramEnvironment& env, Stats& stats, @@ -46,7 +57,8 @@ class ShinyHuntAutonomousOverworld : public SingleSwitchProgram{ private: BooleanCheckBox GO_HOME_WHEN_DONE; BooleanCheckBox PRIORITIZE_EXCLAMATION_POINTS; - BooleanCheckBox ENABLE_CIRCLING; + BooleanCheckBox TARGET_CIRCLING; + SimpleInteger LOCAL_CIRCLING; TimeExpression MAX_MOVE_DURATION; TimeExpression WATCHDOG_TIMER; SectionDivider m_advanced_options; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp index 8550052fef..910fb2140c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp @@ -62,37 +62,44 @@ ShinyHuntAutonomousRegi::ShinyHuntAutonomousRegi() -std::string ShinyHuntAutonomousRegi::Stats::stats() const{ - std::string str; - str += str_encounters(); - str += " - Light Resets: " + tostr_u_commas(m_light_resets); - str += str_shinies(); - return str; +struct ShinyHuntAutonomousRegi::Stats : public ShinyHuntTracker{ + Stats() + : ShinyHuntTracker(true) + , m_light_resets(m_stats["Light Resets"]) + { + m_display_order.insert(m_display_order.begin() + 1, Stat("Light Resets")); + } + uint64_t& m_light_resets; +}; +std::unique_ptr ShinyHuntAutonomousRegi::make_stats() const{ + return std::unique_ptr(new Stats()); } + + void ShinyHuntAutonomousRegi::program(SingleSwitchProgramEnvironment& env) const{ grip_menu_connect_go_home(); resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 200); - Stats stats; + Stats& stats = env.stats(); StandardEncounterTracker tracker(stats, env.console, REQUIRE_SQUARE, EXIT_BATTLE_MASH_TIME); uint32_t last_touch = system_clock() - TOUCH_DATE_INTERVAL; bool error = false; while (true){ - stats.log_stats(env, env.logger); + env.update_stats(); move_to_corner(env, error, TRANSITION_DELAY); if (error){ stats.m_light_resets++; - stats.log_stats(env, env.logger); + env.update_stats(); error = false; } // Touch the date. if (TOUCH_DATE_INTERVAL > 0 && system_clock() - last_touch >= TOUCH_DATE_INTERVAL){ - env.logger.log("Touching date to prevent rollover."); + env.log("Touching date to prevent rollover."); pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); touch_date_from_home(SETTINGS_TO_HOME_DELAY); resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); @@ -108,7 +115,7 @@ void ShinyHuntAutonomousRegi::program(SingleSwitchProgramEnvironment& env) const // Detect shiny. ShinyDetection detection = detect_shiny_battle( - env, env.console, env.logger, + env, env.console, SHINY_BATTLE_REGULAR, std::chrono::seconds(30) ); @@ -123,7 +130,7 @@ void ShinyHuntAutonomousRegi::program(SingleSwitchProgramEnvironment& env) const } } - stats.log_stats(env, env.logger); + env.update_stats(); if (GO_HOME_WHEN_DONE){ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h index 794cf7ae1f..78643f9419 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h @@ -13,7 +13,6 @@ #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" #include "PokemonSwSh/Options/RegiSelector.h" -#include "PokemonSwSh_EncounterStats.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -23,14 +22,11 @@ class ShinyHuntAutonomousRegi : public SingleSwitchProgram{ public: ShinyHuntAutonomousRegi(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: - struct Stats : public EncounterStats{ - Stats() : EncounterStats(true) {} - virtual std::string stats() const override; - uint64_t m_light_resets = 0; - }; + struct Stats; BooleanCheckBox GO_HOME_WHEN_DONE; RegiSelector REGI_NAME; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp index 30380b4bbd..d255b77e8c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp @@ -62,32 +62,38 @@ ShinyHuntAutonomousRegigigas2::ShinyHuntAutonomousRegigigas2() -std::string ShinyHuntAutonomousRegigigas2::Stats::stats() const{ - std::string str; - str += str_encounters(); - str += " - Timeouts: " + tostr_u_commas(m_timeouts); - str += str_shinies(); - return str; +struct ShinyHuntAutonomousRegigigas2::Stats : public ShinyHuntTracker{ + Stats() + : ShinyHuntTracker(true) + , m_timeouts(m_stats["Timeouts"]) + { + m_display_order.insert(m_display_order.begin() + 1, Stat("Timeouts")); + } + uint64_t& m_timeouts; +}; +std::unique_ptr ShinyHuntAutonomousRegigigas2::make_stats() const{ + return std::unique_ptr(new Stats()); } + + + ShinyHuntAutonomousRegigigas2::Tracker::Tracker( - EncounterStats& stats, + ShinyHuntTracker& stats, ProgramEnvironment& env, - Logger& logger, ConsoleHandle& console, bool require_square, uint16_t exit_battle_time ) : StandardEncounterTracker(stats, console, require_square, exit_battle_time) , m_env(env) - , m_logger(logger) {} bool ShinyHuntAutonomousRegigigas2::Tracker::run_away(){ RaidCatchDetector detector(m_console, std::chrono::seconds(30)); pbf_mash_button(BUTTON_A, 4 * TICKS_PER_SECOND); if (!detector.wait(m_env)){ - m_logger.log("Raid Catch Menu not found.", Qt::red); + m_env.log("Raid Catch Menu not found.", Qt::red); return false; } @@ -101,7 +107,7 @@ bool ShinyHuntAutonomousRegigigas2::kill_and_return(SingleSwitchProgramEnvironme pbf_mash_button(BUTTON_A, 4 * TICKS_PER_SECOND); if (!detector.wait(env)){ - env.logger.log("Raid Catch Menu not found.", Qt::red); + env.log("Raid Catch Menu not found.", Qt::red); return false; } @@ -119,25 +125,25 @@ void ShinyHuntAutonomousRegigigas2::program(SingleSwitchProgramEnvironment& env) resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 500); - Stats stats; - Tracker tracker(stats, env, env.logger, env.console, REQUIRE_SQUARE, 0); + Stats& stats = env.stats(); + Tracker tracker(stats, env, env.console, REQUIRE_SQUARE, 0); while (true){ for (uint8_t pp = REVERSAL_PP; pp > 0; pp--){ - stats.log_stats(env, env.logger); + env.update_stats(); - env.logger.log("Starting Regigigas Encounter: " + tostr_u_commas(stats.encounters() + 1)); + env.log("Starting Regigigas Encounter: " + tostr_u_commas(stats.encounters() + 1)); pbf_mash_button(BUTTON_A, 18 * TICKS_PER_SECOND); env.console.botbase().wait_for_all_requests(); { StartBattleDetector detector(env.console, std::chrono::seconds(30)); - detector.wait(env, env.logger); + detector.wait(env); } ShinyDetection detection = detect_shiny_battle( - env, env.console, env.logger, + env, env.console, SHINY_BATTLE_RAID, std::chrono::seconds(30) ); @@ -157,14 +163,14 @@ void ShinyHuntAutonomousRegigigas2::program(SingleSwitchProgramEnvironment& env) last_touch += TOUCH_DATE_INTERVAL; } reset_game_from_home_with_inference( - env, env.logger, env.console, + env, env.console, TOLERATE_SYSTEM_UPDATE_MENU_FAST ); } StopProgram: - stats.log_stats(env, env.logger); + env.update_stats(); if (GO_HOME_WHEN_DONE){ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h index da47aa9222..e7f645d4fb 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h @@ -12,7 +12,6 @@ #include "CommonFramework/Options/SimpleInteger.h" #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" -#include "PokemonSwSh_EncounterStats.h" #include "PokemonSwSh_EncounterTracker.h" namespace PokemonAutomation{ @@ -23,22 +22,18 @@ class ShinyHuntAutonomousRegigigas2 : public SingleSwitchProgram{ public: ShinyHuntAutonomousRegigigas2(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: bool kill_and_return(SingleSwitchProgramEnvironment& env) const; private: - struct Stats : public EncounterStats{ - Stats() : EncounterStats(true) {} - virtual std::string stats() const override; - uint64_t m_timeouts = 0; - }; + struct Stats; struct Tracker : public StandardEncounterTracker{ Tracker( - EncounterStats& stats, + ShinyHuntTracker& stats, ProgramEnvironment& env, - Logger& logger, ConsoleHandle& console, bool require_square, uint16_t exit_battle_time @@ -46,7 +41,6 @@ class ShinyHuntAutonomousRegigigas2 : public SingleSwitchProgram{ virtual bool run_away() override; ProgramEnvironment& m_env; - Logger& m_logger; }; BooleanCheckBox GO_HOME_WHEN_DONE; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp index ce83b6f54e..b4fb27af8f 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp @@ -50,15 +50,24 @@ ShinyHuntAutonomousStrongSpawn::ShinyHuntAutonomousStrongSpawn() } -std::string ShinyHuntAutonomousStrongSpawn::Stats::stats() const{ - std::string str; - str += str_encounters(); - str += " - Timeouts: " + tostr_u_commas(m_timeouts); - str += str_shinies(); - return str; + + +struct ShinyHuntAutonomousStrongSpawn::Stats : public ShinyHuntTracker{ + Stats() + : ShinyHuntTracker(true) + , m_timeouts(m_stats["Timeouts"]) + { + m_display_order.insert(m_display_order.begin() + 1, Stat("Timeouts")); + } + uint64_t& m_timeouts; +}; +std::unique_ptr ShinyHuntAutonomousStrongSpawn::make_stats() const{ + return std::unique_ptr(new Stats()); } -ShinyHuntAutonomousStrongSpawn::Tracker::Tracker(EncounterStats& stats, ConsoleHandle& console) + + +ShinyHuntAutonomousStrongSpawn::Tracker::Tracker(ShinyHuntTracker& stats, ConsoleHandle& console) : StandardEncounterTracker(stats, console, false, 0) {} bool ShinyHuntAutonomousStrongSpawn::Tracker::run_away(){ @@ -73,11 +82,11 @@ void ShinyHuntAutonomousStrongSpawn::program(SingleSwitchProgramEnvironment& env const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; uint32_t last_touch = system_clock(); - Stats stats; + Stats& stats = env.stats(); Tracker tracker(stats, env.console); while (true){ - stats.log_stats(env, env.logger); + env.update_stats(); uint32_t now = system_clock(); if (TIME_ROLLBACK_HOURS > 0 && now - last_touch >= PERIOD){ @@ -85,15 +94,15 @@ void ShinyHuntAutonomousStrongSpawn::program(SingleSwitchProgramEnvironment& env last_touch += PERIOD; } reset_game_from_home_with_inference( - env, env.logger, env.console, + env, env.console, TOLERATE_SYSTEM_UPDATE_MENU_FAST ); - env.logger.log("Starting Encounter: " + tostr_u_commas(stats.encounters() + 1)); + env.log("Starting Encounter: " + tostr_u_commas(stats.encounters() + 1)); env.console.botbase().wait_for_all_requests(); // Detect shiny. ShinyDetection detection = detect_shiny_battle( - env, env.console, env.logger, + env, env.console, SHINY_BATTLE_REGULAR, std::chrono::seconds(30) ); @@ -107,7 +116,7 @@ void ShinyHuntAutonomousStrongSpawn::program(SingleSwitchProgramEnvironment& env } } - stats.log_stats(env, env.logger); + env.update_stats(); if (GO_HOME_WHEN_DONE){ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h index 63e2b83e68..98b75c81ba 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h @@ -12,7 +12,6 @@ #include "CommonFramework/Options/SimpleInteger.h" #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" -#include "PokemonSwSh_EncounterStats.h" #include "PokemonSwSh_EncounterTracker.h" namespace PokemonAutomation{ @@ -23,16 +22,13 @@ class ShinyHuntAutonomousStrongSpawn : public SingleSwitchProgram{ public: ShinyHuntAutonomousStrongSpawn(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: - struct Stats : public EncounterStats{ - Stats() : EncounterStats(true) {} - virtual std::string stats() const override; - uint64_t m_timeouts = 0; - }; + struct Stats; struct Tracker : public StandardEncounterTracker{ - Tracker(EncounterStats& stats, ConsoleHandle& console); + Tracker(ShinyHuntTracker& stats, ConsoleHandle& console); virtual bool run_away() override; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp index 3aee721d25..08c0ceb21f 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp @@ -59,14 +59,23 @@ ShinyHuntAutonomousSwordsOfJustice::ShinyHuntAutonomousSwordsOfJustice() } -std::string ShinyHuntAutonomousSwordsOfJustice::Stats::stats() const{ - std::string str; - str += str_encounters(); - str += " - Timeouts: " + tostr_u_commas(m_timeouts); - str += str_shinies(); - return str; + +struct ShinyHuntAutonomousSwordsOfJustice::Stats : public ShinyHuntTracker{ + Stats() + : ShinyHuntTracker(true) + , m_timeouts(m_stats["Timeouts"]) + { + m_display_order.insert(m_display_order.begin() + 1, Stat("Timeouts")); + } + uint64_t& m_timeouts; +}; +std::unique_ptr ShinyHuntAutonomousSwordsOfJustice::make_stats() const{ + return std::unique_ptr(new Stats()); } + + + void ShinyHuntAutonomousSwordsOfJustice::program(SingleSwitchProgramEnvironment& env) const{ grip_menu_connect_go_home(); resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); @@ -74,11 +83,11 @@ void ShinyHuntAutonomousSwordsOfJustice::program(SingleSwitchProgramEnvironment& const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; uint32_t last_touch = system_clock(); - Stats stats; + Stats& stats = env.stats(); StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); while (true){ - stats.log_stats(env, env.logger); + env.update_stats(); // Touch the date. if (TIME_ROLLBACK_HOURS > 0 && system_clock() - last_touch >= PERIOD){ @@ -97,13 +106,13 @@ void ShinyHuntAutonomousSwordsOfJustice::program(SingleSwitchProgramEnvironment& } pbf_press_button(BUTTON_X, 10, 50); pbf_press_dpad(DPAD_LEFT, 10, 10); - env.logger.log("Starting Encounter: " + tostr_u_commas(stats.encounters() + 1)); + env.log("Starting Encounter: " + tostr_u_commas(stats.encounters() + 1)); pbf_press_button(BUTTON_A, 10, 0); env.console.botbase().wait_for_all_requests(); // Detect shiny. ShinyDetection detection = detect_shiny_battle( - env, env.console, env.logger, + env, env.console, SHINY_BATTLE_REGULAR, std::chrono::seconds(30) ); @@ -118,7 +127,7 @@ void ShinyHuntAutonomousSwordsOfJustice::program(SingleSwitchProgramEnvironment& } } - stats.log_stats(env, env.logger); + env.update_stats(); if (GO_HOME_WHEN_DONE){ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h index 2c4cfd44fe..99e3a5f319 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h @@ -12,7 +12,6 @@ #include "CommonFramework/Options/SimpleInteger.h" #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" -#include "PokemonSwSh_EncounterStats.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -22,14 +21,11 @@ class ShinyHuntAutonomousSwordsOfJustice : public SingleSwitchProgram{ public: ShinyHuntAutonomousSwordsOfJustice(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: - struct Stats : public EncounterStats{ - Stats() : EncounterStats(true) {} - virtual std::string stats() const override; - uint64_t m_timeouts = 0; - }; + struct Stats; BooleanCheckBox GO_HOME_WHEN_DONE; BooleanCheckBox AIRPLANE_MODE; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp index bdf760820d..a625d6d353 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp @@ -51,15 +51,27 @@ ShinyHuntAutonomousWhistling::ShinyHuntAutonomousWhistling() } -std::string ShinyHuntAutonomousWhistling::Stats::stats() const{ - std::string str; - str += str_encounters(); - str += " - Timeouts: " + tostr_u_commas(m_timeouts); - str += " - Unexpected Battles: " + tostr_u_commas(m_unexpected_battles); - str += str_shinies(); - return str; + +struct ShinyHuntAutonomousWhistling::Stats : public ShinyHuntTracker{ + Stats() + : ShinyHuntTracker(true) + , m_timeouts(m_stats["Timeouts"]) + , m_unexpected_battles(m_stats["Unexpected Battles"]) + { + m_display_order.insert(m_display_order.begin() + 1, Stat("Timeouts")); + m_display_order.insert(m_display_order.begin() + 2, Stat("Unexpected Battles")); + } + uint64_t& m_timeouts; + uint64_t& m_unexpected_battles; +}; +std::unique_ptr ShinyHuntAutonomousWhistling::make_stats() const{ + return std::unique_ptr(new Stats()); } + + + + void ShinyHuntAutonomousWhistling::program(SingleSwitchProgramEnvironment& env) const{ grip_menu_connect_go_home(); resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 200); @@ -67,10 +79,10 @@ void ShinyHuntAutonomousWhistling::program(SingleSwitchProgramEnvironment& env) const uint32_t PERIOD = (uint32_t)TIME_ROLLBACK_HOURS * 3600 * TICKS_PER_SECOND; uint32_t last_touch = system_clock(); - Stats stats; + Stats& stats = env.stats(); StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); while (true){ - stats.log_stats(env, env.logger); + env.update_stats(); // Touch the date. if (TIME_ROLLBACK_HOURS > 0 && system_clock() - last_touch >= PERIOD){ @@ -91,7 +103,7 @@ void ShinyHuntAutonomousWhistling::program(SingleSwitchProgramEnvironment& env) do{ screen = env.console.video().snapshot(); if (menu.detect(screen)){ - env.logger.log("ScreenChangeDetector: Unexpected battle menu.", Qt::red); + env.log("ScreenChangeDetector: Unexpected battle menu.", Qt::red); stats.m_unexpected_battles++; unexpected = true; break; @@ -111,7 +123,7 @@ void ShinyHuntAutonomousWhistling::program(SingleSwitchProgramEnvironment& env) // Detect shiny. ShinyDetection detection = detect_shiny_battle( - env, env.console, env.logger, + env, env.console, SHINY_BATTLE_REGULAR, std::chrono::seconds(30) ); @@ -126,7 +138,7 @@ void ShinyHuntAutonomousWhistling::program(SingleSwitchProgramEnvironment& env) } } - stats.log_stats(env, env.logger); + env.update_stats(); if (GO_HOME_WHEN_DONE){ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h index fab716842a..38b59da6ad 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h @@ -12,7 +12,6 @@ #include "CommonFramework/Options/SimpleInteger.h" #include "NintendoSwitch/Options/TimeExpression.h" #include "NintendoSwitch/Framework/SingleSwitchProgram.h" -#include "PokemonSwSh_EncounterStats.h" namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -22,15 +21,11 @@ class ShinyHuntAutonomousWhistling : public SingleSwitchProgram{ public: ShinyHuntAutonomousWhistling(); + virtual std::unique_ptr make_stats() const override; virtual void program(SingleSwitchProgramEnvironment& env) const override; private: - struct Stats : public EncounterStats{ - Stats() : EncounterStats(true) {} - virtual std::string stats() const override; - uint64_t m_timeouts = 0; - uint64_t m_unexpected_battles = 0; - }; + struct Stats; BooleanCheckBox GO_HOME_WHEN_DONE; SectionDivider m_advanced_options; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntTools.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntTools.h index df0bc671b8..02d310357b 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntTools.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntTools.h @@ -7,6 +7,7 @@ #ifndef PokemonAutomation_PokemonSwSh_ShinyHuntingTools_H #define PokemonAutomation_PokemonSwSh_ShinyHuntingTools_H +#include "Common/Compiler.h" #include "Common/SwitchFramework/FrameworkSettings.h" #include "Common/SwitchFramework/Switch_PushButtons.h" #include "Common/PokemonSwSh/PokemonSettings.h" diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp index c5a9f32ea2..0a1f21f5d9 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp @@ -61,7 +61,7 @@ void ShinyHuntUnattendedIoATrade::program(SingleSwitchProgramEnvironment& env) c uint32_t last_touch = system_clock() - TOUCH_DATE_INTERVAL; for (uint32_t c = 0; ; c++){ - env.logger.log("Starting Trade: " + tostr_u_commas(c + 1)); + env.log("Starting Trade: " + tostr_u_commas(c + 1)); pbf_press_button(BUTTON_A, 10, 100); pbf_press_button(BUTTON_A, 10, 60); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp index 04f8652b69..93f4ac167d 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp @@ -74,7 +74,7 @@ void ShinyHuntUnattendedRegi::program(SingleSwitchProgramEnvironment& env) const // Touch the date. if (TOUCH_DATE_INTERVAL > 0 && system_clock() - last_touch >= TOUCH_DATE_INTERVAL){ - env.logger.log("Touching date to prevent rollover."); + env.log("Touching date to prevent rollover."); pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); touch_date_from_home(SETTINGS_TO_HOME_DELAY); resume_game_no_interact(TOLERATE_SYSTEM_UPDATE_MENU_FAST); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp index 2372673ad2..99cffa95d3 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp @@ -68,7 +68,7 @@ void ShinyHuntUnattendedRegigigas2::program(SingleSwitchProgramEnvironment& env) uint32_t encounter = 0; while (true){ for (uint8_t pp = REVERSAL_PP; pp > 0; pp--){ - env.logger.log("Starting Regigigas Encounter: " + tostr_u_commas(++encounter)); + env.log("Starting Regigigas Encounter: " + tostr_u_commas(++encounter)); pbf_press_button(BUTTON_A, 10, 3 * TICKS_PER_SECOND); pbf_press_button(BUTTON_A, 10, TICKS_PER_SECOND); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.cpp index efd52f5b06..9d137c6f34 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.cpp @@ -78,7 +78,7 @@ void ShinyHuntUnattendedStrongSpawn::program(SingleSwitchProgramEnvironment& env pbf_wait(START_GAME_WAIT_DELAY); // Enter game. - env.logger.log("Starting Encounter: " + tostr_u_commas(c + 1)); + env.log("Starting Encounter: " + tostr_u_commas(c + 1)); pbf_press_button(BUTTON_A, 10, ENTER_GAME_TO_RUN_DELAY); // Run away. diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.cpp index 862526da76..94fb1af9ed 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.cpp @@ -76,7 +76,7 @@ void ShinyHuntUnattendedSwordsOfJustice::program(SingleSwitchProgramEnvironment& } pbf_press_button(BUTTON_X, 10, 50); pbf_press_dpad(DPAD_LEFT, 10, 10); - env.logger.log("Starting Encounter: " + tostr_u_commas(c + 1)); + env.log("Starting Encounter: " + tostr_u_commas(c + 1)); pbf_press_button(BUTTON_A, 10, EXIT_CAMP_TO_RUN_DELAY); // Run away if not shiny. diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp index bc70ad17ba..86aacc57dd 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/TestProgram.cpp @@ -6,10 +6,13 @@ #include #include "Common/Clientside/PrettyPrint.h" +#include "ClientSource/Libraries/Logging.h" #include "Common/SwitchFramework/FrameworkSettings.h" #include "Common/SwitchFramework/Switch_PushButtons.h" #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" +#include "CommonFramework/Tools/StatsTracking.h" +#include "CommonFramework/Tools/StatsDatabase.h" #include "CommonFramework/Inference/ImageTools.h" #include "CommonFramework/Inference/InferenceThrottler.h" #include "CommonFramework/Inference/FillGeometry.h" @@ -17,6 +20,7 @@ #include "CommonFramework/Inference/ColorClustering.h" #include "CommonFramework/Inference/StatAccumulator.h" #include "CommonFramework/Inference/TimeWindowStatTracker.h" +#include "PokemonSwSh/ShinyHuntTracker.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyFilters.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SparkleTrigger.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareTrigger.h" @@ -56,15 +60,21 @@ TestProgram::TestProgram() +void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ +// BotBase& botbase = env.console; + VideoFeed& feed = env.console; +#if 0 + StatSet set; + set.open_from_file("test.txt"); +// cout << set.to_str() << endl; + set.save_to_file("test2.txt"); +#endif -void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ - BotBase& botbase = env.console; - VideoFeed& feed = env.console; - +#if 0 // start_game_from_home_with_inference(env, env.logger, env.console, true, 0, 0, true); // std::pair coord = get_direction(-3, -1.0); @@ -73,9 +83,36 @@ void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ // Trajectory trajectory = get_trajectory_float(.09, .09); // cout << trajectory.distance_in_ticks << " : " << (int)trajectory.joystick_x << "," << (int)trajectory.joystick_y << endl; + ShinyHuntTracker tracker(true); + tracker.parse_and_append_line("Encounters: 100 - Star Shinies: 2 - Square Shinies: 1"); +// cout << tracker.to_str() << endl; + +// StatLine line(tracker); +// cout << line.to_str() << endl; + +// StatLine line1("2021-04-08 00:25:12.131850 - Encounters: 100 - Star Shinies: 2 - Square Shinies: 1"); +// cout << line1.to_str() << endl; + + StatList list; + list += tracker; + list += "2021-04-08 00:25:12.131850 - Encounters: 200 - Star Shinies: 1 - Square Shinies: 0"; + + cout << list.to_str() << endl; + + StatSet set; + StatList& program0 = set["program 0"]; + program0 += "Timestamp - Encounters: 100 - Star Shinies: 2 - Square Shinies: 1"; + program0 += "Timestamp - Encounters: 200 - Star Shinies: 1 - Square Shinies: 0"; + StatList& program1 = set["program 1"]; + program1 += "Timestamp - Encounters: 300 - Star Shinies: 3 - Square Shinies: 2"; + program1 += "Timestamp - Encounters: 400 - Star Shinies: 4 - Square Shinies: 1"; + + set.save_to_file("test.txt"); +#endif + #if 0 - QImage image("square-test.png"); + QImage image("test-screen.png"); // QImage image("test-1617471750423682600-O.png"); // QImage image = feed.snapshot(); // image.save("square-test0.png"); @@ -83,6 +120,7 @@ void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ BrightYellowLightFilterDebug filter; matrix.apply_filter(image, filter); + image.save("square-test0.png"); std::vector objects; objects = find_all_objects(matrix, 1, true); @@ -118,8 +156,8 @@ void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ -#if 0 - QImage screen("test-161.png"); +#if 1 + QImage screen("mark-test0.png"); std::vector marks; find_marks(screen, nullptr, &marks); @@ -149,7 +187,7 @@ void TestProgram::program(SingleSwitchProgramEnvironment& env) const{ #if 1 detect_shiny_battle( - env, env.console, env.logger, + env, env.console, SHINY_BATTLE_REGULAR, std::chrono::seconds(60) ); diff --git a/SerialPrograms/Source/PokemonSwSh/ShinyHuntTracker.cpp b/SerialPrograms/Source/PokemonSwSh/ShinyHuntTracker.cpp new file mode 100644 index 0000000000..5dff46bef2 --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/ShinyHuntTracker.cpp @@ -0,0 +1,48 @@ +/* Shiny Hunt Tracker + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "ShinyHuntTracker.h" + +namespace PokemonAutomation{ + + + + +ShinyHuntTracker::ShinyHuntTracker(bool shiny_types) + : m_encounters(m_stats["Encounters"]) + , m_unknown_shinies(m_stats["Unknown Shinies"]) + , m_star_shinies(m_stats["Star Shinies"]) + , m_square_shinies(m_stats["Square Shinies"]) +{ + m_display_order.emplace_back("Encounters"); + if (shiny_types){ + m_display_order.emplace_back("Star Shinies"); + m_display_order.emplace_back("Square Shinies"); + m_display_order.emplace_back("Unknown Shinies", true); + }else{ + m_display_order.emplace_back("Shinies"); + } +} +void ShinyHuntTracker::add_non_shiny(){ + m_encounters++; +} +void ShinyHuntTracker::add_unknown_shiny(){ + m_encounters++; + m_unknown_shinies++; +} +void ShinyHuntTracker::add_star_shiny(){ + m_encounters++; + m_star_shinies++; +} +void ShinyHuntTracker::add_square_shiny(){ + m_encounters++; + m_square_shinies++; +} + + + + +} diff --git a/SerialPrograms/Source/PokemonSwSh/ShinyHuntTracker.h b/SerialPrograms/Source/PokemonSwSh/ShinyHuntTracker.h new file mode 100644 index 0000000000..ecfc59a31f --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/ShinyHuntTracker.h @@ -0,0 +1,36 @@ +/* Shiny Hunt Tracker + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_ShinyHuntTracker_H +#define PokemonAutomation_ShinyHuntTracker_H + +#include "CommonFramework/Tools/StatsTracking.h" + +namespace PokemonAutomation{ + + +class ShinyHuntTracker : public StatsTracker{ +public: + ShinyHuntTracker(bool shiny_types); + + uint64_t encounters() const{ return m_encounters; } + + void add_non_shiny(); + void add_unknown_shiny(); + void add_star_shiny(); + void add_square_shiny(); + +private: + uint64_t& m_encounters; + uint64_t& m_unknown_shinies; + uint64_t& m_star_shinies; + uint64_t& m_square_shinies; +}; + + + +} +#endif From 10dd70ac2f52baef6c82ac5a87229f6a27d48f61 Mon Sep 17 00:00:00 2001 From: Mysticial Date: Sat, 17 Apr 2021 19:29:47 -0500 Subject: [PATCH 13/13] Update to latest release. --- GeneratorConfig/AutoHost-Rolling.json | 2 +- GeneratorConfig/EggCombined2.json | 18 +-- GeneratorConfig/EggSuperCombined2.json | 18 +-- GeneratorConfig/ShinyHunt-Regi.json | 4 - .../ShinyHunt-SwordsOfJustice.json | 18 +-- .../ShinyHuntUnattended-IoATrade.json | 18 +-- GeneratorConfig/ShinyHuntUnattended-Regi.json | 18 +-- .../ShinyHuntUnattended-Regigigas.json | 18 +-- .../ShinyHuntUnattended-Regigigas2.json | 18 +-- .../ShinyHuntUnattended-StrongSpawn.json | 4 - .../ShinyHuntUnattended-SwordsOfJustice.json | 18 +-- NativePrograms/EggCombined2.c | 20 ++-- NativePrograms/EggSuperCombined2.c | 20 ++-- NativePrograms/ShinyHunt-SwordsOfJustice.c | 10 +- NativePrograms/ShinyHuntUnattended-IoATrade.c | 20 ++-- NativePrograms/ShinyHuntUnattended-Regi.c | 20 ++-- .../ShinyHuntUnattended-Regigigas.c | 8 +- .../ShinyHuntUnattended-Regigigas2.c | 14 +-- .../ShinyHuntUnattended-StrongSpawn.c | 14 +-- .../ShinyHuntUnattended-SwordsOfJustice.c | 8 +- SerialPrograms/SerialPrograms.pro | 2 + .../Source/CommonFramework/CrashDump.cpp | 113 ++++++++++++++++++ .../Source/CommonFramework/CrashDump.h | 17 +++ .../Source/CommonFramework/Globals.cpp | 2 +- .../Inference/InferenceThrottler.h | 2 +- .../Source/CommonFramework/Main.cpp | 5 + .../CommonFramework/PersistentSettings.cpp | 2 + .../CommonFramework/PersistentSettings.h | 10 +- .../CommonFramework/Tools/StatsDatabase.cpp | 9 +- .../CommonFramework/Tools/StatsTracking.cpp | 7 +- .../Framework/RunnableSwitchProgram.cpp | 31 +++-- .../Framework/RunnableSwitchProgram.h | 3 + SerialPrograms/Source/PanelList.cpp | 4 +- .../PokemonSwSh_StartBattleDetector.cpp | 38 ++++++ .../PokemonSwSh_StartBattleDetector.h | 23 +++- .../PokemonSwSh_SquareDetector.cpp | 8 +- .../EggPrograms/PokemonSwSh_EggCombined2.cpp | 10 +- .../EggPrograms/PokemonSwSh_EggCombined2.h | 2 +- .../PokemonSwSh_EggSuperCombined2.cpp | 10 +- .../PokemonSwSh_EggSuperCombined2.h | 2 +- .../Hosting/PokemonSwSh_AutoHost-Rolling.cpp | 14 +++ .../Hosting/PokemonSwSh_AutoHost-Rolling.h | 1 + .../PokemonSwSh_OverworldTrajectory.cpp | 4 +- .../PokemonSwSh_EncounterTracker.cpp | 55 ++++++--- .../PokemonSwSh_EncounterTracker.h | 9 +- ...emonSwSh_ShinyHuntAutonomous-BerryTree.cpp | 23 +++- ...okemonSwSh_ShinyHuntAutonomous-BerryTree.h | 2 + ...okemonSwSh_ShinyHuntAutonomous-Fishing.cpp | 46 ++++--- .../PokemonSwSh_ShinyHuntAutonomous-Fishing.h | 4 +- ...kemonSwSh_ShinyHuntAutonomous-IoATrade.cpp | 33 +++-- ...PokemonSwSh_ShinyHuntAutonomous-IoATrade.h | 4 +- ...emonSwSh_ShinyHuntAutonomous-Overworld.cpp | 55 +++++++-- ...okemonSwSh_ShinyHuntAutonomous-Overworld.h | 4 +- .../PokemonSwSh_ShinyHuntAutonomous-Regi.cpp | 31 ++++- .../PokemonSwSh_ShinyHuntAutonomous-Regi.h | 4 +- ...monSwSh_ShinyHuntAutonomous-Regigigas2.cpp | 38 ++++-- ...kemonSwSh_ShinyHuntAutonomous-Regigigas2.h | 8 +- ...onSwSh_ShinyHuntAutonomous-StrongSpawn.cpp | 34 ++++-- ...emonSwSh_ShinyHuntAutonomous-StrongSpawn.h | 11 +- ...Sh_ShinyHuntAutonomous-SwordsOfJustice.cpp | 31 ++++- ...SwSh_ShinyHuntAutonomous-SwordsOfJustice.h | 4 +- ...emonSwSh_ShinyHuntAutonomous-Whistling.cpp | 32 ++++- ...okemonSwSh_ShinyHuntAutonomous-Whistling.h | 4 +- ...kemonSwSh_ShinyHuntUnattended-IoATrade.cpp | 10 +- ...PokemonSwSh_ShinyHuntUnattended-IoATrade.h | 2 +- .../PokemonSwSh_ShinyHuntUnattended-Regi.cpp | 10 +- .../PokemonSwSh_ShinyHuntUnattended-Regi.h | 2 +- ...monSwSh_ShinyHuntUnattended-Regigigas2.cpp | 10 +- ...kemonSwSh_ShinyHuntUnattended-Regigigas2.h | 2 +- ...onSwSh_ShinyHuntUnattended-StrongSpawn.cpp | 4 - ...emonSwSh_ShinyHuntUnattended-StrongSpawn.h | 3 +- ...Sh_ShinyHuntUnattended-SwordsOfJustice.cpp | 10 +- ...SwSh_ShinyHuntUnattended-SwordsOfJustice.h | 2 +- 73 files changed, 778 insertions(+), 316 deletions(-) create mode 100644 SerialPrograms/Source/CommonFramework/CrashDump.cpp create mode 100644 SerialPrograms/Source/CommonFramework/CrashDump.h diff --git a/GeneratorConfig/AutoHost-Rolling.json b/GeneratorConfig/AutoHost-Rolling.json index fd9b4931af..8979613b4e 100644 --- a/GeneratorConfig/AutoHost-Rolling.json +++ b/GeneratorConfig/AutoHost-Rolling.json @@ -104,7 +104,7 @@ }, { "00-Type": "BooleanCheckBox", - "01-Label": "1st Move Dynamax:
Dynamax on first move. (only applies if above option is non-zero)", + "01-Label": "1st Move Dynamax: (requires 1st move select)
Dynamax on first move. (only applies if above option is non-zero)", "02-Declaration": "const bool DYNAMAX", "98-Default": true, "99-Current": true diff --git a/GeneratorConfig/EggCombined2.json b/GeneratorConfig/EggCombined2.json index aaac826e4b..84d45d2431 100644 --- a/GeneratorConfig/EggCombined2.json +++ b/GeneratorConfig/EggCombined2.json @@ -69,6 +69,15 @@ "98-Default": 6, "99-Current": 6 }, + { + "00-Type": "TimeExpression", + "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", + "03-MinValue": 0, + "04-MaxValue": 2147483647, + "98-Default": "4 * 3600 * TICKS_PER_SECOND", + "99-Current": "4 * 3600 * TICKS_PER_SECOND" + }, { "00-Type": "Divider", "01-Label": "Advanced Options: You should not need to touch anything below here." @@ -99,15 +108,6 @@ "04-MaxValue": 65535, "98-Default": "88 * TICKS_PER_SECOND", "99-Current": "88 * TICKS_PER_SECOND" - }, - { - "00-Type": "TimeExpression", - "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", - "03-MinValue": 0, - "04-MaxValue": 2147483647, - "98-Default": "4 * 3600 * TICKS_PER_SECOND", - "99-Current": "4 * 3600 * TICKS_PER_SECOND" } ] } diff --git a/GeneratorConfig/EggSuperCombined2.json b/GeneratorConfig/EggSuperCombined2.json index 57912f76dc..11841465ab 100644 --- a/GeneratorConfig/EggSuperCombined2.json +++ b/GeneratorConfig/EggSuperCombined2.json @@ -87,6 +87,15 @@ "98-Default": 6, "99-Current": 6 }, + { + "00-Type": "TimeExpression", + "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", + "03-MinValue": 0, + "04-MaxValue": 2147483647, + "98-Default": "4 * 3600 * TICKS_PER_SECOND", + "99-Current": "4 * 3600 * TICKS_PER_SECOND" + }, { "00-Type": "Divider", "01-Label": "Advanced Options: You should not need to touch anything below here." @@ -117,15 +126,6 @@ "04-MaxValue": 65535, "98-Default": "88 * TICKS_PER_SECOND", "99-Current": "88 * TICKS_PER_SECOND" - }, - { - "00-Type": "TimeExpression", - "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", - "03-MinValue": 0, - "04-MaxValue": 2147483647, - "98-Default": "4 * 3600 * TICKS_PER_SECOND", - "99-Current": "4 * 3600 * TICKS_PER_SECOND" } ] } diff --git a/GeneratorConfig/ShinyHunt-Regi.json b/GeneratorConfig/ShinyHunt-Regi.json index e48693060f..3f73c9b10b 100644 --- a/GeneratorConfig/ShinyHunt-Regi.json +++ b/GeneratorConfig/ShinyHunt-Regi.json @@ -11,10 +11,6 @@ "98-Default": "15 * TICKS_PER_SECOND", "99-Current": "15 * TICKS_PER_SECOND" }, - { - "00-Type": "Divider", - "01-Label": "Advanced Options: You should not need to touch anything below here." - }, { "00-Type": "TimeExpression", "01-Label": "Rollover Prevention:
Prevent a den from rolling over by
periodically touching the date.

If set to zero, this feature is disabled.", diff --git a/GeneratorConfig/ShinyHunt-SwordsOfJustice.json b/GeneratorConfig/ShinyHunt-SwordsOfJustice.json index ccad3341e7..43b5a14826 100644 --- a/GeneratorConfig/ShinyHunt-SwordsOfJustice.json +++ b/GeneratorConfig/ShinyHunt-SwordsOfJustice.json @@ -11,6 +11,15 @@ "98-Default": "19 * TICKS_PER_SECOND", "99-Current": "19 * TICKS_PER_SECOND" }, + { + "00-Type": "SimpleInteger", + "01-Label": "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + "02-Declaration": "const uint8_t TIME_ROLLBACK_HOURS", + "03-MinValue": 0, + "04-MaxValue": 11, + "98-Default": 1, + "99-Current": 1 + }, { "00-Type": "Divider", "01-Label": "Advanced Options: You should not need to touch anything below here." @@ -32,15 +41,6 @@ "04-MaxValue": 65535, "98-Default": "5 * TICKS_PER_SECOND", "99-Current": "5 * TICKS_PER_SECOND" - }, - { - "00-Type": "SimpleInteger", - "01-Label": "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", - "02-Declaration": "const uint8_t TIME_ROLLBACK_HOURS", - "03-MinValue": 0, - "04-MaxValue": 11, - "98-Default": 1, - "99-Current": 1 } ] } diff --git a/GeneratorConfig/ShinyHuntUnattended-IoATrade.json b/GeneratorConfig/ShinyHuntUnattended-IoATrade.json index 43732e0c4b..426c8d7b51 100644 --- a/GeneratorConfig/ShinyHuntUnattended-IoATrade.json +++ b/GeneratorConfig/ShinyHuntUnattended-IoATrade.json @@ -11,6 +11,15 @@ "98-Default": "1260", "99-Current": "1260" }, + { + "00-Type": "TimeExpression", + "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", + "03-MinValue": 0, + "04-MaxValue": 2147483647, + "98-Default": "4 * 3600 * TICKS_PER_SECOND", + "99-Current": "4 * 3600 * TICKS_PER_SECOND" + }, { "00-Type": "Divider", "01-Label": "Advanced Options: You should not need to touch anything below here." @@ -41,15 +50,6 @@ "04-MaxValue": 65535, "98-Default": "29 * TICKS_PER_SECOND", "99-Current": "29 * TICKS_PER_SECOND" - }, - { - "00-Type": "TimeExpression", - "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", - "03-MinValue": 0, - "04-MaxValue": 2147483647, - "98-Default": "4 * 3600 * TICKS_PER_SECOND", - "99-Current": "4 * 3600 * TICKS_PER_SECOND" } ] } diff --git a/GeneratorConfig/ShinyHuntUnattended-Regi.json b/GeneratorConfig/ShinyHuntUnattended-Regi.json index 69f690a5be..af2c6f696a 100644 --- a/GeneratorConfig/ShinyHuntUnattended-Regi.json +++ b/GeneratorConfig/ShinyHuntUnattended-Regi.json @@ -40,6 +40,15 @@ "98-Default": "Registeel", "99-Current": "Registeel" }, + { + "00-Type": "TimeExpression", + "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", + "03-MinValue": 0, + "04-MaxValue": 2147483647, + "98-Default": "4 * 3600 * TICKS_PER_SECOND", + "99-Current": "4 * 3600 * TICKS_PER_SECOND" + }, { "00-Type": "Divider", "01-Label": "Advanced Options: You should not need to touch anything below here." @@ -61,15 +70,6 @@ "04-MaxValue": 65535, "98-Default": "5 * TICKS_PER_SECOND", "99-Current": "5 * TICKS_PER_SECOND" - }, - { - "00-Type": "TimeExpression", - "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", - "03-MinValue": 0, - "04-MaxValue": 2147483647, - "98-Default": "4 * 3600 * TICKS_PER_SECOND", - "99-Current": "4 * 3600 * TICKS_PER_SECOND" } ] } diff --git a/GeneratorConfig/ShinyHuntUnattended-Regigigas.json b/GeneratorConfig/ShinyHuntUnattended-Regigigas.json index 5021d905d3..36b65d54f3 100644 --- a/GeneratorConfig/ShinyHuntUnattended-Regigigas.json +++ b/GeneratorConfig/ShinyHuntUnattended-Regigigas.json @@ -11,6 +11,15 @@ "98-Default": "3720", "99-Current": "3720" }, + { + "00-Type": "TimeExpression", + "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", + "03-MinValue": 0, + "04-MaxValue": 2147483647, + "98-Default": "4 * 3600 * TICKS_PER_SECOND", + "99-Current": "4 * 3600 * TICKS_PER_SECOND" + }, { "00-Type": "Divider", "01-Label": "Advanced Options: You should not need to touch anything below here." @@ -23,15 +32,6 @@ "04-MaxValue": 65535, "98-Default": "22 * TICKS_PER_SECOND", "99-Current": "22 * TICKS_PER_SECOND" - }, - { - "00-Type": "TimeExpression", - "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", - "03-MinValue": 0, - "04-MaxValue": 2147483647, - "98-Default": "4 * 3600 * TICKS_PER_SECOND", - "99-Current": "4 * 3600 * TICKS_PER_SECOND" } ] } diff --git a/GeneratorConfig/ShinyHuntUnattended-Regigigas2.json b/GeneratorConfig/ShinyHuntUnattended-Regigigas2.json index 7d3e190a05..e1f8442f59 100644 --- a/GeneratorConfig/ShinyHuntUnattended-Regigigas2.json +++ b/GeneratorConfig/ShinyHuntUnattended-Regigigas2.json @@ -20,6 +20,15 @@ "98-Default": "3720", "99-Current": "3720" }, + { + "00-Type": "TimeExpression", + "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", + "03-MinValue": 0, + "04-MaxValue": 2147483647, + "98-Default": "4 * 3600 * TICKS_PER_SECOND", + "99-Current": "4 * 3600 * TICKS_PER_SECOND" + }, { "00-Type": "Divider", "01-Label": "Advanced Options: You should not need to touch anything below here." @@ -41,15 +50,6 @@ "04-MaxValue": 65535, "98-Default": "8 * TICKS_PER_SECOND", "99-Current": "8 * TICKS_PER_SECOND" - }, - { - "00-Type": "TimeExpression", - "01-Label": "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "02-Declaration": "const uint32_t TOUCH_DATE_INTERVAL", - "03-MinValue": 0, - "04-MaxValue": 2147483647, - "98-Default": "4 * 3600 * TICKS_PER_SECOND", - "99-Current": "4 * 3600 * TICKS_PER_SECOND" } ] } diff --git a/GeneratorConfig/ShinyHuntUnattended-StrongSpawn.json b/GeneratorConfig/ShinyHuntUnattended-StrongSpawn.json index 0e2dbd427b..ed16d83dc6 100644 --- a/GeneratorConfig/ShinyHuntUnattended-StrongSpawn.json +++ b/GeneratorConfig/ShinyHuntUnattended-StrongSpawn.json @@ -20,10 +20,6 @@ "98-Default": "20 * TICKS_PER_SECOND", "99-Current": "20 * TICKS_PER_SECOND" }, - { - "00-Type": "Divider", - "01-Label": "Advanced Options: You should not need to touch anything below here." - }, { "00-Type": "SimpleInteger", "01-Label": "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", diff --git a/GeneratorConfig/ShinyHuntUnattended-SwordsOfJustice.json b/GeneratorConfig/ShinyHuntUnattended-SwordsOfJustice.json index 08d04564c6..60d8221294 100644 --- a/GeneratorConfig/ShinyHuntUnattended-SwordsOfJustice.json +++ b/GeneratorConfig/ShinyHuntUnattended-SwordsOfJustice.json @@ -18,6 +18,15 @@ "98-Default": false, "99-Current": false }, + { + "00-Type": "SimpleInteger", + "01-Label": "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + "02-Declaration": "const uint8_t TIME_ROLLBACK_HOURS", + "03-MinValue": 0, + "04-MaxValue": 11, + "98-Default": 1, + "99-Current": 1 + }, { "00-Type": "Divider", "01-Label": "Advanced Options: You should not need to touch anything below here." @@ -30,15 +39,6 @@ "04-MaxValue": 65535, "98-Default": "8 * TICKS_PER_SECOND", "99-Current": "8 * TICKS_PER_SECOND" - }, - { - "00-Type": "SimpleInteger", - "01-Label": "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", - "02-Declaration": "const uint8_t TIME_ROLLBACK_HOURS", - "03-MinValue": 0, - "04-MaxValue": 11, - "98-Default": 1, - "99-Current": 1 } ] } diff --git a/NativePrograms/EggCombined2.c b/NativePrograms/EggCombined2.c index 982df2946e..cecfff1527 100644 --- a/NativePrograms/EggCombined2.c +++ b/NativePrograms/EggCombined2.c @@ -36,6 +36,16 @@ const uint16_t STEPS_TO_HATCH = 5120; // Lookup the # of steps on Ser const float FETCHES_PER_BATCH = 6.0; +// Rollover Prevention +// +// Prevent a den from rolling over by periodically touching the date. +// +// If set to zero, this feature is disabled. +// Otherwise, the program will touch the date at roughly this interval to +// prevent a den from rolling over. This feature requres time-sync be disabled. +const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours + + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -55,13 +65,3 @@ const uint16_t EARLY_HATCH_SAFETY = 5 * TICKS_PER_SECOND; // Total animation time for hatching 5 eggs when there are no shinies. const uint16_t HATCH_DELAY = 88 * TICKS_PER_SECOND; - - -// Rollover Prevention -// -// Prevent a den from rolling over by periodically touching the date. -// -// If set to zero, this feature is disabled. -// Otherwise, the program will touch the date at roughly this interval to -// prevent a den from rolling over. This feature requres time-sync be disabled. -const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours diff --git a/NativePrograms/EggSuperCombined2.c b/NativePrograms/EggSuperCombined2.c index 40ac2bf9ef..2441ab0d99 100644 --- a/NativePrograms/EggSuperCombined2.c +++ b/NativePrograms/EggSuperCombined2.c @@ -44,6 +44,16 @@ const uint16_t STEPS_TO_HATCH = 5120; // Lookup the # of steps on Ser const float FETCHES_PER_BATCH = 6.0; +// Rollover Prevention +// +// Prevent a den from rolling over by periodically touching the date. +// +// If set to zero, this feature is disabled. +// Otherwise, the program will touch the date at roughly this interval to +// prevent a den from rolling over. This feature requres time-sync be disabled. +const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours + + //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// @@ -65,13 +75,3 @@ const uint16_t EARLY_HATCH_SAFETY = 5 * TICKS_PER_SECOND; const uint16_t HATCH_DELAY = 88 * TICKS_PER_SECOND; -// Rollover Prevention -// -// Prevent a den from rolling over by periodically touching the date. -// -// If set to zero, this feature is disabled. -// Otherwise, the program will touch the date at roughly this interval to -// prevent a den from rolling over. This feature requres time-sync be disabled. -const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours - - diff --git a/NativePrograms/ShinyHunt-SwordsOfJustice.c b/NativePrograms/ShinyHunt-SwordsOfJustice.c index 9ea53f68f3..1f29bc2c5e 100644 --- a/NativePrograms/ShinyHunt-SwordsOfJustice.c +++ b/NativePrograms/ShinyHunt-SwordsOfJustice.c @@ -13,11 +13,6 @@ const uint16_t EXIT_CAMP_TO_RUN_DELAY = 19 * TICKS_PER_SECOND; -// These two timings are less important. -const uint16_t ENTER_CAMP_DELAY = 8 * TICKS_PER_SECOND; -const uint16_t RUN_DELAY = 5 * TICKS_PER_SECOND; - - // Time Rollback // // Periodically roll back the time to keep the weather the same. @@ -26,3 +21,8 @@ const uint16_t RUN_DELAY = 5 * TICKS_PER_SECOND; // Otherwise, it roll the hour back by this amount every this many hours. // This feature requres time-sync be disabled. const uint8_t TIME_ROLLBACK_HOURS = 1; + + +// These two timings are less important. +const uint16_t ENTER_CAMP_DELAY = 8 * TICKS_PER_SECOND; +const uint16_t RUN_DELAY = 5 * TICKS_PER_SECOND; diff --git a/NativePrograms/ShinyHuntUnattended-IoATrade.c b/NativePrograms/ShinyHuntUnattended-IoATrade.c index aa251c220e..105dfddfb2 100644 --- a/NativePrograms/ShinyHuntUnattended-IoATrade.c +++ b/NativePrograms/ShinyHuntUnattended-IoATrade.c @@ -22,6 +22,16 @@ const uint16_t START_TO_RUN_DELAY = 1260; +// Rollover Prevention +// +// Prevent a den from rolling over by periodically touching the date. +// +// If set to zero, this feature is disabled. +// Otherwise, the program will touch the date at roughly this interval to +// prevent a den from rolling over. This feature requres time-sync be disabled. +const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours + + // @@ -37,13 +47,3 @@ const uint16_t MOVE_DURATION = 300; // Time to perform the trade. const uint16_t MASH_TO_TRADE_DELAY = 29 * TICKS_PER_SECOND; - - -// Rollover Prevention -// -// Prevent a den from rolling over by periodically touching the date. -// -// If set to zero, this feature is disabled. -// Otherwise, the program will touch the date at roughly this interval to -// prevent a den from rolling over. This feature requres time-sync be disabled. -const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours diff --git a/NativePrograms/ShinyHuntUnattended-Regi.c b/NativePrograms/ShinyHuntUnattended-Regi.c index 30b8b28356..0119270c6a 100644 --- a/NativePrograms/ShinyHuntUnattended-Regi.c +++ b/NativePrograms/ShinyHuntUnattended-Regi.c @@ -33,6 +33,16 @@ const uint16_t START_TO_RUN_DELAY = 1990; const RegiGolem REGI_NAME = Registeel; +// Rollover Prevention +// +// Prevent a den from rolling over by periodically touching the date. +// +// If set to zero, this feature is disabled. +// Otherwise, the program will touch the date at roughly this interval to +// prevent a den from rolling over. This feature requres time-sync be disabled. +const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours + + // The light puzzle is not 100% reliable. Sometimes it will miss lights. // To counter that, the program will periodically leave and re-enter the @@ -43,13 +53,3 @@ const uint16_t CORRECTION_INTERVAL = 20; // Delay to enter/exit the building. const uint16_t TRANSITION_DELAY = 5 * TICKS_PER_SECOND; - - -// Rollover Prevention -// -// Prevent a den from rolling over by periodically touching the date. -// -// If set to zero, this feature is disabled. -// Otherwise, the program will touch the date at roughly this interval to -// prevent a den from rolling over. This feature requres time-sync be disabled. -const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours diff --git a/NativePrograms/ShinyHuntUnattended-Regigigas.c b/NativePrograms/ShinyHuntUnattended-Regigigas.c index 2d79a346d0..fb12752a33 100644 --- a/NativePrograms/ShinyHuntUnattended-Regigigas.c +++ b/NativePrograms/ShinyHuntUnattended-Regigigas.c @@ -21,9 +21,6 @@ // const uint16_t START_TO_ATTACK_DELAY = 3720; -// Time to return to the overworld after selecting a move. -const uint16_t END_BATTLE_DELAY = 22 * TICKS_PER_SECOND; - // Rollover Prevention // @@ -33,3 +30,8 @@ const uint16_t END_BATTLE_DELAY = 22 * TICKS_PER_SECOND; // Otherwise, the program will touch the date at roughly this interval to // prevent a den from rolling over. This feature requres time-sync be disabled. const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours + + + +// Time to return to the overworld after selecting a move. +const uint16_t END_BATTLE_DELAY = 22 * TICKS_PER_SECOND; diff --git a/NativePrograms/ShinyHuntUnattended-Regigigas2.c b/NativePrograms/ShinyHuntUnattended-Regigigas2.c index 4c2403c84a..0d006f276b 100644 --- a/NativePrograms/ShinyHuntUnattended-Regigigas2.c +++ b/NativePrograms/ShinyHuntUnattended-Regigigas2.c @@ -26,13 +26,6 @@ const uint8_t REVERSAL_PP = 24; const uint16_t START_TO_ATTACK_DELAY = 3750; -// Time from when you attack to when you can decide to catch or not catch. -const uint16_t ATTACK_TO_CATCH_DELAY = 9 * TICKS_PER_SECOND; - -// Time from deciding not to catch to returning to the overworld. -const uint16_t CATCH_TO_OVERWORLD_DELAY = 8 * TICKS_PER_SECOND; - - // Rollover Prevention // // Prevent a den from rolling over by periodically touching the date. @@ -41,3 +34,10 @@ const uint16_t CATCH_TO_OVERWORLD_DELAY = 8 * TICKS_PER_SECOND; // Otherwise, the program will touch the date at roughly this interval to // prevent a den from rolling over. This feature requres time-sync be disabled. const uint32_t TOUCH_DATE_INTERVAL = (uint32_t)4 * 3600 * TICKS_PER_SECOND; // 4 hours + + +// Time from when you attack to when you can decide to catch or not catch. +const uint16_t ATTACK_TO_CATCH_DELAY = 9 * TICKS_PER_SECOND; + +// Time from deciding not to catch to returning to the overworld. +const uint16_t CATCH_TO_OVERWORLD_DELAY = 8 * TICKS_PER_SECOND; diff --git a/NativePrograms/ShinyHuntUnattended-StrongSpawn.c b/NativePrograms/ShinyHuntUnattended-StrongSpawn.c index 4e5ee5ecda..305f222c26 100644 --- a/NativePrograms/ShinyHuntUnattended-StrongSpawn.c +++ b/NativePrograms/ShinyHuntUnattended-StrongSpawn.c @@ -22,13 +22,6 @@ const uint16_t ENTER_GAME_TO_RUN_DELAY = 2280; - -// Wait this long for the game to start before pressing A to enter the game -// and start the encounter. You can try to decrease this to make resetting -// faster. -const uint16_t START_GAME_WAIT_DELAY = 20 * TICKS_PER_SECOND; - - // Time Rollback // // Periodically roll back the time to keep the weather the same. @@ -37,3 +30,10 @@ const uint16_t START_GAME_WAIT_DELAY = 20 * TICKS_PER_SECOND; // Otherwise, it roll the hour back by this amount every this many hours. // This feature requres time-sync be disabled. const uint8_t TIME_ROLLBACK_HOURS = 1; + + + +// Wait this long for the game to start before pressing A to enter the game +// and start the encounter. You can try to decrease this to make resetting +// faster. +const uint16_t START_GAME_WAIT_DELAY = 20 * TICKS_PER_SECOND; diff --git a/NativePrograms/ShinyHuntUnattended-SwordsOfJustice.c b/NativePrograms/ShinyHuntUnattended-SwordsOfJustice.c index a1b3d00e83..0c08ed2d1b 100644 --- a/NativePrograms/ShinyHuntUnattended-SwordsOfJustice.c +++ b/NativePrograms/ShinyHuntUnattended-SwordsOfJustice.c @@ -25,10 +25,6 @@ const uint16_t EXIT_CAMP_TO_RUN_DELAY = 1890; const bool AIRPLANE_MODE = false; -// These timings are less important. -const uint16_t ENTER_CAMP_DELAY = 8 * TICKS_PER_SECOND; - - // Time Rollback // // Periodically roll back the time to keep the weather the same. @@ -38,3 +34,7 @@ const uint16_t ENTER_CAMP_DELAY = 8 * TICKS_PER_SECOND; // This feature requres time-sync be disabled. const uint8_t TIME_ROLLBACK_HOURS = 1; + +// These timings are less important. +const uint16_t ENTER_CAMP_DELAY = 8 * TICKS_PER_SECOND; + diff --git a/SerialPrograms/SerialPrograms.pro b/SerialPrograms/SerialPrograms.pro index cf25af9fc6..3b6e9b8826 100644 --- a/SerialPrograms/SerialPrograms.pro +++ b/SerialPrograms/SerialPrograms.pro @@ -68,6 +68,7 @@ SOURCES += \ ../Common/SwitchFramework/FrameworkSettings.cpp \ ../Common/SwitchFramework/Switch_PushButtons.cpp \ ../Common/SwitchRoutines/SwitchDigitEntry.cpp \ + Source/CommonFramework/CrashDump.cpp \ Source/CommonFramework/Globals.cpp \ Source/CommonFramework/Inference/AnomalyDetector.cpp \ Source/CommonFramework/Inference/BlackScreenDetector.cpp \ @@ -221,6 +222,7 @@ HEADERS += \ ../Common/SwitchFramework/SwitchControllerDefs.h \ ../Common/SwitchFramework/Switch_PushButtons.h \ ../Common/SwitchRoutines/SwitchDigitEntry.h \ + Source/CommonFramework/CrashDump.h \ Source/CommonFramework/Globals.h \ Source/CommonFramework/Inference/AnomalyDetector.h \ Source/CommonFramework/Inference/BlackScreenDetector.h \ diff --git a/SerialPrograms/Source/CommonFramework/CrashDump.cpp b/SerialPrograms/Source/CommonFramework/CrashDump.cpp new file mode 100644 index 0000000000..dc697e5e1f --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/CrashDump.cpp @@ -0,0 +1,113 @@ +/* Crash Dump + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include +#include "Common/Clientside/Unicode.h" +//#include "ClientSource/Libraries/Logging.h" +#include "CrashDump.h" + +#include +using std::cout; +using std::endl; + +namespace PokemonAutomation{ + +std::string now_to_filestring(){ +#if _WIN32 && _MSC_VER +#pragma warning(disable:4996) +#endif + + time_t t = time(0); + struct tm* now = localtime(&t); + + std::string str; + str += std::to_string(now->tm_year + 1900); + str += std::string(now->tm_mon + 1 < 10 ? "0" : "") + std::to_string(now->tm_mon + 1); + str += std::string(now->tm_mday < 10 ? "0" : "") + std::to_string(now->tm_mday); + str += "-"; + str += std::string(now->tm_hour < 10 ? "0" : "") + std::to_string(now->tm_hour); + str += std::string(now->tm_min < 10 ? "0" : "") + std::to_string(now->tm_min); + str += std::string(now->tm_sec < 10 ? "0" : "") + std::to_string(now->tm_sec); + return str; +} + +} + + +#if _WIN32 +#pragma comment (lib, "Dbghelp.lib") +#include +#include +#include +namespace PokemonAutomation{ + + +long WINAPI crash_handler(EXCEPTION_POINTERS* e){ + static bool handled = false; + if (handled){ + return EXCEPTION_CONTINUE_SEARCH; + } + handled = true; + + cout << "Oops... Program has crashed." << endl; + cout << "Creating mini-dump file..." << endl; + + std::string filename = "SerialPrograms-"; + filename += now_to_filestring(); + filename += ".dmp"; + + HANDLE handle = CreateFileW( + utf8_to_wstr(filename).c_str(), + FILE_WRITE_ACCESS, + FILE_SHARE_READ, + nullptr, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0 + ); + if (handle == INVALID_HANDLE_VALUE){ + cout << "Unable to create dump file: " << GetLastError() << endl; + return EXCEPTION_EXECUTE_HANDLER; + } + + MINIDUMP_EXCEPTION_INFORMATION exceptionInfo; + exceptionInfo.ThreadId = GetCurrentThreadId(); + exceptionInfo.ExceptionPointers = e; + exceptionInfo.ClientPointers = FALSE; + + int ret = MiniDumpWriteDump( + GetCurrentProcess(), + GetCurrentProcessId(), + handle, + MiniDumpNormal, + e != nullptr ? &exceptionInfo : nullptr, + nullptr, + nullptr + ); + CloseHandle(handle); + + if (!ret){ + cout << "Unable to create minidump: " << GetLastError() << endl; + }else{ + cout << "Minidump created!" << endl; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + + + +void setup_crash_handler(){ + SetUnhandledExceptionFilter(crash_handler); +} + + +} +#else +void setup_crash_handler(){ + // Not supported +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/CrashDump.h b/SerialPrograms/Source/CommonFramework/CrashDump.h new file mode 100644 index 0000000000..1918717016 --- /dev/null +++ b/SerialPrograms/Source/CommonFramework/CrashDump.h @@ -0,0 +1,17 @@ +/* Crash Dump + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_CrashDump_H +#define PokemonAutomation_CrashDump_H + +namespace PokemonAutomation{ + + +void setup_crash_handler(); + + +} +#endif diff --git a/SerialPrograms/Source/CommonFramework/Globals.cpp b/SerialPrograms/Source/CommonFramework/Globals.cpp index a2ee68cc72..8a199288d5 100644 --- a/SerialPrograms/Source/CommonFramework/Globals.cpp +++ b/SerialPrograms/Source/CommonFramework/Globals.cpp @@ -9,7 +9,7 @@ namespace PokemonAutomation{ -const QString VERSION = "v0.5.9"; +const QString VERSION = "v0.5.16"; const QString DISCORD_LINK = "discord.gg/cQ4gWxN"; const QString DISCORD_LINK_FULL = "https://" + DISCORD_LINK; const QString GITHUB_REPO = "https://github.com/PokemonAutomation/SwSh-Arduino"; diff --git a/SerialPrograms/Source/CommonFramework/Inference/InferenceThrottler.h b/SerialPrograms/Source/CommonFramework/Inference/InferenceThrottler.h index 494184698e..779000e321 100644 --- a/SerialPrograms/Source/CommonFramework/Inference/InferenceThrottler.h +++ b/SerialPrograms/Source/CommonFramework/Inference/InferenceThrottler.h @@ -30,7 +30,7 @@ class InferenceThrottler{ // Returns true if this loop has timed out. bool end_iteration(ProgramEnvironment& env){ auto now = std::chrono::system_clock::now(); - if (now - m_start >= m_timeout){ + if (m_timeout != std::chrono::milliseconds(0) && now - m_start >= m_timeout){ return true; } diff --git a/SerialPrograms/Source/CommonFramework/Main.cpp b/SerialPrograms/Source/CommonFramework/Main.cpp index 7bf48212b3..936be81fc4 100644 --- a/SerialPrograms/Source/CommonFramework/Main.cpp +++ b/SerialPrograms/Source/CommonFramework/Main.cpp @@ -3,6 +3,7 @@ #include "Common/Qt/StringException.h" #include "Tesseract/capi.h" #include "PersistentSettings.h" +#include "CrashDump.h" #include "Windows/MainWindow.h" #include @@ -15,6 +16,8 @@ using namespace PokemonAutomation; int main(int argc, char *argv[]) { + setup_crash_handler(); + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication application(argc, argv); @@ -25,6 +28,8 @@ int main(int argc, char *argv[]) }catch (const StringException& error){ cout << error.message().toUtf8().data() << endl; } +// int* ptr = nullptr; +// cout << *ptr << endl; MainWindow w; w.show(); diff --git a/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp b/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp index bc1f26457a..27cdd48a30 100644 --- a/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp +++ b/SerialPrograms/Source/CommonFramework/PersistentSettings.cpp @@ -63,6 +63,8 @@ void PersistentSettings::read(){ // json_get_string(config_path, root, "00-ConfigPath"); // json_get_string(source_path, root, "01-SourcePath"); json_get_string(stats_file, root, "01-StatsFile"); + stat_sets.open_from_file(stats_file); + { QJsonArray res = json_get_array_nothrow(root, "02-WindowSize"); if (res.size() == 2){ diff --git a/SerialPrograms/Source/CommonFramework/PersistentSettings.h b/SerialPrograms/Source/CommonFramework/PersistentSettings.h index 2c9107d024..e054eaae26 100644 --- a/SerialPrograms/Source/CommonFramework/PersistentSettings.h +++ b/SerialPrograms/Source/CommonFramework/PersistentSettings.h @@ -4,13 +4,15 @@ * */ -#include -#include -#include #ifndef PokemonAutomation_PersistentSettings_H #define PokemonAutomation_PersistentSettings_H +#include +#include +#include +#include "Tools/StatsDatabase.h" + namespace PokemonAutomation{ @@ -32,6 +34,8 @@ class PersistentSettings{ bool developer_mode = false; std::atomic log_everything; + StatSet stat_sets; + QJsonObject settings; QJsonObject programs; }; diff --git a/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.cpp b/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.cpp index c805d405b2..b0493fcb39 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/StatsDatabase.cpp @@ -9,6 +9,10 @@ #include "ClientSource/Libraries/Logging.h" #include "StatsDatabase.h" +#include +using std::cout; +using std::endl; + namespace PokemonAutomation{ @@ -70,6 +74,7 @@ std::string StatList::to_str() const{ void StatList::aggregate(StatsTracker& tracker) const{ for (const StatLine& line : m_list){ tracker.parse_and_append_line(line.stats()); +// cout << tracker.to_str() << endl; } } @@ -150,7 +155,7 @@ bool StatSet::get_line(std::string& line, const char*& ptr){ for (;; ptr++){ char ch = *ptr; if (ch == '\0'){ -// cout << line << endl; +// cout << line << endl; ptr++; return false; } @@ -166,6 +171,8 @@ bool StatSet::get_line(std::string& line, const char*& ptr){ } } void StatSet::load_from_string(const char* ptr){ + m_data.clear(); + // Find first section. while (true){ std::string line; diff --git a/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.cpp b/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.cpp index 855ef3aa24..9b151f9625 100644 --- a/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.cpp +++ b/SerialPrograms/Source/CommonFramework/Tools/StatsTracking.cpp @@ -75,14 +75,17 @@ void StatsTracker::parse_and_append_line(const std::string& line){ uint64_t count = 0; while (true){ char ch = *ptr++; - if (ch < 32) break; + if (ch < 32){ + m_stats[label] += count; + return; + } if (ch == ',') continue; if (!('0' <= ch && ch <= '9')) break; count *= 10; count += ch - '0'; } - cout << label << " = " << count << endl; +// cout << label << " = " << count << endl; m_stats[label] += count; // Skip to next; diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp index 58604c26f4..80d3de6976 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp +++ b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.cpp @@ -185,6 +185,7 @@ void RunnableProgramUI::make_body(QWidget& parent, QVBoxLayout& layout){ int font_size = font.pointSize(); font.setPointSize(font_size + font_size / 2); m_status_bar->setFont(font); + update_historical_stats(); QGroupBox* actions_widget = new QGroupBox("Actions", &parent); layout.addWidget(actions_widget); @@ -320,6 +321,19 @@ void RunnableProgramUI::on_stop(){ void RunnableProgramUI::reset_connections(){ if (m_setup) m_setup->reset_serial(); } +void RunnableProgramUI::update_historical_stats(){ + RunnableProgram& factory = static_cast(m_factory); + m_stats = factory.make_stats(); + if (m_stats){ + settings.stat_sets.open_from_file(settings.stats_file); + StatList& list = settings.stat_sets[m_name.toUtf8().data()]; + if (list.size() != 0){ + list.aggregate(*m_stats); + } + m_status_bar->setText(m_stats->to_str().c_str()); + m_status_bar->setVisible(true); + } +} void RunnableProgramUI::set_status(QString status){ if (status.size() <= 0){ m_status_bar->setVisible(false); @@ -346,25 +360,15 @@ void RunnableProgramUI::run_program(){ RunnableProgram& factory = static_cast(m_factory); std::unique_ptr current_stats = factory.make_stats(); - std::unique_ptr historial_stats; std::string program_name = m_name.toUtf8().data(); - // Aggregate historical stats. - if (current_stats){ - StatSet stat_sets; - stat_sets.open_from_file(settings.stats_file); - StatList& list = stat_sets[program_name]; - if (list.size() != 0){ - historial_stats = factory.make_stats(); - list.aggregate(*historial_stats); - } - } - + // Update historical stats. + update_historical_stats(); try{ m_logger.log("Starting Program: " + m_name + ""); - program(current_stats.get(), historial_stats.get()); + program(current_stats.get(), m_stats.get()); m_setup->wait_for_all_requests(); m_logger.log("Ending Program..."); }catch (PokemonAutomation::CancelledException&){ @@ -388,6 +392,7 @@ void RunnableProgramUI::run_program(){ ); // show_stats_warning(); } + settings.stat_sets.open_from_file(settings.stats_file); } diff --git a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h index 026f65ff10..86309c3485 100644 --- a/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h +++ b/SerialPrograms/Source/NintendoSwitch/Framework/RunnableSwitchProgram.h @@ -75,6 +75,7 @@ class RunnableProgramUI : public RightPanelUI{ private: void on_stop(); void reset_connections(); + void update_historical_stats(); virtual void program( StatsTracker* current_stats, const StatsTracker* historical_stats @@ -105,6 +106,8 @@ public slots: QPushButton* m_start_button; QPushButton* m_default_button; + std::unique_ptr m_stats; + // ProgramEnvironment m_environment; std::atomic m_state; std::thread m_thread; diff --git a/SerialPrograms/Source/PanelList.cpp b/SerialPrograms/Source/PanelList.cpp index 951f7b0f52..15715a86df 100644 --- a/SerialPrograms/Source/PanelList.cpp +++ b/SerialPrograms/Source/PanelList.cpp @@ -168,9 +168,9 @@ const std::vector>& PROGRAM_LIST(){ list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); -// if (settings.developer_mode){ + if (settings.developer_mode){ list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); -// } + } list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); list.emplace_back(new SingleSwitchProgramWrapper(settings.programs)); diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp index 66299066df..c3387b2135 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.cpp @@ -96,6 +96,44 @@ bool StartBattleDetector::wait(ProgramEnvironment& env){ } + +AsyncStartBattleDetector::AsyncStartBattleDetector(ProgramEnvironment& env, VideoFeed& feed) + : StartBattleDetector(feed, std::chrono::milliseconds(0)) + , m_stopping(false) + , m_detected(false) + , m_thread(&AsyncStartBattleDetector::thread_loop, this, std::ref(env)) +{} +AsyncStartBattleDetector::~AsyncStartBattleDetector(){ + m_stopping.store(true, std::memory_order_release); + m_thread.join(); +} + +bool AsyncStartBattleDetector::detected() const{ + return m_detected.load(std::memory_order_acquire); +} + +void AsyncStartBattleDetector::thread_loop(ProgramEnvironment& env){ + InferenceThrottler throttler(m_timeout, std::chrono::milliseconds(50)); + while (!m_stopping.load(std::memory_order_acquire) && !detected()){ + env.check_stopping(); + + QImage screen = m_feed.snapshot(); + if (detect(screen)){ + m_detected.store(true, std::memory_order_release); + } + + if (throttler.end_iteration(env)){ + env.log("StartBattleDetector: Timed out.", "red"); + return; + } + } +} + + + + + + } } } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h index 682bd46052..e12e9f3ecb 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h +++ b/SerialPrograms/Source/PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h @@ -32,7 +32,7 @@ class StartBattleDetector{ bool detect(const QImage& screen); bool wait(ProgramEnvironment& env); -private: +protected: VideoFeed& m_feed; std::chrono::milliseconds m_timeout; InferenceBoxScope m_screen_box; @@ -41,6 +41,27 @@ class StartBattleDetector{ }; +class AsyncStartBattleDetector : public StartBattleDetector{ +public: + AsyncStartBattleDetector(ProgramEnvironment& env, VideoFeed& feed); + ~AsyncStartBattleDetector(); + + bool detected() const; + + void start(); + + +private: + void thread_loop(ProgramEnvironment& env); + +private: + std::atomic m_stopping; + std::atomic m_detected; + std::thread m_thread; +}; + + + } } } diff --git a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp index 44105e0602..45171d1096 100644 --- a/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_SquareDetector.cpp @@ -334,10 +334,10 @@ std::multimap> get_edge_points( continue; } if ( - (c > 0 && submatrix[r][c - 1] == background) || - (c + 1 < width && submatrix[r][c + 1] == background) || - (r > 0 && submatrix[r - 1][c] == background) || - (r + 1 < width && submatrix[r + 1][c] == background) + (c > 0 && submatrix[r][c - 1] == background) || + (c + 1 < width && submatrix[r][c + 1] == background) || + (r > 0 && submatrix[r - 1][c] == background) || + (r + 1 < height && submatrix[r + 1][c] == background) ){ double angle = std::atan2( r - center_y, diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombined2.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombined2.cpp index 6eb0087423..f8785bef3c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombined2.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombined2.cpp @@ -28,6 +28,10 @@ EggCombined2::EggCombined2() "Fetches per Batch:
For each batch of eggs, attempt this many egg fetches.", 6.0, 0, 7 ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -43,19 +47,15 @@ EggCombined2::EggCombined2() "Hatch Delay:
Total animation time for hatching 5 eggs when there are no shinies.", "88 * TICKS_PER_SECOND" ) - , TOUCH_DATE_INTERVAL( - "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "4 * 3600 * TICKS_PER_SECOND" - ) { m_options.emplace_back(&BOXES_TO_HATCH, "BOXES_TO_HATCH"); m_options.emplace_back(&STEPS_TO_HATCH, "STEPS_TO_HATCH"); m_options.emplace_back(&FETCHES_PER_BATCH, "FETCHES_PER_BATCH"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&SAFETY_TIME, "SAFETY_TIME"); m_options.emplace_back(&EARLY_HATCH_SAFETY, "EARLY_HATCH_SAFETY"); m_options.emplace_back(&HATCH_DELAY, "HATCH_DELAY"); - m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); } void EggCombined2::program(SingleSwitchProgramEnvironment& env) const{ diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombined2.h b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombined2.h index df62b26279..8da0e8584c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombined2.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggCombined2.h @@ -29,11 +29,11 @@ class EggCombined2 : public SingleSwitchProgram{ SimpleInteger BOXES_TO_HATCH; EggStepCount STEPS_TO_HATCH; FloatingPoint FETCHES_PER_BATCH; + TimeExpression TOUCH_DATE_INTERVAL; SectionDivider m_advanced_options; TimeExpression SAFETY_TIME; TimeExpression EARLY_HATCH_SAFETY; TimeExpression HATCH_DELAY; - TimeExpression TOUCH_DATE_INTERVAL; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.cpp index c83f866ec0..8d75f86a02 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.cpp @@ -37,6 +37,10 @@ EggSuperCombined2::EggSuperCombined2() "Fetches per Batch:
For each batch of eggs, attempt this many egg fetches.", 6.0, 0, 7 ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -52,21 +56,17 @@ EggSuperCombined2::EggSuperCombined2() "Hatch Delay:
Total animation time for hatching 5 eggs when there are no shinies.", "88 * TICKS_PER_SECOND" ) - , TOUCH_DATE_INTERVAL( - "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "4 * 3600 * TICKS_PER_SECOND" - ) { m_options.emplace_back(&BOXES_TO_RELEASE, "BOXES_TO_RELEASE"); m_options.emplace_back(&BOXES_TO_SKIP, "BOXES_TO_SKIP"); m_options.emplace_back(&BOXES_TO_HATCH, "BOXES_TO_HATCH"); m_options.emplace_back(&STEPS_TO_HATCH, "STEPS_TO_HATCH"); m_options.emplace_back(&FETCHES_PER_BATCH, "FETCHES_PER_BATCH"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&SAFETY_TIME, "SAFETY_TIME"); m_options.emplace_back(&EARLY_HATCH_SAFETY, "EARLY_HATCH_SAFETY"); m_options.emplace_back(&HATCH_DELAY, "HATCH_DELAY"); - m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); } void EggSuperCombined2::program(SingleSwitchProgramEnvironment& env) const{ diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.h b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.h index fe743dc741..11ebd43e7a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.h @@ -31,11 +31,11 @@ class EggSuperCombined2 : public SingleSwitchProgram{ SimpleInteger BOXES_TO_HATCH; EggStepCount STEPS_TO_HATCH; FloatingPoint FETCHES_PER_BATCH; + TimeExpression TOUCH_DATE_INTERVAL; SectionDivider m_advanced_options; TimeExpression SAFETY_TIME; TimeExpression EARLY_HATCH_SAFETY; TimeExpression HATCH_DELAY; - TimeExpression TOUCH_DATE_INTERVAL; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp index 23f07b6ce8..e1805697b6 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.cpp @@ -50,6 +50,10 @@ AutoHostRolling::AutoHostRolling() "1st Move Dynamax:
Dynamax on first move. (only applies if above option is non-zero)", true ) + , TROLL_HOSTING( + "Troll Hosting: (requires 1st move select)
0 disables the troll hosting option, 1 attacks the first ally, 2 attacks the second one, 3 attacks the third one. Dynamaxing will disable this option.", + 0, 0, 3 + ) , ALTERNATE_GAMES( "Alternate Games:
Alternate hosting between 1st and 2nd games. Host from both Sword and Shield.", false @@ -92,6 +96,7 @@ AutoHostRolling::AutoHostRolling() m_options.emplace_back(&EXTRA_DELAY_BETWEEN_RAIDS, "EXTRA_DELAY_BETWEEN_RAIDS"); m_options.emplace_back(&MOVE_SLOT, "MOVE_SLOT"); m_options.emplace_back(&DYNAMAX, "DYNAMAX"); + m_options.emplace_back(&TROLL_HOSTING, "TROLL_HOSTING"); m_options.emplace_back(&ALTERNATE_GAMES, "ALTERNATE_GAMES"); m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); m_options.emplace_back(&m_internet_settings, ""); @@ -199,6 +204,15 @@ void AutoHostRolling::program(SingleSwitchProgramEnvironment& env) const{ pbf_press_dpad(DPAD_DOWN, 20, 30); } pbf_press_button(BUTTON_A, 20, 80); + + // Disable the troll hosting option if the dynamax is set to TRUE. + if (!DYNAMAX && TROLL_HOSTING > 0){ + pbf_press_dpad(DPAD_DOWN, 20, 80); + for (uint8_t c = 0; c < TROLL_HOSTING; c++){ + pbf_press_dpad(DPAD_RIGHT, 20, 80); + } + } + pbf_press_button(BUTTON_A, 20, 980); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.h b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.h index 5dd0a9e366..cfb530b846 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/Hosting/PokemonSwSh_AutoHost-Rolling.h @@ -37,6 +37,7 @@ class AutoHostRolling : public SingleSwitchProgram{ SimpleInteger MOVE_SLOT; BooleanCheckBox DYNAMAX; + SimpleInteger TROLL_HOSTING; BooleanCheckBox ALTERNATE_GAMES; TimeExpression TOUCH_DATE_INTERVAL; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.cpp index 21a2f78e6d..193a85c066 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/PokemonSwSh_OverworldTrajectory.cpp @@ -42,8 +42,8 @@ Trajectory get_trajectory_float(double delta_x, double delta_y){ delta_x *= 10; delta_y *= 10; - int int_x = std::floor(delta_x); - int int_y = std::floor(delta_y); + int int_x = (int)std::floor(delta_x); + int int_y = (int)std::floor(delta_y); double frac_x = delta_x - int_x; double frac_y = delta_y - int_y; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp index 97e9802893..0309e05305 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.cpp @@ -16,12 +16,16 @@ StandardEncounterTracker::StandardEncounterTracker( ShinyHuntTracker& stats, ConsoleHandle& console, bool require_square, - uint16_t exit_battle_time + uint16_t exit_battle_time, + bool take_video, + bool run_from_everything ) : m_stats(stats) , m_console(console) , m_require_square(require_square) , m_exit_battle_time(exit_battle_time) + , m_take_video(take_video) + , m_run_from_everything(run_from_everything) {} bool StandardEncounterTracker::run_away(){ @@ -31,35 +35,52 @@ bool StandardEncounterTracker::run_away(){ return true; } +void StandardEncounterTracker::take_video(){ + if (m_take_video){ + pbf_wait(m_console, 5 * TICKS_PER_SECOND); + pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); + } +} + bool StandardEncounterTracker::process_result(ShinyDetection detection){ + bool stop = false; switch (detection){ case ShinyDetection::NO_BATTLE_MENU: return false; + case ShinyDetection::NOT_SHINY: m_stats.add_non_shiny(); - run_away(); - return false; + break; + case ShinyDetection::STAR_SHINY: m_stats.add_star_shiny(); - pbf_wait(m_console, 5 * TICKS_PER_SECOND); - pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); - if (m_require_square){ - run_away(); - return false; - } - return true; + take_video(); + stop = !m_require_square; + break; + case ShinyDetection::SQUARE_SHINY: m_stats.add_square_shiny(); - pbf_wait(m_console, 5 * TICKS_PER_SECOND); - pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); - return true; + take_video(); + stop = true; + break; + case ShinyDetection::UNKNOWN_SHINY: m_stats.add_unknown_shiny(); - pbf_wait(m_console, 5 * TICKS_PER_SECOND); - pbf_press_button(m_console, BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); - return true; + take_video(); + stop = true; + break; + } - return false; + + if (m_run_from_everything){ + stop = false; + } + + if (!stop){ + run_away(); + } + + return stop; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h index 5e9327e682..f518c07e3d 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_EncounterTracker.h @@ -22,18 +22,25 @@ class StandardEncounterTracker{ ShinyHuntTracker& stats, ConsoleHandle& console, bool require_square, - uint16_t exit_battle_time + uint16_t exit_battle_time, + bool take_video, + bool run_from_everything ); virtual bool run_away(); bool process_result(ShinyDetection detection); +private: + void take_video(); + protected: ShinyHuntTracker& m_stats; ConsoleHandle& m_console; bool m_require_square; uint16_t m_exit_battle_time; + bool m_take_video; + bool m_run_from_everything; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp index 069784d295..a1662e934a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/PersistentSettings.h" #include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" #include "PokemonSwSh_EncounterTracker.h" @@ -29,7 +30,7 @@ ShinyHuntAutonomousBerryTree::ShinyHuntAutonomousBerryTree() ) , GO_HOME_WHEN_DONE( "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", - true + false ) , REQUIRE_SQUARE( "Require Square:
Stop only for a square shiny. Run from star shinies.", @@ -42,11 +43,23 @@ ShinyHuntAutonomousBerryTree::ShinyHuntAutonomousBerryTree() "Exit Battle Time:
After running, wait this long to return to overworld.", "6 * TICKS_PER_SECOND" ) + , VIDEO_ON_SHINY( + "Video Capture:
Take a video of the encounter if it is shiny.", + true + ) + , RUN_FROM_EVERYTHING( + "Run from Everything:
Run from everything - even if it is shiny. (For testing only.)", + false + ) { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&REQUIRE_SQUARE, "REQUIRE_SQUARE"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); + if (settings.developer_mode){ + m_options.emplace_back(&VIDEO_ON_SHINY, "VIDEO_ON_SHINY"); + m_options.emplace_back(&RUN_FROM_EVERYTHING, "RUN_FROM_EVERYTHING"); + } } @@ -71,7 +84,13 @@ void ShinyHuntAutonomousBerryTree::program(SingleSwitchProgramEnvironment& env) grip_menu_connect_go_home(); Stats& stats = env.stats(); - StandardEncounterTracker tracker(stats, env.console, REQUIRE_SQUARE, EXIT_BATTLE_MASH_TIME); + StandardEncounterTracker tracker( + stats, env.console, + REQUIRE_SQUARE, + EXIT_BATTLE_MASH_TIME, + VIDEO_ON_SHINY, + RUN_FROM_EVERYTHING + ); uint8_t year = MAX_YEAR; while (true){ diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h index 160db21019..068c593af0 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-BerryTree.h @@ -30,6 +30,8 @@ class ShinyHuntAutonomousBerryTree : public SingleSwitchProgram{ BooleanCheckBox REQUIRE_SQUARE; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; + BooleanCheckBox VIDEO_ON_SHINY; + BooleanCheckBox RUN_FROM_EVERYTHING; }; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp index 728e2e2855..bfc9b079ea 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/PersistentSettings.h" #include "PokemonSwSh/ShinyHuntTracker.h" #include "PokemonSwSh/Inference/PokemonSwSh_FishingDetector.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" @@ -30,7 +31,11 @@ ShinyHuntAutonomousFishing::ShinyHuntAutonomousFishing() ) , GO_HOME_WHEN_DONE( "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", - true + false + ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." @@ -43,16 +48,24 @@ ShinyHuntAutonomousFishing::ShinyHuntAutonomousFishing() "Fish Respawn Time:
Wait this long for fish to respawn.", "4 * TICKS_PER_SECOND" ) - , TIME_ROLLBACK_HOURS( - "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", - 1, 0, 11 + , VIDEO_ON_SHINY( + "Video Capture:
Take a video of the encounter if it is shiny.", + true + ) + , RUN_FROM_EVERYTHING( + "Run from Everything:
Run from everything - even if it is shiny. (For testing only.)", + false ) { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); m_options.emplace_back(&FISH_RESPAWN_TIME, "FISH_RESPAWN_TIME"); - m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); + if (settings.developer_mode){ + m_options.emplace_back(&VIDEO_ON_SHINY, "VIDEO_ON_SHINY"); + m_options.emplace_back(&RUN_FROM_EVERYTHING, "RUN_FROM_EVERYTHING"); + } } @@ -61,16 +74,13 @@ struct ShinyHuntAutonomousFishing::Stats : public ShinyHuntTracker{ Stats() : ShinyHuntTracker(true) , m_misses(m_stats["Misses"]) - , m_timeouts(m_stats["Timeouts"]) - , m_unexpected_battles(m_stats["Unexpected Battles"]) + , m_errors(m_stats["Errors"]) { m_display_order.insert(m_display_order.begin() + 1, Stat("Misses")); - m_display_order.insert(m_display_order.begin() + 2, Stat("Timeouts")); - m_display_order.insert(m_display_order.begin() + 3, Stat("Unexpected Battles")); + m_display_order.insert(m_display_order.begin() + 2, Stat("Errors")); } uint64_t& m_misses; - uint64_t& m_timeouts; - uint64_t& m_unexpected_battles; + uint64_t& m_errors; }; std::unique_ptr ShinyHuntAutonomousFishing::make_stats() const{ return std::unique_ptr(new Stats()); @@ -86,7 +96,13 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co uint32_t last_touch = system_clock(); Stats& stats = env.stats(); - StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); + StandardEncounterTracker tracker( + stats, env.console, + false, + EXIT_BATTLE_MASH_TIME, + VIDEO_ON_SHINY, + RUN_FROM_EVERYTHING + ); while (true){ env.update_stats(); @@ -111,7 +127,7 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co FishingDetector::Detection detection = detector.wait_for_detection(env); switch (detection){ case FishingDetector::NO_DETECTION: - stats.m_timeouts++; + stats.m_errors++; pbf_mash_button(BUTTON_B, 2 * TICKS_PER_SECOND); continue; case FishingDetector::HOOKED: @@ -122,7 +138,7 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co pbf_mash_button(BUTTON_B, 2 * TICKS_PER_SECOND); continue; case FishingDetector::BATTLE_MENU: - stats.m_unexpected_battles++; + stats.m_errors++; tracker.run_away(); continue; } @@ -146,7 +162,7 @@ void ShinyHuntAutonomousFishing::program(SingleSwitchProgramEnvironment& env) co break; } if (detection == ShinyDetection::NO_BATTLE_MENU){ - stats.m_timeouts++; + stats.m_errors++; pbf_mash_button(BUTTON_B, TICKS_PER_SECOND); tracker.run_away(); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h index a364aae517..c36cab6546 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Fishing.h @@ -29,10 +29,12 @@ class ShinyHuntAutonomousFishing : public SingleSwitchProgram{ struct Stats; BooleanCheckBox GO_HOME_WHEN_DONE; + SimpleInteger TIME_ROLLBACK_HOURS; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; TimeExpression FISH_RESPAWN_TIME; - SimpleInteger TIME_ROLLBACK_HOURS; + BooleanCheckBox VIDEO_ON_SHINY; + BooleanCheckBox RUN_FROM_EVERYTHING; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp index 20e75a767c..3b143b4077 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/PersistentSettings.h" #include "PokemonSwSh/ShinyHuntTracker.h" #include "PokemonSwSh/Inference/PokemonSwSh_SummaryShinySymbolDetector.h" #include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" @@ -28,7 +29,11 @@ ShinyHuntAutonomousIoATrade::ShinyHuntAutonomousIoATrade() ) , GO_HOME_WHEN_DONE( "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", - true + false + ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." @@ -37,15 +42,23 @@ ShinyHuntAutonomousIoATrade::ShinyHuntAutonomousIoATrade() "Mash to Trade Delay:
Time to perform the trade.", "29 * TICKS_PER_SECOND" ) - , TOUCH_DATE_INTERVAL( - "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "4 * 3600 * TICKS_PER_SECOND" + , VIDEO_ON_SHINY( + "Video Capture:
Take a video of the encounter if it is shiny.", + true + ) + , RUN_FROM_EVERYTHING( + "Run from Everything:
Run from everything - even if it is shiny. (For testing only.)", + false ) { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&MASH_TO_TRADE_DELAY, "MASH_TO_TRADE_DELAY"); - m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); + if (settings.developer_mode){ + m_options.emplace_back(&VIDEO_ON_SHINY, "VIDEO_ON_SHINY"); + m_options.emplace_back(&RUN_FROM_EVERYTHING, "RUN_FROM_EVERYTHING"); + } } @@ -110,9 +123,13 @@ void ShinyHuntAutonomousIoATrade::program(SingleSwitchProgramEnvironment& env) c break; case SummaryShinySymbolDetector::SHINY: stats.add_unknown_shiny(); - pbf_wait(1 * TICKS_PER_SECOND); - pbf_press_button(BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); - goto StopProgram; + if (VIDEO_ON_SHINY){ + pbf_wait(1 * TICKS_PER_SECOND); + pbf_press_button(BUTTON_CAPTURE, 2 * TICKS_PER_SECOND, 5 * TICKS_PER_SECOND); + } + if (!RUN_FROM_EVERYTHING){ + goto StopProgram; + } } pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h index 91f719484f..f33dc7a76b 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-IoATrade.h @@ -27,9 +27,11 @@ class ShinyHuntAutonomousIoATrade : public SingleSwitchProgram{ struct Stats; BooleanCheckBox GO_HOME_WHEN_DONE; + TimeExpression TOUCH_DATE_INTERVAL; SectionDivider m_advanced_options; TimeExpression MASH_TO_TRADE_DELAY; - TimeExpression TOUCH_DATE_INTERVAL; + BooleanCheckBox VIDEO_ON_SHINY; + BooleanCheckBox RUN_FROM_EVERYTHING; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp index 8f0d977d67..c9eacf240e 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.cpp @@ -11,6 +11,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/PersistentSettings.h" #include "CommonFramework/Inference/ImageTools.h" #include "CommonFramework/Inference/InferenceThrottler.h" #include "PokemonSwSh/ShinyHuntTracker.h" @@ -36,7 +37,7 @@ ShinyHuntAutonomousOverworld::ShinyHuntAutonomousOverworld() ) , GO_HOME_WHEN_DONE( "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", - true + false ) , PRIORITIZE_EXCLAMATION_POINTS( "Prioritize Exclamation Points:
Given multiple options, prefer those with exclamation points." @@ -62,6 +63,10 @@ ShinyHuntAutonomousOverworld::ShinyHuntAutonomousOverworld() "Watchdog Timer:
Reset the game if you go this long without any encounters.", "120 * TICKS_PER_SECOND" ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -69,9 +74,13 @@ ShinyHuntAutonomousOverworld::ShinyHuntAutonomousOverworld() "Exit Battle Time:
After running, wait this long to return to overworld.", "6 * TICKS_PER_SECOND" ) - , TIME_ROLLBACK_HOURS( - "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", - 1, 0, 11 + , VIDEO_ON_SHINY( + "Video Capture:
Take a video of the encounter if it is shiny.", + true + ) + , RUN_FROM_EVERYTHING( + "Run from Everything:
Run from everything - even if it is shiny. (For testing only.)", + false ) { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); @@ -80,9 +89,13 @@ ShinyHuntAutonomousOverworld::ShinyHuntAutonomousOverworld() m_options.emplace_back(&LOCAL_CIRCLING, "LOCAL_CIRCLING"); m_options.emplace_back(&MAX_MOVE_DURATION, "MAX_MOVE_DURATION"); m_options.emplace_back(&WATCHDOG_TIMER, "WATCHDOG_TIMER"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); - m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); + if (settings.developer_mode){ + m_options.emplace_back(&VIDEO_ON_SHINY, "VIDEO_ON_SHINY"); + m_options.emplace_back(&RUN_FROM_EVERYTHING, "RUN_FROM_EVERYTHING"); + } } @@ -145,8 +158,8 @@ ShinyHuntAutonomousOverworld::WatchResult ShinyHuntAutonomousOverworld::whistle_ std::vector& exclamations, std::vector& questions ) const{ - StandardBattleMenuDetector battle_menu(env.console); StartBattleDetector start_battle(env.console, std::chrono::milliseconds(0)); + StandardBattleMenuDetector battle_menu(env.console); InferenceBoxScope search_area(env.console, 0.0, 0.2, 1.0, 0.8); const double center_x = 0.5; @@ -253,11 +266,13 @@ bool ShinyHuntAutonomousOverworld::find_encounter( tracker.run_away(); return false; case WatchResult::BATTLE_START: - env.log("Battle started!"); + env.log("Battle started! (whistle)"); return true; default:; } + AsyncStartBattleDetector start_battle(env, env.console); + // Nothing was found. Rotate the view and try again. if (exclamation_marks.empty() && question_marks.empty()){ env.log("Nothing found. Rotating view."); @@ -269,6 +284,10 @@ bool ShinyHuntAutonomousOverworld::find_encounter( pbf_move_right_joystick(192, 255, 50, 70); } env.console.botbase().wait_for_all_requests(); + if (start_battle.detected()){ + env.log("Battle started! (rotate)"); + return true; + } continue; } @@ -283,7 +302,10 @@ bool ShinyHuntAutonomousOverworld::find_encounter( "purple" ); Trajectory trajectory = get_trajectory_float(delta_x, delta_y); - exclamations.emplace(trajectory.distance_in_ticks, std::pair(trajectory, box)); + exclamations.emplace( + trajectory.distance_in_ticks, + std::pair(trajectory, box) + ); break; } @@ -296,7 +318,10 @@ bool ShinyHuntAutonomousOverworld::find_encounter( "purple" ); Trajectory trajectory = get_trajectory_float(delta_x, delta_y); - questions.emplace(trajectory.distance_in_ticks, std::pair(trajectory, box)); + questions.emplace( + trajectory.distance_in_ticks, + std::pair(trajectory, box) + ); break; } @@ -351,6 +376,10 @@ bool ShinyHuntAutonomousOverworld::find_encounter( } env.console.botbase().wait_for_all_requests(); + if (start_battle.detected()){ + env.log("Battle started! (move)"); + return true; + } } } @@ -363,7 +392,13 @@ void ShinyHuntAutonomousOverworld::program(SingleSwitchProgramEnvironment& env) uint32_t last_touch = system_clock(); Stats& stats = env.stats(); - StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); + StandardEncounterTracker tracker( + stats, env.console, + false, + EXIT_BATTLE_MASH_TIME, + VIDEO_ON_SHINY, + RUN_FROM_EVERYTHING + ); // Encounter Loop while (true){ diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h index da270e61e2..c4e70744a6 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Overworld.h @@ -61,9 +61,11 @@ class ShinyHuntAutonomousOverworld : public SingleSwitchProgram{ SimpleInteger LOCAL_CIRCLING; TimeExpression MAX_MOVE_DURATION; TimeExpression WATCHDOG_TIMER; + SimpleInteger TIME_ROLLBACK_HOURS; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; - SimpleInteger TIME_ROLLBACK_HOURS; + BooleanCheckBox VIDEO_ON_SHINY; + BooleanCheckBox RUN_FROM_EVERYTHING; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp index 910fb2140c..9db4c99caa 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/PersistentSettings.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" #include "PokemonSwSh_EncounterTracker.h" #include "PokemonSwSh_ShinyHunt-Regi.h" @@ -28,12 +29,16 @@ ShinyHuntAutonomousRegi::ShinyHuntAutonomousRegi() ) , GO_HOME_WHEN_DONE( "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", - true + false ) , REQUIRE_SQUARE( "Require Square:
Stop only for a square shiny. Run from star shinies.", false ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -45,18 +50,26 @@ ShinyHuntAutonomousRegi::ShinyHuntAutonomousRegi() "Transition Delay:
Time to enter/exit the building.", "5 * TICKS_PER_SECOND" ) - , TOUCH_DATE_INTERVAL( - "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "4 * 3600 * TICKS_PER_SECOND" + , VIDEO_ON_SHINY( + "Video Capture:
Take a video of the encounter if it is shiny.", + true + ) + , RUN_FROM_EVERYTHING( + "Run from Everything:
Run from everything - even if it is shiny. (For testing only.)", + false ) { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&REQUIRE_SQUARE, "REQUIRE_SQUARE"); m_options.emplace_back(®I_NAME, "REGI_NAME"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); m_options.emplace_back(&TRANSITION_DELAY, "TRANSITION_DELAY"); - m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); + if (settings.developer_mode){ + m_options.emplace_back(&VIDEO_ON_SHINY, "VIDEO_ON_SHINY"); + m_options.emplace_back(&RUN_FROM_EVERYTHING, "RUN_FROM_EVERYTHING"); + } } @@ -83,7 +96,13 @@ void ShinyHuntAutonomousRegi::program(SingleSwitchProgramEnvironment& env) const resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 200); Stats& stats = env.stats(); - StandardEncounterTracker tracker(stats, env.console, REQUIRE_SQUARE, EXIT_BATTLE_MASH_TIME); + StandardEncounterTracker tracker( + stats, env.console, + REQUIRE_SQUARE, + EXIT_BATTLE_MASH_TIME, + VIDEO_ON_SHINY, + RUN_FROM_EVERYTHING + ); uint32_t last_touch = system_clock() - TOUCH_DATE_INTERVAL; bool error = false; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h index 78643f9419..15c8ed23e3 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regi.h @@ -31,10 +31,12 @@ class ShinyHuntAutonomousRegi : public SingleSwitchProgram{ BooleanCheckBox GO_HOME_WHEN_DONE; RegiSelector REGI_NAME; BooleanCheckBox REQUIRE_SQUARE; + TimeExpression TOUCH_DATE_INTERVAL; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; TimeExpression TRANSITION_DELAY; - TimeExpression TOUCH_DATE_INTERVAL; + BooleanCheckBox VIDEO_ON_SHINY; + BooleanCheckBox RUN_FROM_EVERYTHING; }; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp index d255b77e8c..44848edcdd 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/PersistentSettings.h" #include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_RaidCatchDetector.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" @@ -29,7 +30,7 @@ ShinyHuntAutonomousRegigigas2::ShinyHuntAutonomousRegigigas2() ) , GO_HOME_WHEN_DONE( "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", - true + false ) , REVERSAL_PP( "Reversal PP:
The amount of Reversal PP you are saved with.", @@ -39,6 +40,10 @@ ShinyHuntAutonomousRegigigas2::ShinyHuntAutonomousRegigigas2() "Require Square:
Stop only for a square shiny. Run from star shinies.", false ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -46,17 +51,25 @@ ShinyHuntAutonomousRegigigas2::ShinyHuntAutonomousRegigigas2() "Catch to Overworld Delay:", "8 * TICKS_PER_SECOND" ) - , TOUCH_DATE_INTERVAL( - "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "4 * 3600 * TICKS_PER_SECOND" + , VIDEO_ON_SHINY( + "Video Capture:
Take a video of the encounter if it is shiny.", + true + ) + , RUN_FROM_EVERYTHING( + "Run from Everything:
Run from everything - even if it is shiny. (For testing only.)", + false ) { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&REVERSAL_PP, "REVERSAL_PP"); m_options.emplace_back(&REQUIRE_SQUARE, "REQUIRE_SQUARE"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&CATCH_TO_OVERWORLD_DELAY, "CATCH_TO_OVERWORLD_DELAY"); - m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); + if (settings.developer_mode){ + m_options.emplace_back(&VIDEO_ON_SHINY, "VIDEO_ON_SHINY"); + m_options.emplace_back(&RUN_FROM_EVERYTHING, "RUN_FROM_EVERYTHING"); + } } @@ -83,9 +96,11 @@ ShinyHuntAutonomousRegigigas2::Tracker::Tracker( ProgramEnvironment& env, ConsoleHandle& console, bool require_square, - uint16_t exit_battle_time + uint16_t exit_battle_time, + bool take_video, + bool run_from_everything ) - : StandardEncounterTracker(stats, console, require_square, exit_battle_time) + : StandardEncounterTracker(stats, console, require_square, exit_battle_time, take_video, run_from_everything) , m_env(env) {} bool ShinyHuntAutonomousRegigigas2::Tracker::run_away(){ @@ -126,7 +141,14 @@ void ShinyHuntAutonomousRegigigas2::program(SingleSwitchProgramEnvironment& env) resume_game_back_out(TOLERATE_SYSTEM_UPDATE_MENU_FAST, 500); Stats& stats = env.stats(); - Tracker tracker(stats, env, env.console, REQUIRE_SQUARE, 0); + Tracker tracker( + stats, + env, env.console, + REQUIRE_SQUARE, + 0, + VIDEO_ON_SHINY, + RUN_FROM_EVERYTHING + ); while (true){ for (uint8_t pp = REVERSAL_PP; pp > 0; pp--){ diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h index e7f645d4fb..403c852a2b 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Regigigas2.h @@ -36,7 +36,9 @@ class ShinyHuntAutonomousRegigigas2 : public SingleSwitchProgram{ ProgramEnvironment& env, ConsoleHandle& console, bool require_square, - uint16_t exit_battle_time + uint16_t exit_battle_time, + bool take_video, + bool run_from_everything ); virtual bool run_away() override; @@ -46,9 +48,11 @@ class ShinyHuntAutonomousRegigigas2 : public SingleSwitchProgram{ BooleanCheckBox GO_HOME_WHEN_DONE; SimpleInteger REVERSAL_PP; BooleanCheckBox REQUIRE_SQUARE; + TimeExpression TOUCH_DATE_INTERVAL; SectionDivider m_advanced_options; TimeExpression CATCH_TO_OVERWORLD_DELAY; - TimeExpression TOUCH_DATE_INTERVAL; + BooleanCheckBox VIDEO_ON_SHINY; + BooleanCheckBox RUN_FROM_EVERYTHING; }; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp index b4fb27af8f..46b38f0d0a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/PersistentSettings.h" #include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" #include "PokemonSwSh/Programs/PokemonSwSh_StartGame.h" @@ -29,7 +30,11 @@ ShinyHuntAutonomousStrongSpawn::ShinyHuntAutonomousStrongSpawn() ) , GO_HOME_WHEN_DONE( "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", - true + false + ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." @@ -38,15 +43,23 @@ ShinyHuntAutonomousStrongSpawn::ShinyHuntAutonomousStrongSpawn() "Exit Battle Time:
After running, wait this long to return to overworld.", "6 * TICKS_PER_SECOND" ) - , TIME_ROLLBACK_HOURS( - "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", - 1, 0, 11 + , VIDEO_ON_SHINY( + "Video Capture:
Take a video of the encounter if it is shiny.", + true + ) + , RUN_FROM_EVERYTHING( + "Run from Everything:
Run from everything - even if it is shiny. (For testing only.)", + false ) { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); - m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); + if (settings.developer_mode){ + m_options.emplace_back(&VIDEO_ON_SHINY, "VIDEO_ON_SHINY"); + m_options.emplace_back(&RUN_FROM_EVERYTHING, "RUN_FROM_EVERYTHING"); + } } @@ -67,8 +80,13 @@ std::unique_ptr ShinyHuntAutonomousStrongSpawn::make_stats() const -ShinyHuntAutonomousStrongSpawn::Tracker::Tracker(ShinyHuntTracker& stats, ConsoleHandle& console) - : StandardEncounterTracker(stats, console, false, 0) +ShinyHuntAutonomousStrongSpawn::Tracker::Tracker( + ShinyHuntTracker& stats, + ConsoleHandle& console, + bool take_video, + bool run_from_everything +) + : StandardEncounterTracker(stats, console, false, 0, take_video, run_from_everything) {} bool ShinyHuntAutonomousStrongSpawn::Tracker::run_away(){ pbf_press_button(BUTTON_HOME, 10, GAME_TO_HOME_DELAY_SAFE); @@ -83,7 +101,7 @@ void ShinyHuntAutonomousStrongSpawn::program(SingleSwitchProgramEnvironment& env uint32_t last_touch = system_clock(); Stats& stats = env.stats(); - Tracker tracker(stats, env.console); + Tracker tracker(stats, env.console, VIDEO_ON_SHINY, RUN_FROM_EVERYTHING); while (true){ env.update_stats(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h index 98b75c81ba..5186d233ac 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-StrongSpawn.h @@ -28,14 +28,21 @@ class ShinyHuntAutonomousStrongSpawn : public SingleSwitchProgram{ private: struct Stats; struct Tracker : public StandardEncounterTracker{ - Tracker(ShinyHuntTracker& stats, ConsoleHandle& console); + Tracker( + ShinyHuntTracker& stats, + ConsoleHandle& console, + bool take_video, + bool run_from_everything + ); virtual bool run_away() override; }; BooleanCheckBox GO_HOME_WHEN_DONE; + SimpleInteger TIME_ROLLBACK_HOURS; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; - SimpleInteger TIME_ROLLBACK_HOURS; + BooleanCheckBox VIDEO_ON_SHINY; + BooleanCheckBox RUN_FROM_EVERYTHING; }; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp index 08c0ceb21f..9fd57fd17b 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/PersistentSettings.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" #include "PokemonSwSh_EncounterTracker.h" #include "PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h" @@ -28,12 +29,16 @@ ShinyHuntAutonomousSwordsOfJustice::ShinyHuntAutonomousSwordsOfJustice() ) , GO_HOME_WHEN_DONE( "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", - true + false ) , AIRPLANE_MODE( "Airplane Mode:
Enable if airplane mode is on.", false ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -45,17 +50,25 @@ ShinyHuntAutonomousSwordsOfJustice::ShinyHuntAutonomousSwordsOfJustice() "Enter Camp Delay:", "8 * TICKS_PER_SECOND" ) - , TIME_ROLLBACK_HOURS( - "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", - 1, 0, 11 + , VIDEO_ON_SHINY( + "Video Capture:
Take a video of the encounter if it is shiny.", + true + ) + , RUN_FROM_EVERYTHING( + "Run from Everything:
Run from everything - even if it is shiny. (For testing only.)", + false ) { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); m_options.emplace_back(&AIRPLANE_MODE, "AIRPLANE_MODE"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); m_options.emplace_back(&ENTER_CAMP_DELAY, "ENTER_CAMP_DELAY"); - m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); + if (settings.developer_mode){ + m_options.emplace_back(&VIDEO_ON_SHINY, "VIDEO_ON_SHINY"); + m_options.emplace_back(&RUN_FROM_EVERYTHING, "RUN_FROM_EVERYTHING"); + } } @@ -84,7 +97,13 @@ void ShinyHuntAutonomousSwordsOfJustice::program(SingleSwitchProgramEnvironment& uint32_t last_touch = system_clock(); Stats& stats = env.stats(); - StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); + StandardEncounterTracker tracker( + stats, env.console, + false, + EXIT_BATTLE_MASH_TIME, + VIDEO_ON_SHINY, + RUN_FROM_EVERYTHING + ); while (true){ env.update_stats(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h index 99e3a5f319..4fc0d14c36 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-SwordsOfJustice.h @@ -29,10 +29,12 @@ class ShinyHuntAutonomousSwordsOfJustice : public SingleSwitchProgram{ BooleanCheckBox GO_HOME_WHEN_DONE; BooleanCheckBox AIRPLANE_MODE; + SimpleInteger TIME_ROLLBACK_HOURS; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; TimeExpression ENTER_CAMP_DELAY; - SimpleInteger TIME_ROLLBACK_HOURS; + BooleanCheckBox VIDEO_ON_SHINY; + BooleanCheckBox RUN_FROM_EVERYTHING; }; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp index a625d6d353..a23d482a27 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.cpp @@ -10,6 +10,7 @@ #include "Common/PokemonSwSh/PokemonSettings.h" #include "Common/PokemonSwSh/PokemonSwShGameEntry.h" #include "Common/PokemonSwSh/PokemonSwShDateSpam.h" +#include "CommonFramework/PersistentSettings.h" #include "PokemonSwSh/Inference/PokemonSwSh_StartBattleDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_BattleMenuDetector.h" #include "PokemonSwSh/Inference/ShinyDetection/PokemonSwSh_ShinyEncounterDetector.h" @@ -30,7 +31,11 @@ ShinyHuntAutonomousWhistling::ShinyHuntAutonomousWhistling() ) , GO_HOME_WHEN_DONE( "Go Home when Done:
After finding a shiny, go to the Switch Home menu to idle. (turn this off for unattended streaming)", - true + false + ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." @@ -39,15 +44,23 @@ ShinyHuntAutonomousWhistling::ShinyHuntAutonomousWhistling() "Exit Battle Time:
After running, wait this long to return to overworld.", "6 * TICKS_PER_SECOND" ) - , TIME_ROLLBACK_HOURS( - "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", - 1, 0, 11 + , VIDEO_ON_SHINY( + "Video Capture:
Take a video of the encounter if it is shiny.", + true + ) + , RUN_FROM_EVERYTHING( + "Run from Everything:
Run from everything - even if it is shiny. (For testing only.)", + false ) { m_options.emplace_back(&GO_HOME_WHEN_DONE, "GO_HOME_WHEN_DONE"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&EXIT_BATTLE_MASH_TIME, "EXIT_BATTLE_MASH_TIME"); - m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); + if (settings.developer_mode){ + m_options.emplace_back(&VIDEO_ON_SHINY, "VIDEO_ON_SHINY"); + m_options.emplace_back(&RUN_FROM_EVERYTHING, "RUN_FROM_EVERYTHING"); + } } @@ -80,7 +93,14 @@ void ShinyHuntAutonomousWhistling::program(SingleSwitchProgramEnvironment& env) uint32_t last_touch = system_clock(); Stats& stats = env.stats(); - StandardEncounterTracker tracker(stats, env.console, false, EXIT_BATTLE_MASH_TIME); + StandardEncounterTracker tracker( + stats, env.console, + false, + EXIT_BATTLE_MASH_TIME, + VIDEO_ON_SHINY, + RUN_FROM_EVERYTHING + ); + while (true){ env.update_stats(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h index 38b59da6ad..11a208aa89 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntAutonomous-Whistling.h @@ -28,9 +28,11 @@ class ShinyHuntAutonomousWhistling : public SingleSwitchProgram{ struct Stats; BooleanCheckBox GO_HOME_WHEN_DONE; + SimpleInteger TIME_ROLLBACK_HOURS; SectionDivider m_advanced_options; TimeExpression EXIT_BATTLE_MASH_TIME; - SimpleInteger TIME_ROLLBACK_HOURS; + BooleanCheckBox VIDEO_ON_SHINY; + BooleanCheckBox RUN_FROM_EVERYTHING; }; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp index 0a1f21f5d9..e80f879ac6 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.cpp @@ -27,6 +27,10 @@ ShinyHuntUnattendedIoATrade::ShinyHuntUnattendedIoATrade() "Start to Run Delay:
This needs to be carefully calibrated.", "1260" ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -42,17 +46,13 @@ ShinyHuntUnattendedIoATrade::ShinyHuntUnattendedIoATrade() "Mash to Trade Delay:
Time to perform the trade.", "29 * TICKS_PER_SECOND" ) - , TOUCH_DATE_INTERVAL( - "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "4 * 3600 * TICKS_PER_SECOND" - ) { m_options.emplace_back(&START_TO_RUN_DELAY, "START_TO_RUN_DELAY"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&FLY_DURATION, "FLY_DURATION"); m_options.emplace_back(&MOVE_DURATION, "MOVE_DURATION"); m_options.emplace_back(&MASH_TO_TRADE_DELAY, "MASH_TO_TRADE_DELAY"); - m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); } void ShinyHuntUnattendedIoATrade::program(SingleSwitchProgramEnvironment& env) const{ diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.h index a88ebf1f85..824c9ac9f7 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-IoATrade.h @@ -23,11 +23,11 @@ class ShinyHuntUnattendedIoATrade : public SingleSwitchProgram{ private: TimeExpression START_TO_RUN_DELAY; + TimeExpression TOUCH_DATE_INTERVAL; SectionDivider m_advanced_options; TimeExpression FLY_DURATION; TimeExpression MOVE_DURATION; TimeExpression MASH_TO_TRADE_DELAY; - TimeExpression TOUCH_DATE_INTERVAL; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp index 93f4ac167d..718eeaf43e 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.cpp @@ -29,6 +29,10 @@ ShinyHuntUnattendedRegi::ShinyHuntUnattendedRegi() "Start to Run Delay:
This needs to be carefully calibrated.", "1990" ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -40,17 +44,13 @@ ShinyHuntUnattendedRegi::ShinyHuntUnattendedRegi() "Transition Delay:
Time to enter/exit the building.", "5 * TICKS_PER_SECOND" ) - , TOUCH_DATE_INTERVAL( - "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "4 * 3600 * TICKS_PER_SECOND" - ) { m_options.emplace_back(&START_TO_RUN_DELAY, "START_TO_RUN_DELAY"); m_options.emplace_back(®I_NAME, "REGI_NAME"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&CORRECTION_INTERVAL, "CORRECTION_INTERVAL"); m_options.emplace_back(&TRANSITION_DELAY, "TRANSITION_DELAY"); - m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.h index e2ac44e7ab..9158b51fb0 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regi.h @@ -26,10 +26,10 @@ class ShinyHuntUnattendedRegi : public SingleSwitchProgram{ private: TimeExpression START_TO_RUN_DELAY; RegiSelector REGI_NAME; + TimeExpression TOUCH_DATE_INTERVAL; SectionDivider m_advanced_options; SimpleInteger CORRECTION_INTERVAL; TimeExpression TRANSITION_DELAY; - TimeExpression TOUCH_DATE_INTERVAL; }; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp index 99cffa95d3..cff9d8f25a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.cpp @@ -31,6 +31,10 @@ ShinyHuntUnattendedRegigigas2::ShinyHuntUnattendedRegigigas2() "Start to Attack Delay:
This needs to be carefully calibrated.", "3750" ) + , TOUCH_DATE_INTERVAL( + "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", + "4 * 3600 * TICKS_PER_SECOND" + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -42,17 +46,13 @@ ShinyHuntUnattendedRegigigas2::ShinyHuntUnattendedRegigigas2() "Catch to Overworld Delay:", "8 * TICKS_PER_SECOND" ) - , TOUCH_DATE_INTERVAL( - "Rollover Prevention:
Prevent a den from rolling over by periodically touching the date. If set to zero, this feature is disabled.", - "4 * 3600 * TICKS_PER_SECOND" - ) { m_options.emplace_back(&REVERSAL_PP, "REVERSAL_PP"); m_options.emplace_back(&START_TO_ATTACK_DELAY, "START_TO_ATTACK_DELAY"); + m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&ATTACK_TO_CATCH_DELAY, "ATTACK_TO_CATCH_DELAY"); m_options.emplace_back(&CATCH_TO_OVERWORLD_DELAY, "CATCH_TO_OVERWORLD_DELAY"); - m_options.emplace_back(&TOUCH_DATE_INTERVAL, "TOUCH_DATE_INTERVAL"); } void ShinyHuntUnattendedRegigigas2::program(SingleSwitchProgramEnvironment& env) const{ diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.h index 8249166901..b57800e52d 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-Regigigas2.h @@ -25,10 +25,10 @@ class ShinyHuntUnattendedRegigigas2 : public SingleSwitchProgram{ private: SimpleInteger REVERSAL_PP; TimeExpression START_TO_ATTACK_DELAY; + TimeExpression TOUCH_DATE_INTERVAL; SectionDivider m_advanced_options; TimeExpression ATTACK_TO_CATCH_DELAY; TimeExpression CATCH_TO_OVERWORLD_DELAY; - TimeExpression TOUCH_DATE_INTERVAL; }; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.cpp index 9d137c6f34..4f45ed29b1 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.cpp @@ -31,9 +31,6 @@ ShinyHuntUnattendedStrongSpawn::ShinyHuntUnattendedStrongSpawn() "Start Game Wait Delay:
Decrease this if your game starts quickly.", "20 * TICKS_PER_SECOND" ) - , m_advanced_options( - "Advanced Options: You should not need to touch anything below here." - ) , TIME_ROLLBACK_HOURS( "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", 1, 0, 11 @@ -41,7 +38,6 @@ ShinyHuntUnattendedStrongSpawn::ShinyHuntUnattendedStrongSpawn() { m_options.emplace_back(&ENTER_GAME_TO_RUN_DELAY, "ENTER_GAME_TO_RUN_DELAY"); m_options.emplace_back(&START_GAME_WAIT_DELAY, "START_GAME_WAIT_DELAY"); - m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.h index 10dd553068..fb6e84c960 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-StrongSpawn.h @@ -7,7 +7,7 @@ #ifndef PokemonAutomation_PokemonSwSh_ShinyHuntUnattendedStrongSpawn_H #define PokemonAutomation_PokemonSwSh_ShinyHuntUnattendedStrongSpawn_H -#include "CommonFramework/Options/SectionDivider.h" +//#include "CommonFramework/Options/SectionDivider.h" #include "CommonFramework/Options/BooleanCheckBox.h" #include "CommonFramework/Options/SimpleInteger.h" #include "NintendoSwitch/Options/TimeExpression.h" @@ -26,7 +26,6 @@ class ShinyHuntUnattendedStrongSpawn : public SingleSwitchProgram{ private: TimeExpression ENTER_GAME_TO_RUN_DELAY; TimeExpression START_GAME_WAIT_DELAY; - SectionDivider m_advanced_options; SimpleInteger TIME_ROLLBACK_HOURS; }; diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.cpp index 94fb1af9ed..46845b0770 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.cpp @@ -31,6 +31,10 @@ ShinyHuntUnattendedSwordsOfJustice::ShinyHuntUnattendedSwordsOfJustice() "Airplane Mode:
Enable if airplane mode is on.", false ) + , TIME_ROLLBACK_HOURS( + "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", + 1, 0, 11 + ) , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) @@ -38,16 +42,12 @@ ShinyHuntUnattendedSwordsOfJustice::ShinyHuntUnattendedSwordsOfJustice() "Enter Camp Delay:", "8 * TICKS_PER_SECOND" ) - , TIME_ROLLBACK_HOURS( - "Time Rollback (in hours):
Periodically roll back the time to keep the weather the same. If set to zero, this feature is disabled.", - 1, 0, 11 - ) { m_options.emplace_back(&EXIT_CAMP_TO_RUN_DELAY, "EXIT_CAMP_TO_RUN_DELAY"); m_options.emplace_back(&AIRPLANE_MODE, "AIRPLANE_MODE"); + m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); m_options.emplace_back(&m_advanced_options, ""); m_options.emplace_back(&ENTER_CAMP_DELAY, "ENTER_CAMP_DELAY"); - m_options.emplace_back(&TIME_ROLLBACK_HOURS, "TIME_ROLLBACK_HOURS"); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.h b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.h index fe4bd495c1..08f713cbfe 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/ShinyHunting/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.h @@ -26,9 +26,9 @@ class ShinyHuntUnattendedSwordsOfJustice : public SingleSwitchProgram{ private: TimeExpression EXIT_CAMP_TO_RUN_DELAY; BooleanCheckBox AIRPLANE_MODE; + SimpleInteger TIME_ROLLBACK_HOURS; SectionDivider m_advanced_options; TimeExpression ENTER_CAMP_DELAY; - SimpleInteger TIME_ROLLBACK_HOURS; }; }