From 33b7b19984872f5a439699dac15500f9af41d64d Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Tue, 7 Jan 2025 17:29:10 +0100 Subject: [PATCH 01/11] inital SwSh Daily Highlight RNG --- SerialPrograms/CMakeLists.txt | 4 + .../Source/PokemonSwSh/PokemonSwSh_Panels.cpp | 22 +- .../RNG/PokemonSwSh_DailyHighlightRNG.cpp | 338 ++++++++++++++++++ .../RNG/PokemonSwSh_DailyHighlightRNG.h | 77 ++++ .../PokemonSwSh_DailyHighlightDatabase.cpp | 53 +++ .../PokemonSwSh_DailyHighlightDatabase.h | 38 ++ 6 files changed, 522 insertions(+), 10 deletions(-) create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h create mode 100644 SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.cpp create mode 100644 SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h diff --git a/SerialPrograms/CMakeLists.txt b/SerialPrograms/CMakeLists.txt index 741fd545e1..6e9fe54a6a 100644 --- a/SerialPrograms/CMakeLists.txt +++ b/SerialPrograms/CMakeLists.txt @@ -2222,6 +2222,8 @@ file(GLOB MAIN_SOURCES Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h Source/PokemonSwSh/Programs/RNG/PokemonSwSh_CramomaticRNG.cpp Source/PokemonSwSh/Programs/RNG/PokemonSwSh_CramomaticRNG.h + Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp + Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h Source/PokemonSwSh/Programs/RNG/PokemonSwSh_SeedFinder.cpp Source/PokemonSwSh/Programs/RNG/PokemonSwSh_SeedFinder.h Source/PokemonSwSh/Programs/ShinyHuntAutonomous/PokemonSwSh_ShinyHuntAutonomous-BerryTree.cpp @@ -2260,6 +2262,8 @@ file(GLOB MAIN_SOURCES Source/PokemonSwSh/Programs/ShinyHuntUnattended/PokemonSwSh_ShinyHuntUnattended-SwordsOfJustice.h Source/PokemonSwSh/Programs/TestPrograms/PokemonSwSh_ShinyEncounterTester.cpp Source/PokemonSwSh/Programs/TestPrograms/PokemonSwSh_ShinyEncounterTester.h + Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.cpp + Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h Source/PokemonSwSh/Resources/PokemonSwSh_MaxLairDatabase.cpp Source/PokemonSwSh/Resources/PokemonSwSh_MaxLairDatabase.h Source/PokemonSwSh/Resources/PokemonSwSh_NameDatabase.cpp diff --git a/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp b/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp index 34b71ffe24..4ed489bdb3 100644 --- a/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp +++ b/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp @@ -47,14 +47,9 @@ #include "Programs/NonShinyHunting/PokemonSwSh_StatsReset-Moltres.h" #include "Programs/NonShinyHunting/PokemonSwSh_StatsReset-Regi.h" -#include "Programs/EggPrograms/PokemonSwSh_EggAutonomous.h" -#include "Programs/EggPrograms/PokemonSwSh_EggFetcher2.h" -#include "Programs/EggPrograms/PokemonSwSh_EggFetcherMultiple.h" -#include "Programs/EggPrograms/PokemonSwSh_EggHatcher.h" -#include "Programs/EggPrograms/PokemonSwSh_EggCombined2.h" -#include "Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.h" -#include "Programs/EggPrograms/PokemonSwSh_GodEggDuplication.h" -#include "Programs/EggPrograms/PokemonSwSh_GodEggItemDupe.h" +#include "Programs/RNG/PokemonSwSh_CramomaticRNG.h" +#include "Programs/RNG/PokemonSwSh_DailyHighlightRNG.h" +#include "Programs/RNG/PokemonSwSh_SeedFinder.h" #include "Programs/ShinyHuntUnattended/PokemonSwSh_MultiGameFossil.h" #include "Programs/ShinyHuntUnattended/PokemonSwSh_ShinyHuntUnattended-Regi.h" @@ -74,8 +69,14 @@ #include "Programs/ShinyHuntAutonomous/PokemonSwSh_ShinyHuntAutonomous-Fishing.h" #include "Programs/OverworldBot/PokemonSwSh_ShinyHuntAutonomous-Overworld.h" -#include "Programs/RNG/PokemonSwSh_CramomaticRNG.h" -#include "Programs/RNG/PokemonSwSh_SeedFinder.h" +#include "Programs/EggPrograms/PokemonSwSh_EggAutonomous.h" +#include "Programs/EggPrograms/PokemonSwSh_EggFetcher2.h" +#include "Programs/EggPrograms/PokemonSwSh_EggFetcherMultiple.h" +#include "Programs/EggPrograms/PokemonSwSh_EggHatcher.h" +#include "Programs/EggPrograms/PokemonSwSh_EggCombined2.h" +#include "Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.h" +#include "Programs/EggPrograms/PokemonSwSh_GodEggDuplication.h" +#include "Programs/EggPrograms/PokemonSwSh_GodEggItemDupe.h" #include "Programs/PokemonSwSh_SynchronizedSpinning.h" #include "Programs/PokemonSwSh_RaidItemFarmerOKHO.h" @@ -179,6 +180,7 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back("---- RNG ----"); ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); + ret.emplace_back(make_single_switch_program()); ret.emplace_back("---- Multi-Switch Programs ----"); ret.emplace_back(make_multi_switch_program()); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp new file mode 100644 index 0000000000..32b2acd15a --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -0,0 +1,338 @@ +/* RNG Manipulation of the Highlight Watt Trader in the Snowslide Slope area in the Crown Tundra + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + * Based on Anubis' findings: https://docs.google.com/spreadsheets/u/0/d/1pNYtCJKRh_efX9LvzjCiA-0n2lGSFnVmSWwmPzgSOMw/htmlview + * + */ + +#include +#include +#include "CommonFramework/Exceptions/ProgramFinishedException.h" +#include "CommonFramework/Exceptions/OperationFailedException.h" +#include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/VideoPipeline/VideoFeed.h" +#include "CommonFramework/Tools/StatsTracking.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "Pokemon/Pokemon_Strings.h" +#include "PokemonSwSh/PokemonSwSh_Settings.h" +#include "PokemonSwSh/Commands/PokemonSwSh_Commands_DateSpam.h" +#include "PokemonSwSh/Inference/PokemonSwSh_SelectionArrowFinder.h" +#include "PokemonSwSh/Programs/PokemonSwSh_MenuNavigation.h" +#include "PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h" +#include "PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h" + + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ +using namespace Pokemon; + + +const DailyHighlightDatabase& DAILY_HIGHLIGHT_DATABASE() { + static DailyHighlightDatabase database("PokemonSwSh/DailyHighlights.json"); + return database; +} + + +DailyHighlightRNG_Descriptor::DailyHighlightRNG_Descriptor() + : SingleSwitchProgramDescriptor( + "PokemonSwSh:DailyHighlightRNG", + STRING_POKEMON + " SwSh", "Daily Highlight RNG", + "ComputerControl/blob/master/Wiki/Programs/PokemonSwSh/DailyHighlightRNG.md", + "Perform RNG manipulation to get rare items from the daily highlight trader.", + FeedbackType::REQUIRED, + AllowCommandsWhenRunning::DISABLE_COMMANDS, + PABotBaseLevel::PABOTBASE_12KB + ) +{} + +struct DailyHighlightRNG_Descriptor::Stats : public StatsTracker{ +public: + Stats() + : iterations(m_stats["Iterations"]) + , reads(m_stats["Seed Reads"]) + , errors(m_stats["Errors"]) + , highlights(m_stats["Highlights"]) + { + m_display_order.emplace_back("Iterations"); + m_display_order.emplace_back("Seed Reads"); + m_display_order.emplace_back("Highlights"); + m_display_order.emplace_back("Errors", HIDDEN_IF_ZERO); + } + +public: + std::atomic& iterations; + std::atomic& reads; + std::atomic& errors; + std::atomic& highlights; +}; +std::unique_ptr DailyHighlightRNG_Descriptor::make_stats() const{ + return std::unique_ptr(new Stats()); +} + +DailyHighlightRNG::DailyHighlightRNG() + : NUM_HIGHLIGHTS( + "Number of highlights:
How many daily highlights should be bought. A value of 0 will run until you run out of Watts.", + LockMode::UNLOCK_WHILE_RUNNING, 0) + , CONTINUE( + "Continue from last time:
If the initial two daily highlights are already manipulated and should be bought.", + LockMode::LOCK_WHILE_RUNNING, false) + , HIGHLIGHT_SELECTION( + "Desired Highlights:", + "Highlight", + DAILY_HIGHLIGHT_DATABASE().database(), + "bottle-cap-1") + , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) + , NOTIFICATIONS({ + &NOTIFICATION_STATUS_UPDATE, + &NOTIFICATION_PROGRAM_FINISH, + &NOTIFICATION_ERROR_RECOVERABLE, + &NOTIFICATION_ERROR_FATAL, + }) + , m_advanced_options( + "Advanced Options: You should not need to touch anything below here." + ) + , MAX_UNKNOWN_ADVANCES( + "Max Unknown advances:
How many advances to check when updating the rng state.", + LockMode::LOCK_WHILE_RUNNING, + 100000 + ) + , ADVANCE_PRESS_DURATION( + "Advance Press Duration:
Hold the button down for this long to advance once.", + LockMode::LOCK_WHILE_RUNNING, + 10 + ) + , ADVANCE_RELEASE_DURATION( + "Advance Release Duration:
After releasing the button, wait this long before pressing it again.", + LockMode::LOCK_WHILE_RUNNING, + 10 + ) + , SAVE_SCREENSHOTS( + "Save Debug Screenshots:", + LockMode::LOCK_WHILE_RUNNING, + false + ) + , LOG_VALUES( + "Log Animation Values:
", + LockMode::LOCK_WHILE_RUNNING, + false + ) +{ + PA_ADD_OPTION(START_LOCATION); + + PA_ADD_OPTION(NUM_HIGHLIGHTS); + PA_ADD_OPTION(CONTINUE); + PA_ADD_OPTION(HIGHLIGHT_SELECTION); + + PA_ADD_OPTION(NOTIFICATIONS); + + PA_ADD_STATIC(m_advanced_options); + PA_ADD_OPTION(MAX_UNKNOWN_ADVANCES); + PA_ADD_OPTION(ADVANCE_PRESS_DURATION); + PA_ADD_OPTION(ADVANCE_RELEASE_DURATION); + PA_ADD_OPTION(SAVE_SCREENSHOTS); + PA_ADD_OPTION(LOG_VALUES); +} + +void DailyHighlightRNG::move_to_trader(SingleSwitchProgramEnvironment& env, BotBaseContext& context) { + pbf_move_left_joystick(context, 207, 1, 160, 10); // Magic numbers to barely reach the trader + pbf_press_button(context, BUTTON_A, 20, 20); + // TODO: check if NPC was reached -> DialogArrow +} + +void DailyHighlightRNG::navigate_to_party(SingleSwitchProgramEnvironment& env, BotBaseContext& context){ + pbf_mash_button(context, BUTTON_B, 2 * TICKS_PER_SECOND); // exit dialog + pbf_press_button(context, BUTTON_X, 10, GameSettings::instance().OVERWORLD_TO_MENU_DELAY); + navigate_to_menu_app(env, env.console, context, 1, NOTIFICATION_ERROR_RECOVERABLE); // TODO: try-catch-block + error recovery + pbf_press_button(context, BUTTON_A, 10, 3*TICKS_PER_SECOND); + context.wait_for_all_requests(); +} + +uint8_t DailyHighlightRNG::calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, BotBaseContext& context, Pokemon::Xoroshiro128Plus& rng){ + // TODO: implement + return 2; // Usually either 1 or 2 -> higher numbers suggest bad npc state +} + +size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, Xoroshiro128PlusState state, uint8_t num_npcs, std::vector wanted_highlights){ + Xoroshiro128Plus rng(state); + size_t advances = 0; + bool found_advance_amount = false; + + std::vector> ranges; + for (std::string slug : wanted_highlights) { + ranges.push_back(DAILY_HIGHLIGHT_DATABASE().get_range_for_slug(slug)); + } + + while (!found_advance_amount) { + // calculate the result for the current temp_rng state + Xoroshiro128Plus temp_rng(rng.get_state()); + + for (size_t i = 0; i < num_npcs; i++) { + temp_rng.nextInt(91); + } + temp_rng.next(); + temp_rng.nextInt(60); + + uint64_t highlight_roll = temp_rng.nextInt(1000); + + for (auto& range : ranges) { + if (range.first < highlight_roll && range.second > highlight_roll) { + found_advance_amount = true; + // TODO: check if affordable, remove from vector if not. If vector empty: throw Exception + } + } + + if (!found_advance_amount) { + rng.next(); + advances++; + } + } + + return advances; +} + +void DailyHighlightRNG::leave_to_overworld_and_interact(SingleSwitchProgramEnvironment& env, BotBaseContext& context, bool buy_highlight) { + // Close menu + pbf_press_button(context, BUTTON_B, 2 * TICKS_PER_SECOND, 5); + pbf_press_button(context, BUTTON_B, 10, 70); + + // Quickly interact + pbf_press_button(context, BUTTON_A, 30, 30); + pbf_wait(context, 2*TICKS_PER_SECOND); + + // TODO: check if interaction worked -> see move_to_trader() + + // Buy highlight + if (buy_highlight) { + pbf_press_button(context, BUTTON_ZL, 10, 40); + pbf_press_dpad(context, DPAD_DOWN, 10, 10); + pbf_mash_button(context, BUTTON_ZL, 400); + } + + // Leave dialog + pbf_mash_button(context, BUTTON_B, 6 * TICKS_PER_SECOND); +} + +void DailyHighlightRNG::recover_from_wrong_state(SingleSwitchProgramEnvironment& env, BotBaseContext& context) { + // Mash the B button to exit potential menus or dialog boxes + pbf_mash_button(context, BUTTON_B, 30 * TICKS_PER_SECOND); + + // Open map + pbf_press_button(context, BUTTON_X, 20, GameSettings::instance().OVERWORLD_TO_MENU_DELAY); + navigate_to_menu_app(env, env.console, context, 5, NOTIFICATION_ERROR_RECOVERABLE); + + // Fly to Snowslide Slope + pbf_move_left_joystick(context, 200, 210, 20, 20); + pbf_mash_button(context, BUTTON_A, 1 * TICKS_PER_SECOND); +} + + +void DailyHighlightRNG::advance_date(SingleSwitchProgramEnvironment& env, BotBaseContext& context, uint8_t& year) { + pbf_press_button(context, BUTTON_HOME, 10, GameSettings::instance().GAME_TO_HOME_DELAY_FAST); + home_roll_date_enter_game_autorollback(env.console, context, year); + //resume_game_from_home(env.console, context, false); +} + + +void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, BotBaseContext& context){ + DailyHighlightRNG_Descriptor::Stats& stats = env.current_stats(); + env.update_stats(); + + std::vector wanted_highlights = HIGHLIGHT_SELECTION.all_slugs(); + if (wanted_highlights.empty()){ + throw UserSetupError(env.console, "At least one highlight item needs to be selected!"); + } + + if (START_LOCATION.start_in_grip_menu()) { + grip_menu_connect_go_home(context); + } + else { + pbf_press_button(context, BUTTON_B, 5, 5); + } + + Xoroshiro128Plus rng(0, 0); + bool is_state_valid = false; + size_t iteration = 0; + size_t bought_highlights = 0; // = CONTINUE ? iteration : max(0, iteration - 2); + uint8_t year = MAX_YEAR; + uint16_t state_errors = 0; + + // TODO: save from time to time + move_to_trader(env, context); + + while (bought_highlights < NUM_HIGHLIGHTS || NUM_HIGHLIGHTS <= 0){ + iteration++; + stats.iterations++; + env.update_stats(); + send_program_status_notification(env, NOTIFICATION_STATUS_UPDATE); + env.console.log("Daily Highlight RNG iteration: " + std::to_string(iteration)); + + advance_date(env, context, year); + navigate_to_party(env, context); + context.wait_for_all_requests(); + + // Find RNG state + if (!is_state_valid){ + rng = Xoroshiro128Plus(find_rng_state(env.console, context, SAVE_SCREENSHOTS, LOG_VALUES)); +// rng = Xoroshiro128Plus(100, 10000); + is_state_valid = true; + stats.reads++; + }else{ + rng = Xoroshiro128Plus(refind_rng_state(env.console, context, rng.get_state(), 0, MAX_UNKNOWN_ADVANCES, SAVE_SCREENSHOTS, LOG_VALUES)); + stats.reads++; + } + + Xoroshiro128PlusState rng_state = rng.get_state(); + if (rng_state.s0 == 0 && rng_state.s1 == 0){ + stats.errors++; + env.update_stats(); + + state_errors++; + if (state_errors >= 3){ + OperationFailedException::fire( + env.console, ErrorReport::SEND_ERROR_REPORT, + "Detected invalid RNG state three times in a row." + ); + } + VideoSnapshot screen = env.console.video().snapshot(); + send_program_recoverable_error_notification(env, NOTIFICATION_ERROR_RECOVERABLE, "Detected invalid RNG state.", screen); + recover_from_wrong_state(env, context); + is_state_valid = false; + continue; + } + + // Calibrate number of NPCs in the area and check whether Trader is in slow state + uint8_t num_npcs = calibrate_num_npc_from_party(env, context, rng); + + // Do advances + size_t target_advances = calculate_target(env, rng.get_state(), num_npcs, HIGHLIGHT_SELECTION.all_slugs()); + env.console.log("Needed advances: " + std::to_string(target_advances)); + do_rng_advances(env.console, context, rng, target_advances, ADVANCE_PRESS_DURATION, ADVANCE_RELEASE_DURATION); + + // Talk to NPC and buy highlight + bool buy_highlight = (iteration >= 3) || CONTINUE; + leave_to_overworld_and_interact(env, context, buy_highlight); + + if (buy_highlight){ + bought_highlights++; + stats.highlights++; + } + + uint32_t watts = 1000000; // TODO: read screen + uint32_t lowest_cost = 500; // TODO: calculate + // Out of Watts. + if (watts < lowest_cost){ + throw_and_log(env.console, "Cannot buy more daily highlights with the remaining Watts.", env.console); + } + + env.update_stats(); + state_errors = 0; + } + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); +} + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h new file mode 100644 index 0000000000..72df3c30ce --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h @@ -0,0 +1,77 @@ +/* RNG Manipulation of the Highlight Watt Trader in the Snowslide Slope area in the Crown Tundra + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + * Based on Anubis' findings: https://docs.google.com/spreadsheets/u/0/d/1pNYtCJKRh_efX9LvzjCiA-0n2lGSFnVmSWwmPzgSOMw/htmlview + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_DailyHighlightRNG_H +#define PokemonAutomation_PokemonSwSh_DailyHighlightRNG_H + +#include "Common/Cpp/Options/BooleanCheckBoxOption.h" +#include "Common/Cpp/Options/SimpleIntegerOption.h" +#include "Common/Cpp/Options/StaticTextOption.h" +#include "CommonFramework/Notifications/EventNotificationsTable.h" +#include "CommonFramework/Options/LanguageOCROption.h" +#include "CommonFramework/Options/StringSelectTableOption.h" +#include "NintendoSwitch/Options/NintendoSwitch_StartInGripMenuOption.h" +#include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" +#include "Pokemon/Pokemon_Xoroshiro128Plus.h" +#include "PokemonSwSh/Options/PokemonSwSh_DateToucher.h" +#include "PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h" + +namespace PokemonAutomation{ +namespace NintendoSwitch{ +namespace PokemonSwSh{ + + +class DailyHighlightRNG_Descriptor : public SingleSwitchProgramDescriptor{ +public: + DailyHighlightRNG_Descriptor(); + + struct Stats; + virtual std::unique_ptr make_stats() const override; +}; + +class DailyHighlightRNG : public SingleSwitchProgramInstance{ +public: + DailyHighlightRNG(); + + virtual void program(SingleSwitchProgramEnvironment& env, BotBaseContext& context) override; + +private: + StartInGripOrGameOption START_LOCATION; + SimpleIntegerOption NUM_HIGHLIGHTS; + BooleanCheckBoxOption CONTINUE; + StringSelectTableOption HIGHLIGHT_SELECTION; + + EventNotificationOption NOTIFICATION_STATUS_UPDATE; + EventNotificationsOption NOTIFICATIONS; + + SectionDividerOption m_advanced_options; + SimpleIntegerOption MAX_UNKNOWN_ADVANCES; + SimpleIntegerOption ADVANCE_PRESS_DURATION; + SimpleIntegerOption ADVANCE_RELEASE_DURATION; + BooleanCheckBoxOption SAVE_SCREENSHOTS; + BooleanCheckBoxOption LOG_VALUES; + + void move_to_trader(SingleSwitchProgramEnvironment& env, BotBaseContext& context); + uint8_t calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, BotBaseContext& context, Pokemon::Xoroshiro128Plus& rng); + void navigate_to_party(SingleSwitchProgramEnvironment& env, BotBaseContext& context); + size_t calculate_target(SingleSwitchProgramEnvironment& env, Pokemon::Xoroshiro128PlusState state, uint8_t num_npcs, std::vector wanted_highlights); + void leave_to_overworld_and_interact(SingleSwitchProgramEnvironment& env, BotBaseContext& context, bool buy_highlight); + void recover_from_wrong_state(SingleSwitchProgramEnvironment& env, BotBaseContext& context); + void advance_date(SingleSwitchProgramEnvironment& env, BotBaseContext& context, uint8_t& year); +}; + + + + +} +} +} +#endif + + + diff --git a/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.cpp b/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.cpp new file mode 100644 index 0000000000..bed61cca1b --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.cpp @@ -0,0 +1,53 @@ +/* Daily Highlight Database + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#include "Common/Cpp/Json/JsonObject.h" +#include "CommonFramework/Globals.h" +#include "CommonFramework/Logging/Logger.h" +#include "PokemonSwSh_DailyHighlightDatabase.h" + +namespace PokemonAutomation { +namespace NintendoSwitch { +namespace PokemonSwSh { + + + +DailyHighlightDatabase::DailyHighlightDatabase(const char* resource_path) { + std::string filepath = RESOURCE_PATH() + resource_path; + JsonValue json = load_json_file(filepath); + JsonObject& root = json.to_object_throw(filepath); + + for (auto& item : root) { + const std::string& slug = item.first; + JsonObject& obj = item.second.to_object_throw(filepath); + + uint16_t min = (uint16_t)obj.get_integer_throw("min", filepath); + uint16_t max = (uint16_t)obj.get_integer_throw("max", filepath); + m_slug_to_range[slug] = std::pair(min, max); + + std::string display_name = obj.get_string_throw("display_name", filepath); + StringSelectEntry string_select_entry(slug, display_name); + m_stringselect_database.add_entry(string_select_entry); + } +} + +std::pair DailyHighlightDatabase::get_range_for_slug(const std::string& slug) const { + auto iter = m_slug_to_range.find(slug); + if (iter == m_slug_to_range.end()) { + throw InternalProgramError( + nullptr, + PA_CURRENT_FUNCTION, + std::string("Invalid daily highlight slug: " + slug) + ); + } + return iter->second; +} + + + +} +} +} diff --git a/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h b/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h new file mode 100644 index 0000000000..933498480f --- /dev/null +++ b/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h @@ -0,0 +1,38 @@ +/* Daily Highlight Database + * + * From: https://github.com/PokemonAutomation/Arduino-Source + * + */ + +#ifndef PokemonAutomation_PokemonSwSh_DailyHighlightDatabase_H +#define PokemonAutomation_PokemonSwSh_DailyHighlightDatabase_H + +#include +#include "CommonFramework/Options/StringSelectOption.h" + +namespace PokemonAutomation { +namespace NintendoSwitch { +namespace PokemonSwSh { + + +class DailyHighlightDatabase { +public: + DailyHighlightDatabase(const char* resource_path); + + std::pair get_range_for_slug(const std::string& slug) const; + const StringSelectDatabase& database() const { + return m_stringselect_database; + } + +private: + std::map> m_slug_to_range; + StringSelectDatabase m_stringselect_database; +}; + + + + +} +} +} +#endif From 43b314c23c7073bb6356d9a93097cda07b82e177 Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Fri, 7 Feb 2025 17:29:49 +0100 Subject: [PATCH 02/11] check if NPC was reached --- .../Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp index 32b2acd15a..cf589b9603 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -17,6 +17,7 @@ #include "Pokemon/Pokemon_Strings.h" #include "PokemonSwSh/PokemonSwSh_Settings.h" #include "PokemonSwSh/Commands/PokemonSwSh_Commands_DateSpam.h" +#include "PokemonSwSh/Inference/PokemonSwSh_DialogTriangleDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_SelectionArrowFinder.h" #include "PokemonSwSh/Programs/PokemonSwSh_MenuNavigation.h" #include "PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h" @@ -138,7 +139,18 @@ DailyHighlightRNG::DailyHighlightRNG() void DailyHighlightRNG::move_to_trader(SingleSwitchProgramEnvironment& env, BotBaseContext& context) { pbf_move_left_joystick(context, 207, 1, 160, 10); // Magic numbers to barely reach the trader pbf_press_button(context, BUTTON_A, 20, 20); - // TODO: check if NPC was reached -> DialogArrow + + //Check if NPC was reached + DialogTriangleDetector dialog_detector(env.console, ImageFloatBox(0.465, 0.195, 0.054, 0.57)); + dialog_detector.make_overlays(boxes); + + int ret = wait_until(env.console, context, Milliseconds(3000), { dialog_detector }); + if (ret < 0) { + OperationFailedException::fire( + env.console, ErrorReport::SEND_ERROR_REPORT, + "Failed to talk to the trader." + ); + } } void DailyHighlightRNG::navigate_to_party(SingleSwitchProgramEnvironment& env, BotBaseContext& context){ From 3424527474e9a539080857611082b3bf33ccc0c5 Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Mon, 12 May 2025 17:21:43 +0200 Subject: [PATCH 03/11] Update to new controller changes --- .../RNG/PokemonSwSh_DailyHighlightRNG.cpp | 94 +++++++++++-------- .../RNG/PokemonSwSh_DailyHighlightRNG.h | 29 +++--- .../PokemonSwSh_DailyHighlightDatabase.h | 2 +- 3 files changed, 76 insertions(+), 49 deletions(-) diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp index cf589b9603..050bc6c832 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -2,7 +2,7 @@ * * From: https://github.com/PokemonAutomation/Arduino-Source * - * Based on Anubis' findings: https://docs.google.com/spreadsheets/u/0/d/1pNYtCJKRh_efX9LvzjCiA-0n2lGSFnVmSWwmPzgSOMw/htmlview + * Based on Anubis' findings: https://docs.google.com/spreadsheets/u/0/d/1pNYtCJKRh_efX9LvzjCiA-0n2lGSFnVmSWwmPzgSOMw * */ @@ -11,13 +11,15 @@ #include "CommonFramework/Exceptions/ProgramFinishedException.h" #include "CommonFramework/Exceptions/OperationFailedException.h" #include "CommonFramework/Notifications/ProgramNotifications.h" +#include "CommonFramework/ProgramStats/StatsTracking.h" #include "CommonFramework/VideoPipeline/VideoFeed.h" -#include "CommonFramework/Tools/StatsTracking.h" +#include "CommonTools/Async/InferenceRoutines.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" +#include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" #include "Pokemon/Pokemon_Strings.h" #include "PokemonSwSh/PokemonSwSh_Settings.h" #include "PokemonSwSh/Commands/PokemonSwSh_Commands_DateSpam.h" -#include "PokemonSwSh/Inference/PokemonSwSh_DialogTriangleDetector.h" +#include "PokemonSwSh/Inference/PokemonSwSh_DialogBoxDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_SelectionArrowFinder.h" #include "PokemonSwSh/Programs/PokemonSwSh_MenuNavigation.h" #include "PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h" @@ -44,7 +46,8 @@ DailyHighlightRNG_Descriptor::DailyHighlightRNG_Descriptor() "Perform RNG manipulation to get rare items from the daily highlight trader.", FeedbackType::REQUIRED, AllowCommandsWhenRunning::DISABLE_COMMANDS, - PABotBaseLevel::PABOTBASE_12KB + {ControllerFeature::NintendoSwitch_ProController}, + FasterIfTickPrecise::MUCH_FASTER ) {} @@ -94,6 +97,11 @@ DailyHighlightRNG::DailyHighlightRNG() , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) + , MOVE_TIME("Move time:", LockMode::LOCK_WHILE_RUNNING, "1280 ms") + , LEFT_X("Left X:", LockMode::LOCK_WHILE_RUNNING, 207) + , LEFT_Y("Left Y:", LockMode::LOCK_WHILE_RUNNING, 1) + , RIGHT_X("Right X:", LockMode::LOCK_WHILE_RUNNING, 127) + , RIGHT_Y("Right Y:", LockMode::LOCK_WHILE_RUNNING, 127) , MAX_UNKNOWN_ADVANCES( "Max Unknown advances:
How many advances to check when updating the rng state.", LockMode::LOCK_WHILE_RUNNING, @@ -102,12 +110,12 @@ DailyHighlightRNG::DailyHighlightRNG() , ADVANCE_PRESS_DURATION( "Advance Press Duration:
Hold the button down for this long to advance once.", LockMode::LOCK_WHILE_RUNNING, - 10 + "80 ms" ) , ADVANCE_RELEASE_DURATION( "Advance Release Duration:
After releasing the button, wait this long before pressing it again.", LockMode::LOCK_WHILE_RUNNING, - 10 + "80 ms" ) , SAVE_SCREENSHOTS( "Save Debug Screenshots:", @@ -129,6 +137,11 @@ DailyHighlightRNG::DailyHighlightRNG() PA_ADD_OPTION(NOTIFICATIONS); PA_ADD_STATIC(m_advanced_options); + PA_ADD_OPTION(MOVE_TIME); + PA_ADD_OPTION(LEFT_X); + PA_ADD_OPTION(LEFT_Y); + PA_ADD_OPTION(RIGHT_X); + PA_ADD_OPTION(RIGHT_Y); PA_ADD_OPTION(MAX_UNKNOWN_ADVANCES); PA_ADD_OPTION(ADVANCE_PRESS_DURATION); PA_ADD_OPTION(ADVANCE_RELEASE_DURATION); @@ -136,32 +149,38 @@ DailyHighlightRNG::DailyHighlightRNG() PA_ADD_OPTION(LOG_VALUES); } -void DailyHighlightRNG::move_to_trader(SingleSwitchProgramEnvironment& env, BotBaseContext& context) { - pbf_move_left_joystick(context, 207, 1, 160, 10); // Magic numbers to barely reach the trader - pbf_press_button(context, BUTTON_A, 20, 20); +void DailyHighlightRNG::move_to_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + pbf_move_right_joystick(context, LEFT_X, LEFT_Y, MOVE_TIME, Milliseconds(80)); // TODO: remove/combine with left stick + pbf_move_left_joystick(context, RIGHT_X, RIGHT_Y, MOVE_TIME, Milliseconds(80)); + + + pbf_press_button(context, BUTTON_A, Milliseconds(160), Milliseconds(160)); //Check if NPC was reached - DialogTriangleDetector dialog_detector(env.console, ImageFloatBox(0.465, 0.195, 0.054, 0.57)); + //DialogTriangleDetector dialog_detector(env.console, ImageFloatBox(0.465, 0.195, 0.054, 0.57)); + VideoOverlaySet boxes(env.console); + BlackDialogBoxDetector dialog_detector(true); dialog_detector.make_overlays(boxes); int ret = wait_until(env.console, context, Milliseconds(3000), { dialog_detector }); if (ret < 0) { OperationFailedException::fire( - env.console, ErrorReport::SEND_ERROR_REPORT, - "Failed to talk to the trader." + ErrorReport::SEND_ERROR_REPORT, + "Failed to talk to the trader.", + env.console ); } } -void DailyHighlightRNG::navigate_to_party(SingleSwitchProgramEnvironment& env, BotBaseContext& context){ - pbf_mash_button(context, BUTTON_B, 2 * TICKS_PER_SECOND); // exit dialog - pbf_press_button(context, BUTTON_X, 10, GameSettings::instance().OVERWORLD_TO_MENU_DELAY); +void DailyHighlightRNG::navigate_to_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ + pbf_mash_button(context, BUTTON_B, Milliseconds(2000)); // exit dialog + pbf_press_button(context, BUTTON_X, Milliseconds(80), GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); navigate_to_menu_app(env, env.console, context, 1, NOTIFICATION_ERROR_RECOVERABLE); // TODO: try-catch-block + error recovery - pbf_press_button(context, BUTTON_A, 10, 3*TICKS_PER_SECOND); + pbf_press_button(context, BUTTON_A, Milliseconds(80), Milliseconds(3000)); context.wait_for_all_requests(); } -uint8_t DailyHighlightRNG::calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, BotBaseContext& context, Pokemon::Xoroshiro128Plus& rng){ +uint8_t DailyHighlightRNG::calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context, Pokemon::Xoroshiro128Plus& rng){ // TODO: implement return 2; // Usually either 1 or 2 -> higher numbers suggest bad npc state } @@ -204,50 +223,50 @@ size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, return advances; } -void DailyHighlightRNG::leave_to_overworld_and_interact(SingleSwitchProgramEnvironment& env, BotBaseContext& context, bool buy_highlight) { +void DailyHighlightRNG::leave_to_overworld_and_interact(SingleSwitchProgramEnvironment& env, ProControllerContext& context, bool buy_highlight) { // Close menu - pbf_press_button(context, BUTTON_B, 2 * TICKS_PER_SECOND, 5); - pbf_press_button(context, BUTTON_B, 10, 70); + pbf_press_button(context, BUTTON_B, Milliseconds(2000), Milliseconds(40)); + pbf_press_button(context, BUTTON_B, Milliseconds(80), Milliseconds(560)); // Quickly interact - pbf_press_button(context, BUTTON_A, 30, 30); - pbf_wait(context, 2*TICKS_PER_SECOND); + pbf_press_button(context, BUTTON_A, Milliseconds(240), Milliseconds(240)); + pbf_wait(context, Milliseconds(2000)); // TODO: check if interaction worked -> see move_to_trader() // Buy highlight if (buy_highlight) { - pbf_press_button(context, BUTTON_ZL, 10, 40); - pbf_press_dpad(context, DPAD_DOWN, 10, 10); - pbf_mash_button(context, BUTTON_ZL, 400); + pbf_press_button(context, BUTTON_ZL, Milliseconds(80), Milliseconds(360)); + pbf_press_dpad(context, DPAD_DOWN, Milliseconds(80), Milliseconds(80)); + pbf_mash_button(context, BUTTON_ZL, Milliseconds(3200)); } // Leave dialog - pbf_mash_button(context, BUTTON_B, 6 * TICKS_PER_SECOND); + pbf_mash_button(context, BUTTON_B, Milliseconds(6000)); } -void DailyHighlightRNG::recover_from_wrong_state(SingleSwitchProgramEnvironment& env, BotBaseContext& context) { +void DailyHighlightRNG::recover_from_wrong_state(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { // Mash the B button to exit potential menus or dialog boxes - pbf_mash_button(context, BUTTON_B, 30 * TICKS_PER_SECOND); + pbf_mash_button(context, BUTTON_B, Seconds(30)); // Open map - pbf_press_button(context, BUTTON_X, 20, GameSettings::instance().OVERWORLD_TO_MENU_DELAY); + pbf_press_button(context, BUTTON_X, Milliseconds(160), GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); navigate_to_menu_app(env, env.console, context, 5, NOTIFICATION_ERROR_RECOVERABLE); // Fly to Snowslide Slope - pbf_move_left_joystick(context, 200, 210, 20, 20); - pbf_mash_button(context, BUTTON_A, 1 * TICKS_PER_SECOND); + pbf_move_left_joystick(context, 200, 210, Milliseconds(160), Milliseconds(160)); + pbf_mash_button(context, BUTTON_A, Milliseconds(1000)); } -void DailyHighlightRNG::advance_date(SingleSwitchProgramEnvironment& env, BotBaseContext& context, uint8_t& year) { - pbf_press_button(context, BUTTON_HOME, 10, GameSettings::instance().GAME_TO_HOME_DELAY_FAST); +void DailyHighlightRNG::advance_date(SingleSwitchProgramEnvironment& env, ProControllerContext& context, uint8_t& year) { + pbf_press_button(context, BUTTON_HOME, Milliseconds(80), GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); home_roll_date_enter_game_autorollback(env.console, context, year); //resume_game_from_home(env.console, context, false); } -void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, BotBaseContext& context){ +void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ DailyHighlightRNG_Descriptor::Stats& stats = env.current_stats(); env.update_stats(); @@ -260,7 +279,7 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, BotBaseCont grip_menu_connect_go_home(context); } else { - pbf_press_button(context, BUTTON_B, 5, 5); + pbf_press_button(context, BUTTON_B, Milliseconds(40), Milliseconds(40)); } Xoroshiro128Plus rng(0, 0); @@ -303,8 +322,9 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, BotBaseCont state_errors++; if (state_errors >= 3){ OperationFailedException::fire( - env.console, ErrorReport::SEND_ERROR_REPORT, - "Detected invalid RNG state three times in a row." + ErrorReport::SEND_ERROR_REPORT, + "Detected invalid RNG state three times in a row.", + env.console ); } VideoSnapshot screen = env.console.video().snapshot(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h index 72df3c30ce..2341add60a 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h @@ -12,9 +12,10 @@ #include "Common/Cpp/Options/BooleanCheckBoxOption.h" #include "Common/Cpp/Options/SimpleIntegerOption.h" #include "Common/Cpp/Options/StaticTextOption.h" +#include "Common/Cpp/Options/TimeDurationOption.h" #include "CommonFramework/Notifications/EventNotificationsTable.h" -#include "CommonFramework/Options/LanguageOCROption.h" -#include "CommonFramework/Options/StringSelectTableOption.h" +#include "CommonTools/Options/LanguageOCROption.h" +#include "CommonTools/Options/StringSelectTableOption.h" #include "NintendoSwitch/Options/NintendoSwitch_StartInGripMenuOption.h" #include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" #include "Pokemon/Pokemon_Xoroshiro128Plus.h" @@ -38,7 +39,7 @@ class DailyHighlightRNG : public SingleSwitchProgramInstance{ public: DailyHighlightRNG(); - virtual void program(SingleSwitchProgramEnvironment& env, BotBaseContext& context) override; + virtual void program(SingleSwitchProgramEnvironment& env, ProControllerContext& context) override; private: StartInGripOrGameOption START_LOCATION; @@ -50,19 +51,25 @@ class DailyHighlightRNG : public SingleSwitchProgramInstance{ EventNotificationsOption NOTIFICATIONS; SectionDividerOption m_advanced_options; + MillisecondsOption MOVE_TIME; + SimpleIntegerOption LEFT_X; + SimpleIntegerOption LEFT_Y; + SimpleIntegerOption RIGHT_X; + SimpleIntegerOption RIGHT_Y; + SimpleIntegerOption MAX_UNKNOWN_ADVANCES; - SimpleIntegerOption ADVANCE_PRESS_DURATION; - SimpleIntegerOption ADVANCE_RELEASE_DURATION; + MillisecondsOption ADVANCE_PRESS_DURATION; + MillisecondsOption ADVANCE_RELEASE_DURATION; BooleanCheckBoxOption SAVE_SCREENSHOTS; BooleanCheckBoxOption LOG_VALUES; - void move_to_trader(SingleSwitchProgramEnvironment& env, BotBaseContext& context); - uint8_t calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, BotBaseContext& context, Pokemon::Xoroshiro128Plus& rng); - void navigate_to_party(SingleSwitchProgramEnvironment& env, BotBaseContext& context); + void move_to_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + uint8_t calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context, Pokemon::Xoroshiro128Plus& rng); + void navigate_to_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context); size_t calculate_target(SingleSwitchProgramEnvironment& env, Pokemon::Xoroshiro128PlusState state, uint8_t num_npcs, std::vector wanted_highlights); - void leave_to_overworld_and_interact(SingleSwitchProgramEnvironment& env, BotBaseContext& context, bool buy_highlight); - void recover_from_wrong_state(SingleSwitchProgramEnvironment& env, BotBaseContext& context); - void advance_date(SingleSwitchProgramEnvironment& env, BotBaseContext& context, uint8_t& year); + void leave_to_overworld_and_interact(SingleSwitchProgramEnvironment& env, ProControllerContext& context, bool buy_highlight); + void recover_from_wrong_state(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void advance_date(SingleSwitchProgramEnvironment& env, ProControllerContext& context, uint8_t& year); }; diff --git a/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h b/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h index 933498480f..5c9052744b 100644 --- a/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h +++ b/SerialPrograms/Source/PokemonSwSh/Resources/PokemonSwSh_DailyHighlightDatabase.h @@ -8,7 +8,7 @@ #define PokemonAutomation_PokemonSwSh_DailyHighlightDatabase_H #include -#include "CommonFramework/Options/StringSelectOption.h" +#include "CommonTools/Options/StringSelectOption.h" namespace PokemonAutomation { namespace NintendoSwitch { From 360b67c9057c0cd5c7d921d9c6f29769a138f919 Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Thu, 15 May 2025 20:23:01 +0200 Subject: [PATCH 04/11] working highlight RNG --- .../Pokemon/Pokemon_Xoroshiro128Plus.cpp | 16 + .../Source/Pokemon/Pokemon_Xoroshiro128Plus.h | 7 + .../Programs/RNG/PokemonSwSh_BasicRNG.cpp | 15 +- .../Programs/RNG/PokemonSwSh_BasicRNG.h | 16 + .../RNG/PokemonSwSh_DailyHighlightRNG.cpp | 334 +++++++++++++----- .../RNG/PokemonSwSh_DailyHighlightRNG.h | 12 +- 6 files changed, 313 insertions(+), 87 deletions(-) diff --git a/SerialPrograms/Source/Pokemon/Pokemon_Xoroshiro128Plus.cpp b/SerialPrograms/Source/Pokemon/Pokemon_Xoroshiro128Plus.cpp index 9581f508d0..f0cd6c0bcc 100644 --- a/SerialPrograms/Source/Pokemon/Pokemon_Xoroshiro128Plus.cpp +++ b/SerialPrograms/Source/Pokemon/Pokemon_Xoroshiro128Plus.cpp @@ -77,6 +77,22 @@ std::vector Xoroshiro128Plus::generate_last_bit_sequence(size_t max_advanc return sequence; } + +std::pair Xoroshiro128Plus::advances_to_state(Xoroshiro128PlusState other_state, uint64_t max_advances) { + Xoroshiro128Plus temp_rng(get_state()); + uint64_t advances = 0; + + while (advances <= max_advances) { + Xoroshiro128PlusState temp_state = temp_rng.get_state(); + if (temp_state.s0 == other_state.s0 && temp_state.s1 == other_state.s1) { + return { true, advances }; + } + temp_rng.next(); + advances++; + } + return { false, advances }; +} + // The generic solution to the system of equations to calculate the initial state from the last bits of 128 consecutive Xoroshiro128+ results. uint64_t Xoroshiro128Plus::last_bits_reverse_matrix[128][2] = { /*s0 bit 0*/ {0b0101001100100001111011111110111001010011111110101011100011001101, 0b0111010111110111000101010100001111101001111001011111001011010111} , diff --git a/SerialPrograms/Source/Pokemon/Pokemon_Xoroshiro128Plus.h b/SerialPrograms/Source/Pokemon/Pokemon_Xoroshiro128Plus.h index 66f353f2ac..c7b1116d4d 100644 --- a/SerialPrograms/Source/Pokemon/Pokemon_Xoroshiro128Plus.h +++ b/SerialPrograms/Source/Pokemon/Pokemon_Xoroshiro128Plus.h @@ -31,6 +31,13 @@ class Xoroshiro128Plus{ Xoroshiro128PlusState get_state(); std::vector generate_last_bit_sequence(size_t max_advances); + // Calculates how many advances are required to reach the given state. + // The given state must be reachable within max_advances advances. + // Returns a pair: + // first: true if the state is reachable within max_advances, false otherwise + // second: the number of advances required (if first is true) + std::pair advances_to_state(Xoroshiro128PlusState other_state, uint64_t max_advances = 100000); + static Xoroshiro128Plus xoroshiro128plus_from_last_bits(std::pair last_bits); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.cpp index 3ff413b45b..80b9a86e36 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.cpp @@ -66,7 +66,6 @@ Xoroshiro128PlusState find_rng_state( return rng.get_state(); } - Xoroshiro128PlusState refind_rng_state( VideoStream& stream, ProControllerContext& context, @@ -75,6 +74,18 @@ Xoroshiro128PlusState refind_rng_state( size_t max_advances, bool save_screenshots, bool log_image_values +) { + return refind_rng_state_and_animations(stream, context, last_known_state, min_advances, max_advances, save_screenshots, log_image_values).first; +} + +std::pair refind_rng_state_and_animations( + VideoStream& stream, + ProControllerContext& context, + Xoroshiro128PlusState last_known_state, + size_t min_advances, + size_t max_advances, + bool save_screenshots, + bool log_image_values ) { Xoroshiro128Plus rng(last_known_state.s0, last_known_state.s1); @@ -140,7 +151,7 @@ Xoroshiro128PlusState refind_rng_state( stream.log("RNG: state[0] = " + tostr_hex(rng.get_state().s0)); stream.log("RNG: state[1] = " + tostr_hex(rng.get_state().s1)); - return rng.get_state(); + return { rng.get_state(), sequence.size() }; } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h index b6d22bcebe..923b88d84e 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h @@ -8,6 +8,8 @@ #include "NintendoSwitch/Controllers/NintendoSwitch_ProController.h" #include "Pokemon/Pokemon_Xoroshiro128Plus.h" +#include + namespace PokemonAutomation{ namespace NintendoSwitch{ namespace PokemonSwSh{ @@ -33,6 +35,20 @@ Xoroshiro128PlusState refind_rng_state( bool log_values ); +// Performs Orbeetle attack animations until only one possible state is left. +// Returns a pair: +// first: current RNG state +// second: the number of animations required to find the state +std::pair refind_rng_state_and_animations( + VideoStream& stream, + ProControllerContext& context, + Xoroshiro128PlusState last_known_state, + size_t min_advances, + size_t max_advances, + bool save_screenshots, + bool log_values +); + void do_rng_advances( VideoStream& stream, ProControllerContext& context, Xoroshiro128Plus& rng, diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp index 050bc6c832..b2d7eaad51 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -8,6 +8,7 @@ #include #include +#include "Common/Cpp/PrettyPrint.h" #include "CommonFramework/Exceptions/ProgramFinishedException.h" #include "CommonFramework/Exceptions/OperationFailedException.h" #include "CommonFramework/Notifications/ProgramNotifications.h" @@ -15,12 +16,16 @@ #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Async/InferenceRoutines.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" -#include "NintendoSwitch/Commands/NintendoSwitch_Commands_Superscalar.h" +#include "NintendoSwitch/Inference/NintendoSwitch_DateReader.h" +#include "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h" +#include "NintendoSwitch/Programs/NintendoSwitch_Navigation.h" +#include "NintendoSwitch/NintendoSwitch_Settings.h" #include "Pokemon/Pokemon_Strings.h" #include "PokemonSwSh/PokemonSwSh_Settings.h" #include "PokemonSwSh/Commands/PokemonSwSh_Commands_DateSpam.h" #include "PokemonSwSh/Inference/PokemonSwSh_DialogBoxDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_SelectionArrowFinder.h" +#include "PokemonSwSh/Inference/PokemonSwSh_YCommDetector.h" #include "PokemonSwSh/Programs/PokemonSwSh_MenuNavigation.h" #include "PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h" #include "PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h" @@ -77,11 +82,20 @@ std::unique_ptr DailyHighlightRNG_Descriptor::make_stats() const{ DailyHighlightRNG::DailyHighlightRNG() : NUM_HIGHLIGHTS( - "Number of highlights:
How many daily highlights should be bought. A value of 0 will run until you run out of Watts.", + "Number of highlights:
How many daily highlights should be bought.
A value of 0 will run until you stop the program.", LockMode::UNLOCK_WHILE_RUNNING, 0) , CONTINUE( "Continue from last time:
If the initial two daily highlights are already manipulated and should be bought.", - LockMode::LOCK_WHILE_RUNNING, false) + LockMode::UNLOCK_WHILE_RUNNING, false) + , FIX_TIME_WHEN_DONE( + "Fix Time when Done:
Fix the time after the program finishes.
Doesn't do anything if Number of highlights is 0.", + LockMode::UNLOCK_WHILE_RUNNING, false) + , GO_HOME_WHEN_DONE(false) + , SAVE_ITERATIONS( + "Save Every this Many Day Skips:
(zero disables saving): ", + LockMode::LOCK_WHILE_RUNNING, + 20 + ) , HIGHLIGHT_SELECTION( "Desired Highlights:", "Highlight", @@ -98,6 +112,7 @@ DailyHighlightRNG::DailyHighlightRNG() "Advanced Options: You should not need to touch anything below here." ) , MOVE_TIME("Move time:", LockMode::LOCK_WHILE_RUNNING, "1280 ms") + , MOVE_TIME2("Move time right:", LockMode::LOCK_WHILE_RUNNING, "1280 ms") , LEFT_X("Left X:", LockMode::LOCK_WHILE_RUNNING, 207) , LEFT_Y("Left Y:", LockMode::LOCK_WHILE_RUNNING, 1) , RIGHT_X("Right X:", LockMode::LOCK_WHILE_RUNNING, 127) @@ -132,12 +147,16 @@ DailyHighlightRNG::DailyHighlightRNG() PA_ADD_OPTION(NUM_HIGHLIGHTS); PA_ADD_OPTION(CONTINUE); + PA_ADD_OPTION(FIX_TIME_WHEN_DONE); + PA_ADD_OPTION(GO_HOME_WHEN_DONE); + PA_ADD_OPTION(SAVE_ITERATIONS); PA_ADD_OPTION(HIGHLIGHT_SELECTION); PA_ADD_OPTION(NOTIFICATIONS); PA_ADD_STATIC(m_advanced_options); PA_ADD_OPTION(MOVE_TIME); + PA_ADD_OPTION(MOVE_TIME2); PA_ADD_OPTION(LEFT_X); PA_ADD_OPTION(LEFT_Y); PA_ADD_OPTION(RIGHT_X); @@ -150,19 +169,21 @@ DailyHighlightRNG::DailyHighlightRNG() } void DailyHighlightRNG::move_to_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { - pbf_move_right_joystick(context, LEFT_X, LEFT_Y, MOVE_TIME, Milliseconds(80)); // TODO: remove/combine with left stick - pbf_move_left_joystick(context, RIGHT_X, RIGHT_Y, MOVE_TIME, Milliseconds(80)); - + pbf_move_right_joystick(context, 255, 128, 500ms, 80ms); + pbf_move_left_joystick(context, 128, 0, 1280ms, 80ms); +} - pbf_press_button(context, BUTTON_A, Milliseconds(160), Milliseconds(160)); - - //Check if NPC was reached - //DialogTriangleDetector dialog_detector(env.console, ImageFloatBox(0.465, 0.195, 0.054, 0.57)); +void DailyHighlightRNG::interact_with_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + pbf_press_button(context, BUTTON_A, 160ms, 600ms); + pbf_press_button(context, BUTTON_A, 160ms, 160ms); + + //Check if talking to the NPC was successfull VideoOverlaySet boxes(env.console); - BlackDialogBoxDetector dialog_detector(true); - dialog_detector.make_overlays(boxes); + SelectionArrowFinder arrow_detector(env.console, ImageFloatBox(0.5, 0.58, 0.2, 0.08)); + arrow_detector.make_overlays(boxes); - int ret = wait_until(env.console, context, Milliseconds(3000), { dialog_detector }); + context.wait_for_all_requests(); + int ret = wait_until(env.console, context, 3000ms, { arrow_detector }); if (ret < 0) { OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, @@ -172,20 +193,114 @@ void DailyHighlightRNG::move_to_trader(SingleSwitchProgramEnvironment& env, ProC } } -void DailyHighlightRNG::navigate_to_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context){ - pbf_mash_button(context, BUTTON_B, Milliseconds(2000)); // exit dialog - pbf_press_button(context, BUTTON_X, Milliseconds(80), GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); - navigate_to_menu_app(env, env.console, context, 1, NOTIFICATION_ERROR_RECOVERABLE); // TODO: try-catch-block + error recovery - pbf_press_button(context, BUTTON_A, Milliseconds(80), Milliseconds(3000)); +void DailyHighlightRNG::buy_highlight(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + env.log("Buying Highlight."); + env.console.overlay().add_log("Buying Highlight!", COLOR_WHITE); + pbf_press_dpad(context, DPAD_DOWN, 80ms, 80ms); + + pbf_press_button(context, BUTTON_A, 160ms, 160ms); + context.wait_for_all_requests(); + + VideoOverlaySet boxes(env.console); + SelectionArrowFinder arrow_detector(env.console, ImageFloatBox(0.5, 0.58, 0.2, 0.08)); + arrow_detector.make_overlays(boxes); + + int ret = run_until( + env.console, + context, + [](ProControllerContext& context) { + pbf_mash_button(context, BUTTON_A, 20000ms); + }, + { {arrow_detector} } + ); + + if (ret < 0) { + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Could not detect dialog.", + env.console + ); + } +} + +void DailyHighlightRNG::navigate_to_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + pbf_press_button(context, BUTTON_X, 80ms, GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); + navigate_to_menu_app(env, env.console, context, 1, NOTIFICATION_ERROR_FATAL); + pbf_press_button(context, BUTTON_A, 80ms, 3000ms); context.wait_for_all_requests(); } -uint8_t DailyHighlightRNG::calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context, Pokemon::Xoroshiro128Plus& rng){ - // TODO: implement - return 2; // Usually either 1 or 2 -> higher numbers suggest bad npc state +void DailyHighlightRNG::save_game(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + context.wait_for_all_requests(); + env.log("Saving."); + env.console.overlay().add_log("Saving!", COLOR_WHITE); + pbf_press_button(context, BUTTON_X, 80ms, GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); + pbf_press_button(context, BUTTON_R, 80ms, 2000ms); + pbf_mash_button(context, BUTTON_A, 500ms); + + return_to_overworld(env, context); +} + +uint8_t DailyHighlightRNG::calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context, Pokemon::Xoroshiro128Plus& rng) { + return_to_overworld(env, context); + navigate_to_party(env, context); + + env.log("Calibrating NPC amount."); + env.console.overlay().add_log("Calibrating NPC amount.", COLOR_WHITE); + + env.current_stats().reads++; + std::pair result = refind_rng_state_and_animations(env.console, context, rng.get_state(), 0, 100, SAVE_SCREENSHOTS, LOG_VALUES); + Xoroshiro128PlusState new_state = result.first; + uint64_t additional_advances = result.second; + + // Calculate state for possible NPC amounts + const uint8_t MAX_NPCS = 4; // Usually either 1 or 2, maybe(?) 3 or 4 -> high numbers suggest bad npc state + std::vector rng_states; + + for (size_t i = 0; i <= MAX_NPCS; i++) { + Xoroshiro128Plus temp_rng(rng.get_state()); + + for (size_t j = 0; j < i; j++) { + temp_rng.nextInt(91); + } + temp_rng.next(); + temp_rng.nextInt(61); + + // Do advances that were needed to find current state + for (size_t j = 0; j < additional_advances; j++) { + temp_rng.next(); + } + + env.console.log(std::to_string(i)); + env.console.log(std::to_string(additional_advances)); + env.console.log(tostr_hex(temp_rng.get_state().s0)); + env.console.log(tostr_hex(temp_rng.get_state().s1)); + + rng_states.push_back(temp_rng.get_state()); + } + + // Compare current state to expected states + uint8_t num_npcs = 0; + for (uint8_t i = 1; i <= MAX_NPCS; i++) { + if (rng_states[i].s0 == new_state.s0 && rng_states[i].s1 == new_state.s1) { + rng.state = new_state; + num_npcs = i; + } + } + if (num_npcs == 0) { + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "NPC is in the wrong state or an unexpected amount of NPCs is in the area.", + env.console + ); + } + + env.console.log("Calculated there are " + std::to_string(num_npcs) + "NPCs."); + env.console.overlay().add_log(std::to_string(num_npcs) + " NPCs", COLOR_WHITE); + return num_npcs; } -size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, Xoroshiro128PlusState state, uint8_t num_npcs, std::vector wanted_highlights){ +size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, Xoroshiro128PlusState state, uint8_t num_npcs, std::vector wanted_highlights) { Xoroshiro128Plus rng(state); size_t advances = 0; bool found_advance_amount = false; @@ -196,21 +311,20 @@ size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, } while (!found_advance_amount) { - // calculate the result for the current temp_rng state - Xoroshiro128Plus temp_rng(rng.get_state()); + // Calculate the result for the current temp_rng state + Xoroshiro128Plus temp_rng(rng.get_state()); for (size_t i = 0; i < num_npcs; i++) { temp_rng.nextInt(91); } temp_rng.next(); - temp_rng.nextInt(60); + temp_rng.nextInt(61); uint64_t highlight_roll = temp_rng.nextInt(1000); for (auto& range : ranges) { if (range.first < highlight_roll && range.second > highlight_roll) { found_advance_amount = true; - // TODO: check if affordable, remove from vector if not. If vector empty: throw Exception } } @@ -223,46 +337,70 @@ size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, return advances; } -void DailyHighlightRNG::leave_to_overworld_and_interact(SingleSwitchProgramEnvironment& env, ProControllerContext& context, bool buy_highlight) { - // Close menu - pbf_press_button(context, BUTTON_B, Milliseconds(2000), Milliseconds(40)); - pbf_press_button(context, BUTTON_B, Milliseconds(80), Milliseconds(560)); - - // Quickly interact - pbf_press_button(context, BUTTON_A, Milliseconds(240), Milliseconds(240)); - pbf_wait(context, Milliseconds(2000)); - - // TODO: check if interaction worked -> see move_to_trader() - - // Buy highlight - if (buy_highlight) { - pbf_press_button(context, BUTTON_ZL, Milliseconds(80), Milliseconds(360)); - pbf_press_dpad(context, DPAD_DOWN, Milliseconds(80), Milliseconds(80)); - pbf_mash_button(context, BUTTON_ZL, Milliseconds(3200)); - } - - // Leave dialog - pbf_mash_button(context, BUTTON_B, Milliseconds(6000)); -} +void DailyHighlightRNG::prepare_game_state(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + env.log("Prepare player position."); + env.console.overlay().add_log("Reset Player Position.", COLOR_WHITE); -void DailyHighlightRNG::recover_from_wrong_state(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { - // Mash the B button to exit potential menus or dialog boxes - pbf_mash_button(context, BUTTON_B, Seconds(30)); + // Return to overworld + return_to_overworld(env, context); // Open map - pbf_press_button(context, BUTTON_X, Milliseconds(160), GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); + pbf_press_button(context, BUTTON_X, 160ms, GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); navigate_to_menu_app(env, env.console, context, 5, NOTIFICATION_ERROR_RECOVERABLE); + pbf_press_button(context, BUTTON_A, 160ms, 4000ms); // Fly to Snowslide Slope - pbf_move_left_joystick(context, 200, 210, Milliseconds(160), Milliseconds(160)); - pbf_mash_button(context, BUTTON_A, Milliseconds(1000)); + pbf_move_left_joystick(context, 200, 210, 160ms, 160ms); + pbf_mash_button(context, BUTTON_A, 1000ms); + return_to_overworld(env, context); +} + +void DailyHighlightRNG::return_to_overworld(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + env.console.log("Returning to the overworld."); + YCommIconDetector y_comm_icon_detector(true); + int ret = run_until( + env.console, context, + [](ProControllerContext& context) { + pbf_mash_button(context, BUTTON_B, 20000ms); + }, + { {y_comm_icon_detector} } + ); + if (ret != 0) { + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Cannot detect the Y-Comm icon.", + env.console + ); + } } void DailyHighlightRNG::advance_date(SingleSwitchProgramEnvironment& env, ProControllerContext& context, uint8_t& year) { - pbf_press_button(context, BUTTON_HOME, Milliseconds(80), GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); - home_roll_date_enter_game_autorollback(env.console, context, year); - //resume_game_from_home(env.console, context, false); + pbf_press_button(context, BUTTON_HOME, 80ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); + home_to_date_time(context, true, false); + pbf_press_button(context, BUTTON_A, 160ms, 240ms); + context.wait_for_all_requests(); + + VideoOverlaySet overlays(env.console.overlay()); + DateChangeWatcher date_reader; + date_reader.make_overlays(overlays); + + DateTime date{ 2000, 10, 31, 1, 0, 0 }; // 31st October for fixed Overcast weather + + if (year >= MAX_YEAR) { + date_reader.set_date(env.program_info(), env.console, context, date); + pbf_press_button(context, BUTTON_A, 160ms, 240ms); + pbf_press_button(context, BUTTON_A, 160ms, 240ms); + year = 1; + } + + date.year += year; + date_reader.set_date(env.program_info(), env.console, context, date); + pbf_press_button(context, BUTTON_A, 160ms, 240ms); + + pbf_press_button(context, BUTTON_HOME, 160ms, ConsoleSettings::instance().SETTINGS_TO_HOME_DELAY0); + resume_game_from_home(env.console, context, false); + year++; } @@ -279,37 +417,54 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll grip_menu_connect_go_home(context); } else { - pbf_press_button(context, BUTTON_B, Milliseconds(40), Milliseconds(40)); + pbf_press_button(context, BUTTON_B, 40ms, 40ms); } Xoroshiro128Plus rng(0, 0); bool is_state_valid = false; size_t iteration = 0; - size_t bought_highlights = 0; // = CONTINUE ? iteration : max(0, iteration - 2); + size_t successfull_iterations = 0; + size_t bought_highlights = 0; uint8_t year = MAX_YEAR; uint16_t state_errors = 0; - // TODO: save from time to time - move_to_trader(env, context); - while (bought_highlights < NUM_HIGHLIGHTS || NUM_HIGHLIGHTS <= 0){ iteration++; stats.iterations++; env.update_stats(); send_program_status_notification(env, NOTIFICATION_STATUS_UPDATE); env.console.log("Daily Highlight RNG iteration: " + std::to_string(iteration)); - + env.console.overlay().add_log("Iteration: " + std::to_string(iteration), COLOR_WHITE); + + if (SAVE_ITERATIONS > 0 && iteration % SAVE_ITERATIONS == 0) { + save_game(env, context); + } + + return_to_overworld(env, context); advance_date(env, context, year); - navigate_to_party(env, context); - context.wait_for_all_requests(); - // Find RNG state - if (!is_state_valid){ + if (!is_state_valid) { + successfull_iterations = 0; + prepare_game_state(env, context); + pbf_press_button(context, BUTTON_X, 160ms, GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); + return_to_overworld(env, context); + move_to_trader(env, context); + interact_with_trader(env, context); + return_to_overworld(env, context); + navigate_to_party(env, context); + context.wait_for_all_requests(); + + // Find RNG state + env.log("Finding initial rng state."); + env.console.overlay().add_log("Initial RNG state:", COLOR_WHITE); rng = Xoroshiro128Plus(find_rng_state(env.console, context, SAVE_SCREENSHOTS, LOG_VALUES)); -// rng = Xoroshiro128Plus(100, 10000); - is_state_valid = true; stats.reads++; + is_state_valid = true; + continue; }else{ + env.log("Refinding rng state."); + env.console.overlay().add_log("New RNG state:", COLOR_WHITE); + navigate_to_party(env, context); rng = Xoroshiro128Plus(refind_rng_state(env.console, context, rng.get_state(), 0, MAX_UNKNOWN_ADVANCES, SAVE_SCREENSHOTS, LOG_VALUES)); stats.reads++; } @@ -329,38 +484,51 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll } VideoSnapshot screen = env.console.video().snapshot(); send_program_recoverable_error_notification(env, NOTIFICATION_ERROR_RECOVERABLE, "Detected invalid RNG state.", screen); - recover_from_wrong_state(env, context); is_state_valid = false; continue; } - // Calibrate number of NPCs in the area and check whether Trader is in slow state - uint8_t num_npcs = calibrate_num_npc_from_party(env, context, rng); + // Calibrate number of NPCs in the area and check whether the trader is in the slow state + uint8_t num_npcs; + try { + num_npcs = calibrate_num_npc_from_party(env, context, rng); + } + catch (OperationFailedException& exception) { + send_program_recoverable_error_notification(env, NOTIFICATION_ERROR_RECOVERABLE, exception.message(), exception.screenshot()); + is_state_valid = false; + stats.errors++; + continue; + } - // Do advances + // Do required advances size_t target_advances = calculate_target(env, rng.get_state(), num_npcs, HIGHLIGHT_SELECTION.all_slugs()); env.console.log("Needed advances: " + std::to_string(target_advances)); do_rng_advances(env.console, context, rng, target_advances, ADVANCE_PRESS_DURATION, ADVANCE_RELEASE_DURATION); // Talk to NPC and buy highlight - bool buy_highlight = (iteration >= 3) || CONTINUE; - leave_to_overworld_and_interact(env, context, buy_highlight); - - if (buy_highlight){ + return_to_overworld(env, context); + interact_with_trader(env, context); + if (successfull_iterations >= 2) { + buy_highlight(env, context); bought_highlights++; stats.highlights++; - } - - uint32_t watts = 1000000; // TODO: read screen - uint32_t lowest_cost = 500; // TODO: calculate - // Out of Watts. - if (watts < lowest_cost){ - throw_and_log(env.console, "Cannot buy more daily highlights with the remaining Watts.", env.console); } + return_to_overworld(env, context); env.update_stats(); + successfull_iterations++; state_errors = 0; } + + if (FIX_TIME_WHEN_DONE) { + pbf_press_button(context, BUTTON_HOME, 80ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); + home_to_date_time(context, false, false); + pbf_press_button(context, BUTTON_A, 20, 105); + pbf_press_button(context, BUTTON_A, 20, 105); + pbf_press_button(context, BUTTON_HOME, 160ms, ConsoleSettings::instance().SETTINGS_TO_HOME_DELAY0); + resume_game_from_home(env.console, context); + } + GO_HOME_WHEN_DONE.run_end_of_program(context); send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h index 2341add60a..662808caf5 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h @@ -16,6 +16,7 @@ #include "CommonFramework/Notifications/EventNotificationsTable.h" #include "CommonTools/Options/LanguageOCROption.h" #include "CommonTools/Options/StringSelectTableOption.h" +#include "NintendoSwitch/Options/NintendoSwitch_GoHomeWhenDoneOption.h" #include "NintendoSwitch/Options/NintendoSwitch_StartInGripMenuOption.h" #include "NintendoSwitch/NintendoSwitch_SingleSwitchProgram.h" #include "Pokemon/Pokemon_Xoroshiro128Plus.h" @@ -45,6 +46,9 @@ class DailyHighlightRNG : public SingleSwitchProgramInstance{ StartInGripOrGameOption START_LOCATION; SimpleIntegerOption NUM_HIGHLIGHTS; BooleanCheckBoxOption CONTINUE; + BooleanCheckBoxOption FIX_TIME_WHEN_DONE; + GoHomeWhenDoneOption GO_HOME_WHEN_DONE; + SimpleIntegerOption SAVE_ITERATIONS; StringSelectTableOption HIGHLIGHT_SELECTION; EventNotificationOption NOTIFICATION_STATUS_UPDATE; @@ -52,6 +56,7 @@ class DailyHighlightRNG : public SingleSwitchProgramInstance{ SectionDividerOption m_advanced_options; MillisecondsOption MOVE_TIME; + MillisecondsOption MOVE_TIME2; SimpleIntegerOption LEFT_X; SimpleIntegerOption LEFT_Y; SimpleIntegerOption RIGHT_X; @@ -64,11 +69,14 @@ class DailyHighlightRNG : public SingleSwitchProgramInstance{ BooleanCheckBoxOption LOG_VALUES; void move_to_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void interact_with_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void return_to_overworld(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void buy_highlight(SingleSwitchProgramEnvironment& env, ProControllerContext& context); uint8_t calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context, Pokemon::Xoroshiro128Plus& rng); void navigate_to_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context); size_t calculate_target(SingleSwitchProgramEnvironment& env, Pokemon::Xoroshiro128PlusState state, uint8_t num_npcs, std::vector wanted_highlights); - void leave_to_overworld_and_interact(SingleSwitchProgramEnvironment& env, ProControllerContext& context, bool buy_highlight); - void recover_from_wrong_state(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void prepare_game_state(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void save_game(SingleSwitchProgramEnvironment& env, ProControllerContext& context); void advance_date(SingleSwitchProgramEnvironment& env, ProControllerContext& context, uint8_t& year); }; From 7566c46a5409ba9fd9a327acd6288ba5966532b0 Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Thu, 15 May 2025 21:38:38 +0200 Subject: [PATCH 05/11] remove unneccessary options and small cleanup --- .../RNG/PokemonSwSh_DailyHighlightRNG.cpp | 33 ++++++++----------- .../RNG/PokemonSwSh_DailyHighlightRNG.h | 8 ----- 2 files changed, 14 insertions(+), 27 deletions(-) diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp index b2d7eaad51..6ede4c7253 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -6,8 +6,6 @@ * */ -#include -#include #include "Common/Cpp/PrettyPrint.h" #include "CommonFramework/Exceptions/ProgramFinishedException.h" #include "CommonFramework/Exceptions/OperationFailedException.h" @@ -23,7 +21,6 @@ #include "Pokemon/Pokemon_Strings.h" #include "PokemonSwSh/PokemonSwSh_Settings.h" #include "PokemonSwSh/Commands/PokemonSwSh_Commands_DateSpam.h" -#include "PokemonSwSh/Inference/PokemonSwSh_DialogBoxDetector.h" #include "PokemonSwSh/Inference/PokemonSwSh_SelectionArrowFinder.h" #include "PokemonSwSh/Inference/PokemonSwSh_YCommDetector.h" #include "PokemonSwSh/Programs/PokemonSwSh_MenuNavigation.h" @@ -84,9 +81,6 @@ DailyHighlightRNG::DailyHighlightRNG() : NUM_HIGHLIGHTS( "Number of highlights:
How many daily highlights should be bought.
A value of 0 will run until you stop the program.", LockMode::UNLOCK_WHILE_RUNNING, 0) - , CONTINUE( - "Continue from last time:
If the initial two daily highlights are already manipulated and should be bought.", - LockMode::UNLOCK_WHILE_RUNNING, false) , FIX_TIME_WHEN_DONE( "Fix Time when Done:
Fix the time after the program finishes.
Doesn't do anything if Number of highlights is 0.", LockMode::UNLOCK_WHILE_RUNNING, false) @@ -146,7 +140,6 @@ DailyHighlightRNG::DailyHighlightRNG() PA_ADD_OPTION(START_LOCATION); PA_ADD_OPTION(NUM_HIGHLIGHTS); - PA_ADD_OPTION(CONTINUE); PA_ADD_OPTION(FIX_TIME_WHEN_DONE); PA_ADD_OPTION(GO_HOME_WHEN_DONE); PA_ADD_OPTION(SAVE_ITERATIONS); @@ -155,12 +148,6 @@ DailyHighlightRNG::DailyHighlightRNG() PA_ADD_OPTION(NOTIFICATIONS); PA_ADD_STATIC(m_advanced_options); - PA_ADD_OPTION(MOVE_TIME); - PA_ADD_OPTION(MOVE_TIME2); - PA_ADD_OPTION(LEFT_X); - PA_ADD_OPTION(LEFT_Y); - PA_ADD_OPTION(RIGHT_X); - PA_ADD_OPTION(RIGHT_Y); PA_ADD_OPTION(MAX_UNKNOWN_ADVANCES); PA_ADD_OPTION(ADVANCE_PRESS_DURATION); PA_ADD_OPTION(ADVANCE_RELEASE_DURATION); @@ -177,7 +164,7 @@ void DailyHighlightRNG::interact_with_trader(SingleSwitchProgramEnvironment& env pbf_press_button(context, BUTTON_A, 160ms, 600ms); pbf_press_button(context, BUTTON_A, 160ms, 160ms); - //Check if talking to the NPC was successfull + // Check if talking to the NPC was successfull VideoOverlaySet boxes(env.console); SelectionArrowFinder arrow_detector(env.console, ImageFloatBox(0.5, 0.58, 0.2, 0.08)); arrow_detector.make_overlays(boxes); @@ -185,6 +172,8 @@ void DailyHighlightRNG::interact_with_trader(SingleSwitchProgramEnvironment& env context.wait_for_all_requests(); int ret = wait_until(env.console, context, 3000ms, { arrow_detector }); if (ret < 0) { + DailyHighlightRNG_Descriptor::Stats& stats = env.current_stats(); + stats.errors++; OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, "Failed to talk to the trader.", @@ -194,6 +183,7 @@ void DailyHighlightRNG::interact_with_trader(SingleSwitchProgramEnvironment& env } void DailyHighlightRNG::buy_highlight(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { + // The player is expected to be in main dialog of the trader env.log("Buying Highlight."); env.console.overlay().add_log("Buying Highlight!", COLOR_WHITE); pbf_press_dpad(context, DPAD_DOWN, 80ms, 80ms); @@ -215,6 +205,7 @@ void DailyHighlightRNG::buy_highlight(SingleSwitchProgramEnvironment& env, ProCo ); if (ret < 0) { + env. OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, "Could not detect dialog.", @@ -248,13 +239,14 @@ uint8_t DailyHighlightRNG::calibrate_num_npc_from_party(SingleSwitchProgramEnvir env.log("Calibrating NPC amount."); env.console.overlay().add_log("Calibrating NPC amount.", COLOR_WHITE); - env.current_stats().reads++; + DailyHighlightRNG_Descriptor::Stats& stats = env.current_stats(); + stats.reads++; std::pair result = refind_rng_state_and_animations(env.console, context, rng.get_state(), 0, 100, SAVE_SCREENSHOTS, LOG_VALUES); Xoroshiro128PlusState new_state = result.first; uint64_t additional_advances = result.second; // Calculate state for possible NPC amounts - const uint8_t MAX_NPCS = 4; // Usually either 1 or 2, maybe(?) 3 or 4 -> high numbers suggest bad npc state + const uint8_t MAX_NPCS = 6; // Usually either 1 or 2, sometimes 3 or 4, maybe 5 or 6 -> high numbers suggest bad npc state std::vector rng_states; for (size_t i = 0; i <= MAX_NPCS; i++) { @@ -340,9 +332,6 @@ size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, void DailyHighlightRNG::prepare_game_state(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { env.log("Prepare player position."); env.console.overlay().add_log("Reset Player Position.", COLOR_WHITE); - - // Return to overworld - return_to_overworld(env, context); // Open map pbf_press_button(context, BUTTON_X, 160ms, GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); @@ -366,6 +355,8 @@ void DailyHighlightRNG::return_to_overworld(SingleSwitchProgramEnvironment& env, { {y_comm_icon_detector} } ); if (ret != 0) { + DailyHighlightRNG_Descriptor::Stats& stats = env.current_stats(); + stats.errors++; OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, "Cannot detect the Y-Comm icon.", @@ -420,6 +411,7 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll pbf_press_button(context, BUTTON_B, 40ms, 40ms); } + Xoroshiro128Plus rng(0, 0); bool is_state_valid = false; size_t iteration = 0; @@ -446,10 +438,13 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll if (!is_state_valid) { successfull_iterations = 0; prepare_game_state(env, context); + + // open and close the menu and immediately walk to the trader pbf_press_button(context, BUTTON_X, 160ms, GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); return_to_overworld(env, context); move_to_trader(env, context); interact_with_trader(env, context); + return_to_overworld(env, context); navigate_to_party(env, context); context.wait_for_all_requests(); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h index 662808caf5..b227ef6945 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h @@ -45,7 +45,6 @@ class DailyHighlightRNG : public SingleSwitchProgramInstance{ private: StartInGripOrGameOption START_LOCATION; SimpleIntegerOption NUM_HIGHLIGHTS; - BooleanCheckBoxOption CONTINUE; BooleanCheckBoxOption FIX_TIME_WHEN_DONE; GoHomeWhenDoneOption GO_HOME_WHEN_DONE; SimpleIntegerOption SAVE_ITERATIONS; @@ -55,13 +54,6 @@ class DailyHighlightRNG : public SingleSwitchProgramInstance{ EventNotificationsOption NOTIFICATIONS; SectionDividerOption m_advanced_options; - MillisecondsOption MOVE_TIME; - MillisecondsOption MOVE_TIME2; - SimpleIntegerOption LEFT_X; - SimpleIntegerOption LEFT_Y; - SimpleIntegerOption RIGHT_X; - SimpleIntegerOption RIGHT_Y; - SimpleIntegerOption MAX_UNKNOWN_ADVANCES; MillisecondsOption ADVANCE_PRESS_DURATION; MillisecondsOption ADVANCE_RELEASE_DURATION; From 6f81f45c97b78612dd5c270b08abeca485b480e9 Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Thu, 15 May 2025 21:55:55 +0200 Subject: [PATCH 06/11] refactor out rng state prediction after menu close, implicit cram-o-matic fix --- .../Programs/RNG/PokemonSwSh_BasicRNG.cpp | 12 +++++++++ .../Programs/RNG/PokemonSwSh_BasicRNG.h | 2 ++ .../RNG/PokemonSwSh_CramomaticRNG.cpp | 8 ++---- .../RNG/PokemonSwSh_DailyHighlightRNG.cpp | 27 +++++-------------- 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.cpp index 80b9a86e36..42a4ae2692 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.cpp @@ -180,6 +180,18 @@ void do_rng_advances( pbf_wait(context, 1000ms); } +Xoroshiro128PlusState predict_state_after_menu_close(Xoroshiro128PlusState current_state, uint8_t num_npcs) { + Xoroshiro128Plus rng(current_state); + + for (size_t i = 0; i < num_npcs; i++) { + rng.nextInt(91); + } + rng.next(); + rng.nextInt(61); + + return rng.get_state(); +} + } } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h index 923b88d84e..4267ad8be0 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h @@ -57,6 +57,8 @@ void do_rng_advances( Milliseconds release_duration ); +Xoroshiro128PlusState predict_state_after_menu_close(Xoroshiro128PlusState current_state, uint8_t num_npcs); + } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_CramomaticRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_CramomaticRNG.cpp index 506380f7e6..ee108d314c 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_CramomaticRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_CramomaticRNG.cpp @@ -194,13 +194,9 @@ CramomaticTarget CramomaticRNG::calculate_target(SingleSwitchProgramEnvironment& // priority_advances only starts counting up after the first good result is found while (priority_advances <= MAX_PRIORITY_ADVANCES){ // calculate the result for the current temp_rng state - Xoroshiro128Plus temp_rng(rng.get_state()); + Xoroshiro128PlusState temp_state = predict_state_after_menu_close(rng.get_state(), NUM_NPCS); + Xoroshiro128Plus temp_rng(temp_state); - for (size_t i = 0; i < NUM_NPCS; i++){ - temp_rng.nextInt(91); - } - temp_rng.next(); - temp_rng.nextInt(60); /*uint64_t item_roll =*/ temp_rng.nextInt(4); uint64_t ball_roll = temp_rng.nextInt(100); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp index 6ede4c7253..1fa39ab595 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -249,25 +249,14 @@ uint8_t DailyHighlightRNG::calibrate_num_npc_from_party(SingleSwitchProgramEnvir const uint8_t MAX_NPCS = 6; // Usually either 1 or 2, sometimes 3 or 4, maybe 5 or 6 -> high numbers suggest bad npc state std::vector rng_states; - for (size_t i = 0; i <= MAX_NPCS; i++) { - Xoroshiro128Plus temp_rng(rng.get_state()); - - for (size_t j = 0; j < i; j++) { - temp_rng.nextInt(91); - } - temp_rng.next(); - temp_rng.nextInt(61); + for (size_t npcs = 0; npcs <= MAX_NPCS; npcs++) { + Xoroshiro128PlusState temp_state = predict_state_after_menu_close(rng.get_state(), npcs); + Xoroshiro128Plus temp_rng(temp_state); // Do advances that were needed to find current state - for (size_t j = 0; j < additional_advances; j++) { + for (size_t i = 0; i < additional_advances; i++) { temp_rng.next(); } - - env.console.log(std::to_string(i)); - env.console.log(std::to_string(additional_advances)); - env.console.log(tostr_hex(temp_rng.get_state().s0)); - env.console.log(tostr_hex(temp_rng.get_state().s1)); - rng_states.push_back(temp_rng.get_state()); } @@ -305,12 +294,8 @@ size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, while (!found_advance_amount) { // Calculate the result for the current temp_rng state - Xoroshiro128Plus temp_rng(rng.get_state()); - for (size_t i = 0; i < num_npcs; i++) { - temp_rng.nextInt(91); - } - temp_rng.next(); - temp_rng.nextInt(61); + Xoroshiro128PlusState temp_state = predict_state_after_menu_close(rng.get_state(), num_npcs); + Xoroshiro128Plus temp_rng(temp_state); uint64_t highlight_roll = temp_rng.nextInt(1000); From e0e2f8aa1e58673e07e4c4ae1e1065c9b16365d8 Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Thu, 15 May 2025 22:06:46 +0200 Subject: [PATCH 07/11] move DailyHighlightRNG under developer mode --- .../Source/PokemonSwSh/PokemonSwSh_Panels.cpp | 4 ++-- .../Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp | 12 +++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp b/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp index 4ed489bdb3..3a03402d69 100644 --- a/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp +++ b/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp @@ -180,7 +180,6 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back("---- RNG ----"); ret.emplace_back(make_single_switch_program()); ret.emplace_back(make_single_switch_program()); - ret.emplace_back(make_single_switch_program()); ret.emplace_back("---- Multi-Switch Programs ----"); ret.emplace_back(make_multi_switch_program()); @@ -202,7 +201,8 @@ std::vector PanelListFactory::make_panels() const{ ret.emplace_back(make_single_switch_program()); if (PreloadSettings::instance().DEVELOPER_MODE){ -// ret.emplace_back("---- Untested/Beta/WIP ----"); + ret.emplace_back("---- Untested/Beta/WIP ----"); + ret.emplace_back(make_single_switch_program()); } if (PreloadSettings::instance().DEVELOPER_MODE){ ret.emplace_back("---- Developer Tools ----"); diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp index 1fa39ab595..bef099cff6 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -6,7 +6,6 @@ * */ -#include "Common/Cpp/PrettyPrint.h" #include "CommonFramework/Exceptions/ProgramFinishedException.h" #include "CommonFramework/Exceptions/OperationFailedException.h" #include "CommonFramework/Notifications/ProgramNotifications.h" @@ -105,12 +104,6 @@ DailyHighlightRNG::DailyHighlightRNG() , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) - , MOVE_TIME("Move time:", LockMode::LOCK_WHILE_RUNNING, "1280 ms") - , MOVE_TIME2("Move time right:", LockMode::LOCK_WHILE_RUNNING, "1280 ms") - , LEFT_X("Left X:", LockMode::LOCK_WHILE_RUNNING, 207) - , LEFT_Y("Left Y:", LockMode::LOCK_WHILE_RUNNING, 1) - , RIGHT_X("Right X:", LockMode::LOCK_WHILE_RUNNING, 127) - , RIGHT_Y("Right Y:", LockMode::LOCK_WHILE_RUNNING, 127) , MAX_UNKNOWN_ADVANCES( "Max Unknown advances:
How many advances to check when updating the rng state.", LockMode::LOCK_WHILE_RUNNING, @@ -205,7 +198,8 @@ void DailyHighlightRNG::buy_highlight(SingleSwitchProgramEnvironment& env, ProCo ); if (ret < 0) { - env. + DailyHighlightRNG_Descriptor::Stats& stats = env.current_stats(); + stats.errors++; OperationFailedException::fire( ErrorReport::SEND_ERROR_REPORT, "Could not detect dialog.", @@ -249,7 +243,7 @@ uint8_t DailyHighlightRNG::calibrate_num_npc_from_party(SingleSwitchProgramEnvir const uint8_t MAX_NPCS = 6; // Usually either 1 or 2, sometimes 3 or 4, maybe 5 or 6 -> high numbers suggest bad npc state std::vector rng_states; - for (size_t npcs = 0; npcs <= MAX_NPCS; npcs++) { + for (uint8_t npcs = 0; npcs <= MAX_NPCS; npcs++) { Xoroshiro128PlusState temp_state = predict_state_after_menu_close(rng.get_state(), npcs); Xoroshiro128Plus temp_rng(temp_state); From 3a1689780673bc9954d870d471d7dbd55d58475c Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Sun, 15 Jun 2025 22:12:50 +0200 Subject: [PATCH 08/11] Change calibration procedure. Several small changes to make the program more stable. --- .../RNG/PokemonSwSh_DailyHighlightRNG.cpp | 270 +++++++++++------- .../RNG/PokemonSwSh_DailyHighlightRNG.h | 8 +- 2 files changed, 176 insertions(+), 102 deletions(-) diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp index bef099cff6..6d9a2de930 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -26,6 +26,8 @@ #include "PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h" #include "PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h" +#include "Common/Cpp/PrettyPrint.h" + namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -78,17 +80,22 @@ std::unique_ptr DailyHighlightRNG_Descriptor::make_stats() const{ DailyHighlightRNG::DailyHighlightRNG() : NUM_HIGHLIGHTS( - "Number of highlights:
How many daily highlights should be bought.
A value of 0 will run until you stop the program.", + "Number of highlights:
How many daily highlights should be bought.
Zero will run until you stop the program.", LockMode::UNLOCK_WHILE_RUNNING, 0) , FIX_TIME_WHEN_DONE( "Fix Time when Done:
Fix the time after the program finishes.
Doesn't do anything if Number of highlights is 0.", LockMode::UNLOCK_WHILE_RUNNING, false) , GO_HOME_WHEN_DONE(false) - , SAVE_ITERATIONS( - "Save Every this Many Day Skips:
(zero disables saving): ", - LockMode::LOCK_WHILE_RUNNING, + , SAVE_INTERVAL( + "Save Every this Many Day Skips:
(zero disables saving)", + LockMode::UNLOCK_WHILE_RUNNING, 20 ) + , CALIBRATION_INTERAVAL( + "Calibrate the number of NPCs this many Day Skips:
Zero will only calibrate once.", + LockMode::UNLOCK_WHILE_RUNNING, + 10 + ) , HIGHLIGHT_SELECTION( "Desired Highlights:", "Highlight", @@ -104,8 +111,23 @@ DailyHighlightRNG::DailyHighlightRNG() , m_advanced_options( "Advanced Options: You should not need to touch anything below here." ) + , MAX_EXPECTED_NPCS( + "Max Expected NPCs:
How many NPCs are expected in the area at most.", + LockMode::LOCK_WHILE_RUNNING, + 8 + ) + , CALIBRATION_REPEATS( + "Calibration Repeats:
How many times the number of NPCs is checked per calibration.", + LockMode::LOCK_WHILE_RUNNING, + 5 + ) + , CALIBRATION_THRESHOLD( + "Calibration Treshold:
How many NPC counts need to be the same.
This needs to be less than or equal to Calibration Repeats.", + LockMode::LOCK_WHILE_RUNNING, + 3 + ) , MAX_UNKNOWN_ADVANCES( - "Max Unknown advances:
How many advances to check when updating the rng state.", + "Max Unknown Advances:
How many advances to check when updating the rng state.", LockMode::LOCK_WHILE_RUNNING, 100000 ) @@ -135,12 +157,16 @@ DailyHighlightRNG::DailyHighlightRNG() PA_ADD_OPTION(NUM_HIGHLIGHTS); PA_ADD_OPTION(FIX_TIME_WHEN_DONE); PA_ADD_OPTION(GO_HOME_WHEN_DONE); - PA_ADD_OPTION(SAVE_ITERATIONS); + PA_ADD_OPTION(SAVE_INTERVAL); + PA_ADD_OPTION(CALIBRATION_INTERAVAL); PA_ADD_OPTION(HIGHLIGHT_SELECTION); PA_ADD_OPTION(NOTIFICATIONS); PA_ADD_STATIC(m_advanced_options); + PA_ADD_OPTION(CALIBRATION_REPEATS); + PA_ADD_OPTION(CALIBRATION_THRESHOLD); + PA_ADD_OPTION(MAX_EXPECTED_NPCS); PA_ADD_OPTION(MAX_UNKNOWN_ADVANCES); PA_ADD_OPTION(ADVANCE_PRESS_DURATION); PA_ADD_OPTION(ADVANCE_RELEASE_DURATION); @@ -149,29 +175,36 @@ DailyHighlightRNG::DailyHighlightRNG() } void DailyHighlightRNG::move_to_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { - pbf_move_right_joystick(context, 255, 128, 500ms, 80ms); + pbf_move_right_joystick(context, 255, 128, 460ms, 80ms); pbf_move_left_joystick(context, 128, 0, 1280ms, 80ms); } void DailyHighlightRNG::interact_with_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { - pbf_press_button(context, BUTTON_A, 160ms, 600ms); - pbf_press_button(context, BUTTON_A, 160ms, 160ms); - - // Check if talking to the NPC was successfull VideoOverlaySet boxes(env.console); SelectionArrowFinder arrow_detector(env.console, ImageFloatBox(0.5, 0.58, 0.2, 0.08)); arrow_detector.make_overlays(boxes); + YCommIconDetector y_comm_icon_detector(true); + size_t tries = 0; - context.wait_for_all_requests(); - int ret = wait_until(env.console, context, 3000ms, { arrow_detector }); - if (ret < 0) { - DailyHighlightRNG_Descriptor::Stats& stats = env.current_stats(); - stats.errors++; - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, - "Failed to talk to the trader.", - env.console - ); + while (true) { + if (tries >= 5) { + DailyHighlightRNG_Descriptor::Stats& stats = env.current_stats(); + stats.errors++; + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "Failed to talk to the trader.", + env.console + ); + } + context.wait_for_all_requests(); + int ret = wait_until(env.console, context, 500ms, { y_comm_icon_detector, arrow_detector }); + if (ret == -1 || ret == 0) { + tries++; + pbf_press_button(context, BUTTON_A, 80ms, 80ms); + } + else if (ret == 1) { + return; + } } } @@ -179,7 +212,7 @@ void DailyHighlightRNG::buy_highlight(SingleSwitchProgramEnvironment& env, ProCo // The player is expected to be in main dialog of the trader env.log("Buying Highlight."); env.console.overlay().add_log("Buying Highlight!", COLOR_WHITE); - pbf_press_dpad(context, DPAD_DOWN, 80ms, 80ms); + pbf_press_dpad(context, DPAD_DOWN, 160ms, 160ms); pbf_press_button(context, BUTTON_A, 160ms, 160ms); context.wait_for_all_requests(); @@ -210,16 +243,16 @@ void DailyHighlightRNG::buy_highlight(SingleSwitchProgramEnvironment& env, ProCo void DailyHighlightRNG::navigate_to_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { pbf_press_button(context, BUTTON_X, 80ms, GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); + pbf_wait(context, 200ms); navigate_to_menu_app(env, env.console, context, 1, NOTIFICATION_ERROR_FATAL); pbf_press_button(context, BUTTON_A, 80ms, 3000ms); - context.wait_for_all_requests(); } void DailyHighlightRNG::save_game(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { - context.wait_for_all_requests(); env.log("Saving."); env.console.overlay().add_log("Saving!", COLOR_WHITE); pbf_press_button(context, BUTTON_X, 80ms, GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); + pbf_wait(context, 200ms); pbf_press_button(context, BUTTON_R, 80ms, 2000ms); pbf_mash_button(context, BUTTON_A, 500ms); @@ -227,52 +260,79 @@ void DailyHighlightRNG::save_game(SingleSwitchProgramEnvironment& env, ProContro } uint8_t DailyHighlightRNG::calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context, Pokemon::Xoroshiro128Plus& rng) { - return_to_overworld(env, context); - navigate_to_party(env, context); - - env.log("Calibrating NPC amount."); - env.console.overlay().add_log("Calibrating NPC amount.", COLOR_WHITE); + env.log("Calibrating NPC count."); + env.console.overlay().add_log("Calibrating NPC count.", COLOR_WHITE); DailyHighlightRNG_Descriptor::Stats& stats = env.current_stats(); - stats.reads++; - std::pair result = refind_rng_state_and_animations(env.console, context, rng.get_state(), 0, 100, SAVE_SCREENSHOTS, LOG_VALUES); - Xoroshiro128PlusState new_state = result.first; - uint64_t additional_advances = result.second; + size_t max_expected_npcs = MAX_EXPECTED_NPCS; + size_t calibration_repeats = CALIBRATION_REPEATS; + size_t calibration_threshold = CALIBRATION_THRESHOLD; + size_t finished_calibration_steps = 0; + std::vector npc_counts(max_expected_npcs, 0); + while (finished_calibration_steps < calibration_repeats) { + env.log("Calibration: " + std::to_string(finished_calibration_steps+1) + "/" + std::to_string(calibration_repeats)); + env.console.overlay().add_log("Calibration: " + std::to_string(finished_calibration_steps+1) + "/" + std::to_string(calibration_repeats), COLOR_WHITE); + + return_to_overworld(env, context); + navigate_to_party(env, context); - // Calculate state for possible NPC amounts - const uint8_t MAX_NPCS = 6; // Usually either 1 or 2, sometimes 3 or 4, maybe 5 or 6 -> high numbers suggest bad npc state - std::vector rng_states; + Xoroshiro128PlusState state_before_menu_close(rng.get_state()); + std::pair result = refind_rng_state_and_animations(env.console, context, rng.get_state(), 0, 100, SAVE_SCREENSHOTS, LOG_VALUES); + Xoroshiro128PlusState new_state = result.first; + uint64_t additional_advances = result.second; - for (uint8_t npcs = 0; npcs <= MAX_NPCS; npcs++) { - Xoroshiro128PlusState temp_state = predict_state_after_menu_close(rng.get_state(), npcs); - Xoroshiro128Plus temp_rng(temp_state); + // Calculate state for possible NPC counts + std::vector rng_states; + + for (uint8_t npcs = 0; npcs <= max_expected_npcs; npcs++) { + Xoroshiro128PlusState temp_state = predict_state_after_menu_close(state_before_menu_close, npcs); + Xoroshiro128Plus temp_rng(temp_state); + + // Do advances that were needed to refind current state + for (size_t i = 0; i < additional_advances; i++) { + temp_rng.next(); + } + rng_states.push_back(temp_rng.get_state()); + } - // Do advances that were needed to find current state - for (size_t i = 0; i < additional_advances; i++) { - temp_rng.next(); + // Compare current state to expected states + uint8_t num_npcs = 0; + uint8_t possible_choices = 0; + for (uint8_t i = 0; i <= max_expected_npcs; i++) { + if (rng_states[i].s0 == new_state.s0 && rng_states[i].s1 == new_state.s1) { + num_npcs = i; + possible_choices++; + } } - rng_states.push_back(temp_rng.get_state()); - } + rng.state = new_state; - // Compare current state to expected states - uint8_t num_npcs = 0; - for (uint8_t i = 1; i <= MAX_NPCS; i++) { - if (rng_states[i].s0 == new_state.s0 && rng_states[i].s1 == new_state.s1) { - rng.state = new_state; - num_npcs = i; + stats.reads++; + + // Add another calibration round if multiple npcs counts are possible + if (possible_choices > 1) { + env.log("NPC count is ambiguous."); + env.console.overlay().add_log("NPC count is ambiguous. Repeat calibration.", COLOR_WHITE); + continue; + } + + env.console.log("Calculated there are " + std::to_string(num_npcs) + " NPCs."); + env.console.overlay().add_log(std::to_string(num_npcs) + " NPCs", COLOR_WHITE); + npc_counts[num_npcs]++; + finished_calibration_steps++; + + if (npc_counts[num_npcs] >= calibration_threshold) { + env.console.log("Final Calibration: " + std::to_string(num_npcs) + " NPCs."); + env.console.overlay().add_log("Final Calibration: " + std::to_string(num_npcs) + " NPCs", COLOR_WHITE); + + return num_npcs; } - } - if (num_npcs == 0) { - OperationFailedException::fire( - ErrorReport::SEND_ERROR_REPORT, - "NPC is in the wrong state or an unexpected amount of NPCs is in the area.", - env.console - ); } - env.console.log("Calculated there are " + std::to_string(num_npcs) + "NPCs."); - env.console.overlay().add_log(std::to_string(num_npcs) + " NPCs", COLOR_WHITE); - return num_npcs; + OperationFailedException::fire( + ErrorReport::SEND_ERROR_REPORT, + "NPC is in the wrong state or an unexpected number of NPCs is in the area.", + env.console + ); } size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, Xoroshiro128PlusState state, uint8_t num_npcs, std::vector wanted_highlights) { @@ -294,8 +354,9 @@ size_t DailyHighlightRNG::calculate_target(SingleSwitchProgramEnvironment& env, uint64_t highlight_roll = temp_rng.nextInt(1000); for (auto& range : ranges) { - if (range.first < highlight_roll && range.second > highlight_roll) { + if (range.first <= highlight_roll && range.second >= highlight_roll) { found_advance_amount = true; + env.console.log("Target highlight roll: " + std::to_string(highlight_roll)); } } @@ -314,16 +375,20 @@ void DailyHighlightRNG::prepare_game_state(SingleSwitchProgramEnvironment& env, // Open map pbf_press_button(context, BUTTON_X, 160ms, GameSettings::instance().OVERWORLD_TO_MENU_DELAY0); + pbf_wait(context, 200ms); navigate_to_menu_app(env, env.console, context, 5, NOTIFICATION_ERROR_RECOVERABLE); + pbf_wait(context, 200ms); pbf_press_button(context, BUTTON_A, 160ms, 4000ms); // Fly to Snowslide Slope pbf_move_left_joystick(context, 200, 210, 160ms, 160ms); - pbf_mash_button(context, BUTTON_A, 1000ms); + pbf_move_left_joystick(context, 150, 165, 160ms, 160ms); + pbf_mash_button(context, BUTTON_A, 1500ms); return_to_overworld(env, context); } -void DailyHighlightRNG::return_to_overworld(SingleSwitchProgramEnvironment& env, ProControllerContext& context) { +void DailyHighlightRNG::return_to_overworld(SingleSwitchProgramEnvironment& env, ProControllerContext& context, bool wait_after_detection) { + context.wait_for_all_requests(); env.console.log("Returning to the overworld."); YCommIconDetector y_comm_icon_detector(true); int ret = run_until( @@ -342,11 +407,16 @@ void DailyHighlightRNG::return_to_overworld(SingleSwitchProgramEnvironment& env, env.console ); } -} + // Sometimes Y-Comm is detected before button presses are accepted again + if (wait_after_detection) { + context.wait_for(300ms); + } +} void DailyHighlightRNG::advance_date(SingleSwitchProgramEnvironment& env, ProControllerContext& context, uint8_t& year) { - pbf_press_button(context, BUTTON_HOME, 80ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); + context.wait_for(200ms); + pbf_press_button(context, BUTTON_HOME, 160ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); home_to_date_time(context, true, false); pbf_press_button(context, BUTTON_A, 160ms, 240ms); context.wait_for_all_requests(); @@ -355,7 +425,7 @@ void DailyHighlightRNG::advance_date(SingleSwitchProgramEnvironment& env, ProCon DateChangeWatcher date_reader; date_reader.make_overlays(overlays); - DateTime date{ 2000, 10, 31, 1, 0, 0 }; // 31st October for fixed Overcast weather + DateTime date{ 2000, 12, 31, 1, 0, 0 }; // 31st December for fixed Normal weather if (year >= MAX_YEAR) { date_reader.set_date(env.program_info(), env.console, context, date); @@ -387,35 +457,33 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll grip_menu_connect_go_home(context); } else { - pbf_press_button(context, BUTTON_B, 40ms, 40ms); + pbf_press_button(context, BUTTON_B, 80ms, 80ms); } Xoroshiro128Plus rng(0, 0); - bool is_state_valid = false; - size_t iteration = 0; - size_t successfull_iterations = 0; + uint8_t num_npcs = 0; + size_t iterations = 0; + size_t assumed_successful_iterations = 0; size_t bought_highlights = 0; uint8_t year = MAX_YEAR; uint16_t state_errors = 0; while (bought_highlights < NUM_HIGHLIGHTS || NUM_HIGHLIGHTS <= 0){ - iteration++; + iterations++; stats.iterations++; env.update_stats(); send_program_status_notification(env, NOTIFICATION_STATUS_UPDATE); - env.console.log("Daily Highlight RNG iteration: " + std::to_string(iteration)); - env.console.overlay().add_log("Iteration: " + std::to_string(iteration), COLOR_WHITE); + env.console.log("Daily Highlight RNG iteration: " + std::to_string(iterations)); + env.console.overlay().add_log("Iteration: " + std::to_string(iterations), COLOR_WHITE); - if (SAVE_ITERATIONS > 0 && iteration % SAVE_ITERATIONS == 0) { + if (SAVE_INTERVAL > 0 && iterations % SAVE_INTERVAL == 0) { save_game(env, context); } return_to_overworld(env, context); - advance_date(env, context, year); - if (!is_state_valid) { - successfull_iterations = 0; + if (assumed_successful_iterations <= 0) { prepare_game_state(env, context); // open and close the menu and immediately walk to the trader @@ -425,20 +493,21 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll interact_with_trader(env, context); return_to_overworld(env, context); - navigate_to_party(env, context); - context.wait_for_all_requests(); + } + + advance_date(env, context, year); + navigate_to_party(env, context); + context.wait_for_all_requests(); - // Find RNG state + // (Re-)Find RNG state + if (assumed_successful_iterations <= 0) { env.log("Finding initial rng state."); env.console.overlay().add_log("Initial RNG state:", COLOR_WHITE); rng = Xoroshiro128Plus(find_rng_state(env.console, context, SAVE_SCREENSHOTS, LOG_VALUES)); stats.reads++; - is_state_valid = true; - continue; - }else{ + } else { env.log("Refinding rng state."); env.console.overlay().add_log("New RNG state:", COLOR_WHITE); - navigate_to_party(env, context); rng = Xoroshiro128Plus(refind_rng_state(env.console, context, rng.get_state(), 0, MAX_UNKNOWN_ADVANCES, SAVE_SCREENSHOTS, LOG_VALUES)); stats.reads++; } @@ -458,31 +527,32 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll } VideoSnapshot screen = env.console.video().snapshot(); send_program_recoverable_error_notification(env, NOTIFICATION_ERROR_RECOVERABLE, "Detected invalid RNG state.", screen); - is_state_valid = false; + assumed_successful_iterations = 0; continue; } // Calibrate number of NPCs in the area and check whether the trader is in the slow state - uint8_t num_npcs; - try { - num_npcs = calibrate_num_npc_from_party(env, context, rng); - } - catch (OperationFailedException& exception) { - send_program_recoverable_error_notification(env, NOTIFICATION_ERROR_RECOVERABLE, exception.message(), exception.screenshot()); - is_state_valid = false; - stats.errors++; - continue; + if ((CALIBRATION_INTERAVAL > 0 && assumed_successful_iterations % CALIBRATION_INTERAVAL == 0) || assumed_successful_iterations <= 0) { + try { + num_npcs = calibrate_num_npc_from_party(env, context, rng); + } + catch (OperationFailedException& exception) { + send_program_recoverable_error_notification(env, NOTIFICATION_ERROR_RECOVERABLE, exception.message(), exception.screenshot()); + assumed_successful_iterations = 0; + stats.errors++; + continue; + } } // Do required advances - size_t target_advances = calculate_target(env, rng.get_state(), num_npcs, HIGHLIGHT_SELECTION.all_slugs()); + size_t target_advances = calculate_target(env, rng.get_state(), num_npcs, HIGHLIGHT_SELECTION.all_slugs()); // TODO: make highlight selection env.console.log("Needed advances: " + std::to_string(target_advances)); do_rng_advances(env.console, context, rng, target_advances, ADVANCE_PRESS_DURATION, ADVANCE_RELEASE_DURATION); // Talk to NPC and buy highlight - return_to_overworld(env, context); + return_to_overworld(env, context, false); interact_with_trader(env, context); - if (successfull_iterations >= 2) { + if (assumed_successful_iterations >= 2) { buy_highlight(env, context); bought_highlights++; stats.highlights++; @@ -490,12 +560,12 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll return_to_overworld(env, context); env.update_stats(); - successfull_iterations++; + assumed_successful_iterations++; state_errors = 0; } if (FIX_TIME_WHEN_DONE) { - pbf_press_button(context, BUTTON_HOME, 80ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); + pbf_press_button(context, BUTTON_HOME, 160ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); home_to_date_time(context, false, false); pbf_press_button(context, BUTTON_A, 20, 105); pbf_press_button(context, BUTTON_A, 20, 105); @@ -503,7 +573,7 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll resume_game_from_home(env.console, context); } GO_HOME_WHEN_DONE.run_end_of_program(context); - send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH); + send_program_finished_notification(env, NOTIFICATION_PROGRAM_FINISH, "Finished buying " + std::to_string(bought_highlights) + " daily highlights!", env.console.video().snapshot()); } diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h index b227ef6945..3176a3923e 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h @@ -47,13 +47,17 @@ class DailyHighlightRNG : public SingleSwitchProgramInstance{ SimpleIntegerOption NUM_HIGHLIGHTS; BooleanCheckBoxOption FIX_TIME_WHEN_DONE; GoHomeWhenDoneOption GO_HOME_WHEN_DONE; - SimpleIntegerOption SAVE_ITERATIONS; + SimpleIntegerOption SAVE_INTERVAL; + SimpleIntegerOption CALIBRATION_INTERAVAL; StringSelectTableOption HIGHLIGHT_SELECTION; EventNotificationOption NOTIFICATION_STATUS_UPDATE; EventNotificationsOption NOTIFICATIONS; SectionDividerOption m_advanced_options; + SimpleIntegerOption MAX_EXPECTED_NPCS; + SimpleIntegerOption CALIBRATION_REPEATS; + SimpleIntegerOption CALIBRATION_THRESHOLD; SimpleIntegerOption MAX_UNKNOWN_ADVANCES; MillisecondsOption ADVANCE_PRESS_DURATION; MillisecondsOption ADVANCE_RELEASE_DURATION; @@ -62,7 +66,7 @@ class DailyHighlightRNG : public SingleSwitchProgramInstance{ void move_to_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context); void interact_with_trader(SingleSwitchProgramEnvironment& env, ProControllerContext& context); - void return_to_overworld(SingleSwitchProgramEnvironment& env, ProControllerContext& context); + void return_to_overworld(SingleSwitchProgramEnvironment& env, ProControllerContext& context, bool wait_after_detection = true); void buy_highlight(SingleSwitchProgramEnvironment& env, ProControllerContext& context); uint8_t calibrate_num_npc_from_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context, Pokemon::Xoroshiro128Plus& rng); void navigate_to_party(SingleSwitchProgramEnvironment& env, ProControllerContext& context); From 1ccbeaa3f729390766589b926dc353be0de1f0ff Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:02:16 +0200 Subject: [PATCH 09/11] Adapt to new date manipulation routines --- .../RNG/PokemonSwSh_DailyHighlightRNG.cpp | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp index 6d9a2de930..bb62d08012 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -13,9 +13,9 @@ #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Async/InferenceRoutines.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" -#include "NintendoSwitch/Inference/NintendoSwitch_DateReader.h" +#include "NintendoSwitch/Programs/DateSpam/NintendoSwitch_HomeToDateTime.h" +#include "NintendoSwitch/Programs/DateManip/NintendoSwitch_DateManip.h" #include "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h" -#include "NintendoSwitch/Programs/NintendoSwitch_Navigation.h" #include "NintendoSwitch/NintendoSwitch_Settings.h" #include "Pokemon/Pokemon_Strings.h" #include "PokemonSwSh/PokemonSwSh_Settings.h" @@ -26,8 +26,6 @@ #include "PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h" #include "PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h" -#include "Common/Cpp/PrettyPrint.h" - namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -80,19 +78,19 @@ std::unique_ptr DailyHighlightRNG_Descriptor::make_stats() const{ DailyHighlightRNG::DailyHighlightRNG() : NUM_HIGHLIGHTS( - "Number of highlights:
How many daily highlights should be bought.
Zero will run until you stop the program.", + "Number of Highlights:
How many daily highlights should be bought.
Zero will run until you stop the program.", LockMode::UNLOCK_WHILE_RUNNING, 0) , FIX_TIME_WHEN_DONE( - "Fix Time when Done:
Fix the time after the program finishes.
Doesn't do anything if Number of highlights is 0.", + "Fix Time when Done:
Fix the time after the program finishes.
Doesn't do anything if Number of Highlights is 0.", LockMode::UNLOCK_WHILE_RUNNING, false) , GO_HOME_WHEN_DONE(false) , SAVE_INTERVAL( - "Save Every this Many Day Skips:
(zero disables saving)", + "Save Every this Many Day Skips:
Zero disables saving.", LockMode::UNLOCK_WHILE_RUNNING, 20 ) , CALIBRATION_INTERAVAL( - "Calibrate the number of NPCs this many Day Skips:
Zero will only calibrate once.", + "Calibrate the Number of NPCs this Many Day Skips:
Zero will only calibrate once.", LockMode::UNLOCK_WHILE_RUNNING, 10 ) @@ -100,7 +98,7 @@ DailyHighlightRNG::DailyHighlightRNG() "Desired Highlights:", "Highlight", DAILY_HIGHLIGHT_DATABASE().database(), - "bottle-cap-1") + "dream-ball") , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) , NOTIFICATIONS({ &NOTIFICATION_STATUS_UPDATE, @@ -417,12 +415,12 @@ void DailyHighlightRNG::return_to_overworld(SingleSwitchProgramEnvironment& env, void DailyHighlightRNG::advance_date(SingleSwitchProgramEnvironment& env, ProControllerContext& context, uint8_t& year) { context.wait_for(200ms); pbf_press_button(context, BUTTON_HOME, 160ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); - home_to_date_time(context, true, false); + home_to_date_time(env.console, context, true); pbf_press_button(context, BUTTON_A, 160ms, 240ms); context.wait_for_all_requests(); VideoOverlaySet overlays(env.console.overlay()); - DateChangeWatcher date_reader; + DateChangeWatcher date_reader(env.console); date_reader.make_overlays(overlays); DateTime date{ 2000, 12, 31, 1, 0, 0 }; // 31st December for fixed Normal weather @@ -545,7 +543,7 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll } // Do required advances - size_t target_advances = calculate_target(env, rng.get_state(), num_npcs, HIGHLIGHT_SELECTION.all_slugs()); // TODO: make highlight selection + size_t target_advances = calculate_target(env, rng.get_state(), num_npcs, HIGHLIGHT_SELECTION.all_slugs()); env.console.log("Needed advances: " + std::to_string(target_advances)); do_rng_advances(env.console, context, rng, target_advances, ADVANCE_PRESS_DURATION, ADVANCE_RELEASE_DURATION); @@ -566,7 +564,7 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll if (FIX_TIME_WHEN_DONE) { pbf_press_button(context, BUTTON_HOME, 160ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); - home_to_date_time(context, false, false); + home_to_date_time(env.console, context, false); pbf_press_button(context, BUTTON_A, 20, 105); pbf_press_button(context, BUTTON_A, 20, 105); pbf_press_button(context, BUTTON_HOME, 160ms, ConsoleSettings::instance().SETTINGS_TO_HOME_DELAY0); From 78ae4b4e4d1766797663fa98aa1e50869e817c13 Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:02:16 +0200 Subject: [PATCH 10/11] Adapt to new date manipulation routines --- .../Source/PokemonSwSh/PokemonSwSh_Panels.cpp | 22 ++++++++--------- .../RNG/PokemonSwSh_DailyHighlightRNG.cpp | 24 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp b/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp index 3a03402d69..b70948dfe2 100644 --- a/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp +++ b/SerialPrograms/Source/PokemonSwSh/PokemonSwSh_Panels.cpp @@ -47,9 +47,14 @@ #include "Programs/NonShinyHunting/PokemonSwSh_StatsReset-Moltres.h" #include "Programs/NonShinyHunting/PokemonSwSh_StatsReset-Regi.h" -#include "Programs/RNG/PokemonSwSh_CramomaticRNG.h" -#include "Programs/RNG/PokemonSwSh_DailyHighlightRNG.h" -#include "Programs/RNG/PokemonSwSh_SeedFinder.h" +#include "Programs/EggPrograms/PokemonSwSh_EggAutonomous.h" +#include "Programs/EggPrograms/PokemonSwSh_EggFetcher2.h" +#include "Programs/EggPrograms/PokemonSwSh_EggFetcherMultiple.h" +#include "Programs/EggPrograms/PokemonSwSh_EggHatcher.h" +#include "Programs/EggPrograms/PokemonSwSh_EggCombined2.h" +#include "Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.h" +#include "Programs/EggPrograms/PokemonSwSh_GodEggDuplication.h" +#include "Programs/EggPrograms/PokemonSwSh_GodEggItemDupe.h" #include "Programs/ShinyHuntUnattended/PokemonSwSh_MultiGameFossil.h" #include "Programs/ShinyHuntUnattended/PokemonSwSh_ShinyHuntUnattended-Regi.h" @@ -69,14 +74,9 @@ #include "Programs/ShinyHuntAutonomous/PokemonSwSh_ShinyHuntAutonomous-Fishing.h" #include "Programs/OverworldBot/PokemonSwSh_ShinyHuntAutonomous-Overworld.h" -#include "Programs/EggPrograms/PokemonSwSh_EggAutonomous.h" -#include "Programs/EggPrograms/PokemonSwSh_EggFetcher2.h" -#include "Programs/EggPrograms/PokemonSwSh_EggFetcherMultiple.h" -#include "Programs/EggPrograms/PokemonSwSh_EggHatcher.h" -#include "Programs/EggPrograms/PokemonSwSh_EggCombined2.h" -#include "Programs/EggPrograms/PokemonSwSh_EggSuperCombined2.h" -#include "Programs/EggPrograms/PokemonSwSh_GodEggDuplication.h" -#include "Programs/EggPrograms/PokemonSwSh_GodEggItemDupe.h" +#include "Programs/RNG/PokemonSwSh_CramomaticRNG.h" +#include "Programs/RNG/PokemonSwSh_DailyHighlightRNG.h" +#include "Programs/RNG/PokemonSwSh_SeedFinder.h" #include "Programs/PokemonSwSh_SynchronizedSpinning.h" #include "Programs/PokemonSwSh_RaidItemFarmerOKHO.h" diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp index 6d9a2de930..bb62d08012 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.cpp @@ -13,9 +13,9 @@ #include "CommonFramework/VideoPipeline/VideoFeed.h" #include "CommonTools/Async/InferenceRoutines.h" #include "NintendoSwitch/Commands/NintendoSwitch_Commands_PushButtons.h" -#include "NintendoSwitch/Inference/NintendoSwitch_DateReader.h" +#include "NintendoSwitch/Programs/DateSpam/NintendoSwitch_HomeToDateTime.h" +#include "NintendoSwitch/Programs/DateManip/NintendoSwitch_DateManip.h" #include "NintendoSwitch/Programs/NintendoSwitch_GameEntry.h" -#include "NintendoSwitch/Programs/NintendoSwitch_Navigation.h" #include "NintendoSwitch/NintendoSwitch_Settings.h" #include "Pokemon/Pokemon_Strings.h" #include "PokemonSwSh/PokemonSwSh_Settings.h" @@ -26,8 +26,6 @@ #include "PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h" #include "PokemonSwSh/Programs/RNG/PokemonSwSh_DailyHighlightRNG.h" -#include "Common/Cpp/PrettyPrint.h" - namespace PokemonAutomation{ namespace NintendoSwitch{ @@ -80,19 +78,19 @@ std::unique_ptr DailyHighlightRNG_Descriptor::make_stats() const{ DailyHighlightRNG::DailyHighlightRNG() : NUM_HIGHLIGHTS( - "Number of highlights:
How many daily highlights should be bought.
Zero will run until you stop the program.", + "Number of Highlights:
How many daily highlights should be bought.
Zero will run until you stop the program.", LockMode::UNLOCK_WHILE_RUNNING, 0) , FIX_TIME_WHEN_DONE( - "Fix Time when Done:
Fix the time after the program finishes.
Doesn't do anything if Number of highlights is 0.", + "Fix Time when Done:
Fix the time after the program finishes.
Doesn't do anything if Number of Highlights is 0.", LockMode::UNLOCK_WHILE_RUNNING, false) , GO_HOME_WHEN_DONE(false) , SAVE_INTERVAL( - "Save Every this Many Day Skips:
(zero disables saving)", + "Save Every this Many Day Skips:
Zero disables saving.", LockMode::UNLOCK_WHILE_RUNNING, 20 ) , CALIBRATION_INTERAVAL( - "Calibrate the number of NPCs this many Day Skips:
Zero will only calibrate once.", + "Calibrate the Number of NPCs this Many Day Skips:
Zero will only calibrate once.", LockMode::UNLOCK_WHILE_RUNNING, 10 ) @@ -100,7 +98,7 @@ DailyHighlightRNG::DailyHighlightRNG() "Desired Highlights:", "Highlight", DAILY_HIGHLIGHT_DATABASE().database(), - "bottle-cap-1") + "dream-ball") , NOTIFICATION_STATUS_UPDATE("Status Update", true, false, std::chrono::seconds(3600)) , NOTIFICATIONS({ &NOTIFICATION_STATUS_UPDATE, @@ -417,12 +415,12 @@ void DailyHighlightRNG::return_to_overworld(SingleSwitchProgramEnvironment& env, void DailyHighlightRNG::advance_date(SingleSwitchProgramEnvironment& env, ProControllerContext& context, uint8_t& year) { context.wait_for(200ms); pbf_press_button(context, BUTTON_HOME, 160ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); - home_to_date_time(context, true, false); + home_to_date_time(env.console, context, true); pbf_press_button(context, BUTTON_A, 160ms, 240ms); context.wait_for_all_requests(); VideoOverlaySet overlays(env.console.overlay()); - DateChangeWatcher date_reader; + DateChangeWatcher date_reader(env.console); date_reader.make_overlays(overlays); DateTime date{ 2000, 12, 31, 1, 0, 0 }; // 31st December for fixed Normal weather @@ -545,7 +543,7 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll } // Do required advances - size_t target_advances = calculate_target(env, rng.get_state(), num_npcs, HIGHLIGHT_SELECTION.all_slugs()); // TODO: make highlight selection + size_t target_advances = calculate_target(env, rng.get_state(), num_npcs, HIGHLIGHT_SELECTION.all_slugs()); env.console.log("Needed advances: " + std::to_string(target_advances)); do_rng_advances(env.console, context, rng, target_advances, ADVANCE_PRESS_DURATION, ADVANCE_RELEASE_DURATION); @@ -566,7 +564,7 @@ void DailyHighlightRNG::program(SingleSwitchProgramEnvironment& env, ProControll if (FIX_TIME_WHEN_DONE) { pbf_press_button(context, BUTTON_HOME, 160ms, GameSettings::instance().GAME_TO_HOME_DELAY_SAFE0); - home_to_date_time(context, false, false); + home_to_date_time(env.console, context, false); pbf_press_button(context, BUTTON_A, 20, 105); pbf_press_button(context, BUTTON_A, 20, 105); pbf_press_button(context, BUTTON_HOME, 160ms, ConsoleSettings::instance().SETTINGS_TO_HOME_DELAY0); From cd206404716bcc2cf5d1144c34f08fef79f43e63 Mon Sep 17 00:00:00 2001 From: fyex <33452346+fyex@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:16:03 +0200 Subject: [PATCH 11/11] remove unused import --- .../Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h | 1 - 1 file changed, 1 deletion(-) diff --git a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h index 4267ad8be0..5814bb01cb 100644 --- a/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h +++ b/SerialPrograms/Source/PokemonSwSh/Programs/RNG/PokemonSwSh_BasicRNG.h @@ -8,7 +8,6 @@ #include "NintendoSwitch/Controllers/NintendoSwitch_ProController.h" #include "Pokemon/Pokemon_Xoroshiro128Plus.h" -#include namespace PokemonAutomation{ namespace NintendoSwitch{